vendor: Update HCL2 and cty, and other related packages
This brings in a bugfix for analyzing variables inside relative traversal expressions in HCL, and a cosmetic bugfix in cty for GoString of cty.NullVal(cty.DynamicPseudoType). This also updates some other packages, as a result of running "go get -u".
This commit is contained in:
parent
fe67d9dbf8
commit
917a320a3b
24
go.mod
24
go.mod
|
@ -32,7 +32,7 @@ require (
|
|||
github.com/coreos/go-semver v0.2.0 // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/davecgh/go-spew v1.1.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160617170158-f0777076321a // indirect
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 // indirect
|
||||
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
||||
|
@ -51,15 +51,15 @@ require (
|
|||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect
|
||||
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce
|
||||
github.com/hashicorp/errwrap v1.0.0
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||
github.com/hashicorp/go-cleanhttp v0.0.0-20171130225243-06c9ea3a335b
|
||||
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86
|
||||
github.com/hashicorp/go-hclog v0.0.0-20170716174523-b4e5765d1e5f
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect
|
||||
github.com/hashicorp/go-multierror v0.0.0-20150916205742-d30f09973e19
|
||||
github.com/hashicorp/go-plugin v0.0.0-20180125190438-e53f54cbf51e
|
||||
github.com/hashicorp/go-multierror v1.0.0
|
||||
github.com/hashicorp/go-plugin v0.0.0-20180814222501-a4620f9913d1
|
||||
github.com/hashicorp/go-retryablehttp v0.0.0-20160930035102-6e85be8fee1d
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
|
||||
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc // indirect
|
||||
|
@ -67,7 +67,7 @@ require (
|
|||
github.com/hashicorp/go-uuid v1.0.0
|
||||
github.com/hashicorp/go-version v0.0.0-20180322230233-23480c066577
|
||||
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
|
||||
github.com/hashicorp/hcl2 v0.0.0-20180308163058-5f8ed954abd8
|
||||
github.com/hashicorp/hcl2 v0.0.0-20180925175540-3f1c5474d4f7
|
||||
github.com/hashicorp/hil v0.0.0-20170627220502-fa9f258a9250
|
||||
github.com/hashicorp/logutils v0.0.0-20150609070431-0dc08b1671f3
|
||||
github.com/hashicorp/memberlist v0.1.0 // indirect
|
||||
|
@ -96,7 +96,7 @@ require (
|
|||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747
|
||||
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20170730050907-9a441910b168 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7
|
||||
github.com/mitchellh/go-wordwrap v1.0.0
|
||||
github.com/mitchellh/hashstructure v0.0.0-20160209213820-6b17d669fac5
|
||||
github.com/mitchellh/mapstructure v0.0.0-20170307201123-53818660ed49
|
||||
github.com/mitchellh/panicwrap v0.0.0-20161208170302-ba9e1a65e0f7
|
||||
|
@ -107,7 +107,6 @@ require (
|
|||
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
||||
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8
|
||||
github.com/prometheus/client_golang v0.9.0 // indirect
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
|
||||
|
@ -119,7 +118,6 @@ require (
|
|||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
|
||||
github.com/spf13/afero v1.0.2
|
||||
github.com/stretchr/testify v1.2.2 // indirect
|
||||
github.com/terraform-providers/terraform-provider-aws v1.40.0
|
||||
github.com/terraform-providers/terraform-provider-openstack v0.0.0-20170616075611-4080a521c6ea
|
||||
github.com/terraform-providers/terraform-provider-template v1.0.0 // indirect
|
||||
|
@ -130,15 +128,15 @@ require (
|
|||
github.com/xanzy/ssh-agent v0.1.0
|
||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
|
||||
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4
|
||||
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
|
||||
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b
|
||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7
|
||||
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3
|
||||
golang.org/x/sys v0.0.0-20170608164803-0b25a408a500 // indirect
|
||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e // indirect
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
|
||||
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5
|
||||
google.golang.org/appengine v1.2.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63 // indirect
|
||||
google.golang.org/grpc v0.0.0-20170809211603-7657092a1303
|
||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e // indirect
|
||||
)
|
||||
|
|
57
go.sum
57
go.sum
|
@ -8,6 +8,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20170803034930-c92175d54006 h1:dVyNL14dq1500J
|
|||
github.com/Azure/go-ntlmssp v0.0.0-20170803034930-c92175d54006/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290 h1:K9I21XUHNbYD3GNMmJBN0UKJCpdP+glftwNZ7Bo8kqY=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||
github.com/DHowett/go-plist v0.0.0-20180609054337-500bd5b9081b h1:WFNhl1+1ofCWWdNFEhut77cmuMXjJYYvkEVloDdaUCI=
|
||||
github.com/DHowett/go-plist v0.0.0-20180609054337-500bd5b9081b/go.mod h1:5paT5ZDrOm8eAJPem2Bd+q3FTi3Gxm/U4tb2tH8YIUQ=
|
||||
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292 h1:tuQ7w+my8a8mkwN7x2TSd7OzTjkZ7rAeSyH4xncuAMI=
|
||||
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||
|
@ -62,8 +64,8 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9
|
|||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160617170158-f0777076321a h1:pzKxqfSfp4kqrm6jfyVYYkWhf+e1hPRt3rX+Yj/3UBU=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160617170158-f0777076321a/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 h1:Dzuw9GtbmllUqEcoHfScT9YpKFUssSiZ5PgZkIGf/YQ=
|
||||
|
@ -104,8 +106,10 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.1 h1:3scN4iuXkNOyP98jF55Lv8a9j1o/Iwv
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.5.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089 h1:1eDpXAxTh0iPv+1kc9/gfSI2pxRERDsTk/lNGolwHn8=
|
||||
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w+PflHoszQNLTUh4kaByUcEWM/9uin4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357 h1:Rem2+U35z1QtPQc6r+WolF7yXiefXqDKyk+lN2pE164=
|
||||
github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
|
||||
github.com/hashicorp/go-cleanhttp v0.0.0-20171130225243-06c9ea3a335b h1:xrvnoavY7pMnMB/4x+cSAMgkzwjiSyilS55LZ14Ko7o=
|
||||
|
@ -118,10 +122,12 @@ github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxB
|
|||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c h1:BTAbnbegUIMB6xmQCwWE8yRzbA4XSpnZY5hvRJC188I=
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20150916205742-d30f09973e19 h1:gb61U/o4ZJ6TRYvZqJUKYidIhJOEAvNyVMesryROxAY=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20150916205742-d30f09973e19/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20180125190438-e53f54cbf51e h1:v7Pi8dJoDS0h0BAyFll8mfbrBrXg2vtfPg+J0XnIibM=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20180125190438-e53f54cbf51e/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0 h1:j30noezaCfvNLcdMYSvHLv81DxYRSt1grlpseG67vhU=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20180814222501-a4620f9913d1 h1:VOzBI0phmFk2VWekzswI3F7yEu6LPMKz/Bjd9wzRsI0=
|
||||
github.com/hashicorp/go-plugin v0.0.0-20180814222501-a4620f9913d1/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.0.0-20160930035102-6e85be8fee1d h1:/T1aqTlRV/71ER/wHvhqTZaXGQW7XSO+F16mIIHw7zc=
|
||||
github.com/hashicorp/go-retryablehttp v0.0.0-20160930035102-6e85be8fee1d/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM=
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:9HVkPxOpo+yO93Ah4yrO67d/qh0fbLLWbKqhYjyHq9A=
|
||||
|
@ -138,8 +144,8 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO
|
|||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws=
|
||||
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20180308163058-5f8ed954abd8 h1:laCE8EKBOUVN6LwBt7Be9IX7i2RQ2cnfbt+Z5a+0PRI=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20180308163058-5f8ed954abd8/go.mod h1:xp1eMAxqhQKBxz+yQUTsig9bBMRRWRWw+rK3FJmHf/A=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20180925175540-3f1c5474d4f7 h1:lNBI8nmNlIpLl8Lqf+9JLBmNGrU91Nlt71q7cGpZi+k=
|
||||
github.com/hashicorp/hcl2 v0.0.0-20180925175540-3f1c5474d4f7/go.mod h1:hrRmgPTdXWuNLpHcv7cRXL+ofoFsY76vDylTzH8eu3E=
|
||||
github.com/hashicorp/hil v0.0.0-20170627220502-fa9f258a9250 h1:fooK5IvDL/KIsi4LxF/JH68nVdrBSiGNPhS2JAQjtjo=
|
||||
github.com/hashicorp/hil v0.0.0-20170627220502-fa9f258a9250/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts=
|
||||
github.com/hashicorp/logutils v0.0.0-20150609070431-0dc08b1671f3 h1:oD64EFjELI9RY9yoWlfua58r+etdnoIC871z+rr6lkA=
|
||||
|
@ -206,6 +212,8 @@ github.com/mitchellh/go-testing-interface v0.0.0-20170730050907-9a441910b168 h1:
|
|||
github.com/mitchellh/go-testing-interface v0.0.0-20170730050907-9a441910b168/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20160209213820-6b17d669fac5 h1:h+4fp6yIoLPf/K2egDK3kvYM2zqb28gJIWWMiDzBdKM=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20160209213820-6b17d669fac5/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20170307201123-53818660ed49 h1:kaWdlw4YogwkDl8CG+/VxhXkrL9uz3n1D9QBC2pEGLE=
|
||||
|
@ -244,12 +252,15 @@ github.com/satori/uuid v0.0.0-20160927100844-b061729afc07 h1:81vvGlnI/AZ1/TxGDir
|
|||
github.com/satori/uuid v0.0.0-20160927100844-b061729afc07/go.mod h1:B8HLsPLik/YNn6KKWVMDJ8nzCL8RP5WyfsnmvnAEwIU=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/spf13/afero v1.0.2 h1:5bRmqmInNmNFkI9NG9O0Xc/Lgl9wOWWUUA/O8XZqTCo=
|
||||
github.com/spf13/afero v1.0.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/terraform-providers/terraform-provider-aws v1.40.0 h1:hdzdBEEW+a4MGy+6IUW/rrvFvSafYfFCSrbxy3Ffjy0=
|
||||
|
@ -274,20 +285,26 @@ github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 h1:MPPkRncZLN9Kh4M
|
|||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs=
|
||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4 h1:C02D0gjAVFMKqFUaZvaZK2YWGK1HAQwVTZWDAENYDjA=
|
||||
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v0.0.0-20180815031001-58bb2bc0302a/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b h1:9rQAtgrPBuyPjmPEcx4pqJs6D+u41FYbbVE/hhdsrtk=
|
||||
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87 h1:gCHhzI+1R9peHIMyiWVxoVaWlk1cYK7VThX5ptLtbXY=
|
||||
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E=
|
||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7 h1:zKzVgSQ8WOSHzD7I4k8LQjrHUUCNOlBsgc0PcYLVNnY=
|
||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3 h1:YGx0PRKSN/2n/OcdFycCC0JUA/Ln+i5lPcN8VoNDus0=
|
||||
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170608164803-0b25a408a500 h1:fhpo4nhRTq7HDlHaX8W7357HRWFlwsS1IAkGNCmoZ+E=
|
||||
golang.org/x/sys v0.0.0-20170608164803-0b25a408a500/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c h1:uHnKXcvx6SNkuwC+nrzxkJ+TpPwZOtumbhWrrOYN5YA=
|
||||
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e h1:LSlw/Dbj0MkNvPYAAkGinYmGliq+aqS7eKPYlE4oWC4=
|
||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
|
||||
|
@ -296,13 +313,17 @@ google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5 h1:PDkJGYjSvxJyevtZRGmB
|
|||
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63 h1:yNBw5bwywOTguAu+h6SkCUaWdEZ7ZXgfiwb2YTN1eQw=
|
||||
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v0.0.0-20170809211603-7657092a1303 h1:C5gwSQxZkG33JZoP+ZjEclrLu6DIRLVw743KKZfIXP4=
|
||||
google.golang.org/grpc v0.0.0-20170809211603-7657092a1303/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38VS1jM=
|
||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e h1:o/mfNjxpTLivuKEfxzzwrJ8PmulH2wEp7t713uMwKAA=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
howett.net/plist v0.0.0-20180609054337-500bd5b9081b/go.mod h1:jInWmjR7JRkkon4jlLXDZGVEeY/wo3kOOJEWYhNE+9Y=
|
||||
|
|
|
@ -2,7 +2,7 @@ ISC License
|
|||
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
Permission to use, copy, modify, and/or 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.
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build !js,!appengine,!safe,!disableunsafe
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
|
||||
|
@ -34,80 +36,49 @@ const (
|
|||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
var (
|
||||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
||||
// internal reflect.Value fields. These values are valid before golang
|
||||
// commit ecccf07e7f9d which changed the format. The are also valid
|
||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||
// the original format. Code in the init function updates these offsets
|
||||
// as necessary.
|
||||
offsetPtr = uintptr(ptrSize)
|
||||
offsetScalar = uintptr(0)
|
||||
offsetFlag = uintptr(ptrSize * 2)
|
||||
type flag uintptr
|
||||
|
||||
// flagKindWidth and flagKindShift indicate various bits that the
|
||||
// reflect package uses internally to track kind information.
|
||||
//
|
||||
// flagRO indicates whether or not the value field of a reflect.Value is
|
||||
// read-only.
|
||||
//
|
||||
// flagIndir indicates whether the value field of a reflect.Value is
|
||||
// the actual data or a pointer to the data.
|
||||
//
|
||||
// These values are valid before golang commit 90a7c3c86944 which
|
||||
// changed their positions. Code in the init function updates these
|
||||
// flags as necessary.
|
||||
flagKindWidth = uintptr(5)
|
||||
flagKindShift = uintptr(flagKindWidth - 1)
|
||||
flagRO = uintptr(1 << 0)
|
||||
flagIndir = uintptr(1 << 1)
|
||||
var (
|
||||
// flagRO indicates whether the value field of a reflect.Value
|
||||
// is read-only.
|
||||
flagRO flag
|
||||
|
||||
// flagAddr indicates whether the address of the reflect.Value's
|
||||
// value may be taken.
|
||||
flagAddr flag
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Older versions of reflect.Value stored small integers directly in the
|
||||
// ptr field (which is named val in the older versions). Versions
|
||||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
||||
// scalar for this purpose which unfortunately came before the flag
|
||||
// field, so the offset of the flag field is different for those
|
||||
// versions.
|
||||
//
|
||||
// This code constructs a new reflect.Value from a known small integer
|
||||
// and checks if the size of the reflect.Value struct indicates it has
|
||||
// the scalar field. When it does, the offsets are updated accordingly.
|
||||
vv := reflect.ValueOf(0xf00)
|
||||
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
||||
offsetScalar = ptrSize * 2
|
||||
offsetFlag = ptrSize * 3
|
||||
}
|
||||
// flagKindMask holds the bits that make up the kind
|
||||
// part of the flags field. In all the supported versions,
|
||||
// it is in the lower 5 bits.
|
||||
const flagKindMask = flag(0x1f)
|
||||
|
||||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
||||
// order bits are the kind. This code extracts the kind from the flags
|
||||
// field and ensures it's the correct type. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are updated
|
||||
// accordingly.
|
||||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
||||
upfv := *(*uintptr)(upf)
|
||||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
||||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
||||
flagKindShift = 0
|
||||
flagRO = 1 << 5
|
||||
flagIndir = 1 << 6
|
||||
// Different versions of Go have used different
|
||||
// bit layouts for the flags type. This table
|
||||
// records the known combinations.
|
||||
var okFlags = []struct {
|
||||
ro, addr flag
|
||||
}{{
|
||||
// From Go 1.4 to 1.5
|
||||
ro: 1 << 5,
|
||||
addr: 1 << 7,
|
||||
}, {
|
||||
// Up to Go tip.
|
||||
ro: 1<<5 | 1<<6,
|
||||
addr: 1 << 8,
|
||||
}}
|
||||
|
||||
// Commit adf9b30e5594 modified the flags to separate the
|
||||
// flagRO flag into two bits which specifies whether or not the
|
||||
// field is embedded. This causes flagIndir to move over a bit
|
||||
// and means that flagRO is the combination of either of the
|
||||
// original flagRO bit and the new bit.
|
||||
//
|
||||
// This code detects the change by extracting what used to be
|
||||
// the indirect bit to ensure it's set. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are
|
||||
// updated accordingly.
|
||||
if upfv&flagIndir == 0 {
|
||||
flagRO = 3 << 5
|
||||
flagIndir = 1 << 7
|
||||
}
|
||||
var flagValOffset = func() uintptr {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
return field.Offset
|
||||
}()
|
||||
|
||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||
func flagField(v *reflect.Value) *flag {
|
||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
|
@ -119,34 +90,56 @@ func init() {
|
|||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
||||
indirects := 1
|
||||
vt := v.Type()
|
||||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
||||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
||||
if rvf&flagIndir != 0 {
|
||||
vt = reflect.PtrTo(v.Type())
|
||||
indirects++
|
||||
} else if offsetScalar != 0 {
|
||||
// The value is in the scalar field when it's not one of the
|
||||
// reference types.
|
||||
switch vt.Kind() {
|
||||
case reflect.Uintptr:
|
||||
case reflect.Chan:
|
||||
case reflect.Func:
|
||||
case reflect.Map:
|
||||
case reflect.Ptr:
|
||||
case reflect.UnsafePointer:
|
||||
default:
|
||||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
||||
offsetScalar)
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||
return v
|
||||
}
|
||||
flagFieldPtr := flagField(&v)
|
||||
*flagFieldPtr &^= flagRO
|
||||
*flagFieldPtr |= flagAddr
|
||||
return v
|
||||
}
|
||||
|
||||
// Sanity checks against future reflect package changes
|
||||
// to the type or semantics of the Value.flag field.
|
||||
func init() {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||
panic("reflect.Value flag field has changed kind")
|
||||
}
|
||||
type t0 int
|
||||
var t struct {
|
||||
A t0
|
||||
// t0 will have flagEmbedRO set.
|
||||
t0
|
||||
// a will have flagStickyRO set
|
||||
a t0
|
||||
}
|
||||
vA := reflect.ValueOf(t).FieldByName("A")
|
||||
va := reflect.ValueOf(t).FieldByName("a")
|
||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||
|
||||
// Infer flagRO from the difference between the flags
|
||||
// for the (otherwise identical) fields in t.
|
||||
flagPublic := *flagField(&vA)
|
||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||
flagRO = flagPublic ^ flagWithRO
|
||||
|
||||
// Infer flagAddr from the difference between a value
|
||||
// taken from a pointer and not.
|
||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||
flagNoPtr := *flagField(&vA)
|
||||
flagPtr := *flagField(&vPtrA)
|
||||
flagAddr = flagNoPtr ^ flagPtr
|
||||
|
||||
// Check that the inferred flags tally with one of the known versions.
|
||||
for _, f := range okFlags {
|
||||
if flagRO == f.ro && flagAddr == f.addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pv := reflect.NewAt(vt, upv)
|
||||
rv = pv
|
||||
for i := 0; i < indirects; i++ {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
return rv
|
||||
panic("reflect.Value read-only flag has changed semantics")
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe
|
||||
// +build js appengine safe disableunsafe !go1.4
|
||||
|
||||
package spew
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
|||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
||||
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
|
|
|
@ -35,16 +35,16 @@ var (
|
|||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
|
@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
|||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
|
|
|
@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
|||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
|
|
|
@ -48,7 +48,7 @@ func main() {
|
|||
// We can use the Contains helpers to check if an error contains
|
||||
// another error. It is safe to do this with a nil error, or with
|
||||
// an error that doesn't even use the errwrap package.
|
||||
if errwrap.Contains(err, ErrNotExist) {
|
||||
if errwrap.Contains(err, "does not exist") {
|
||||
// Do something
|
||||
}
|
||||
if errwrap.ContainsType(err, new(os.PathError)) {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module github.com/hashicorp/errwrap
|
|
@ -0,0 +1,12 @@
|
|||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
script: make test testrace
|
|
@ -0,0 +1,31 @@
|
|||
TEST?=./...
|
||||
|
||||
default: test
|
||||
|
||||
# test runs the test suite and vets the code.
|
||||
test: generate
|
||||
@echo "==> Running tests..."
|
||||
@go list $(TEST) \
|
||||
| grep -v "/vendor/" \
|
||||
| xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS}
|
||||
|
||||
# testrace runs the race checker
|
||||
testrace: generate
|
||||
@echo "==> Running tests (race)..."
|
||||
@go list $(TEST) \
|
||||
| grep -v "/vendor/" \
|
||||
| xargs -n1 go test -timeout=60s -race ${TESTARGS}
|
||||
|
||||
# updatedeps installs all the dependencies needed to run and build.
|
||||
updatedeps:
|
||||
@sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'"
|
||||
|
||||
# generate runs `go generate` to build the dynamically generated source files.
|
||||
generate:
|
||||
@echo "==> Generating..."
|
||||
@find . -type f -name '.DS_Store' -delete
|
||||
@go list ./... \
|
||||
| grep -v "/vendor/" \
|
||||
| xargs -n1 go generate
|
||||
|
||||
.PHONY: default test testrace updatedeps generate
|
|
@ -1,5 +1,11 @@
|
|||
# go-multierror
|
||||
|
||||
[![Build Status](http://img.shields.io/travis/hashicorp/go-multierror.svg?style=flat-square)][travis]
|
||||
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]
|
||||
|
||||
[travis]: https://travis-ci.org/hashicorp/go-multierror
|
||||
[godocs]: https://godoc.org/github.com/hashicorp/go-multierror
|
||||
|
||||
`go-multierror` is a package for Go that provides a mechanism for
|
||||
representing a list of `error` values as a single `error`.
|
||||
|
||||
|
|
|
@ -18,9 +18,13 @@ func Append(err error, errs ...error) *Error {
|
|||
for _, e := range errs {
|
||||
switch e := e.(type) {
|
||||
case *Error:
|
||||
err.Errors = append(err.Errors, e.Errors...)
|
||||
if e != nil {
|
||||
err.Errors = append(err.Errors, e.Errors...)
|
||||
}
|
||||
default:
|
||||
err.Errors = append(err.Errors, e)
|
||||
if e != nil {
|
||||
err.Errors = append(err.Errors, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,16 @@ type ErrorFormatFunc func([]error) string
|
|||
// ListFormatFunc is a basic formatter that outputs the number of errors
|
||||
// that occurred along with a bullet point list of the errors.
|
||||
func ListFormatFunc(es []error) string {
|
||||
if len(es) == 1 {
|
||||
return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0])
|
||||
}
|
||||
|
||||
points := make([]string, len(es))
|
||||
for i, err := range es {
|
||||
points[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%d error(s) occurred:\n\n%s",
|
||||
len(es), strings.Join(points, "\n"))
|
||||
"%d errors occurred:\n\t%s\n\n",
|
||||
len(es), strings.Join(points, "\n\t"))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/hashicorp/go-multierror
|
||||
|
||||
require github.com/hashicorp/errwrap v1.0.0
|
|
@ -0,0 +1,4 @@
|
|||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w+PflHoszQNLTUh4kaByUcEWM/9uin4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
@ -40,11 +40,11 @@ func (e *Error) GoString() string {
|
|||
}
|
||||
|
||||
// WrappedErrors returns the list of errors that this Error is wrapping.
|
||||
// It is an implementatin of the errwrap.Wrapper interface so that
|
||||
// It is an implementation of the errwrap.Wrapper interface so that
|
||||
// multierror.Error can be used with that library.
|
||||
//
|
||||
// This method is not safe to be called concurrently and is no different
|
||||
// than accessing the Errors field directly. It is implementd only to
|
||||
// than accessing the Errors field directly. It is implemented only to
|
||||
// satisfy the errwrap.Wrapper interface.
|
||||
func (e *Error) WrappedErrors() []error {
|
||||
return e.Errors
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package multierror
|
||||
|
||||
// Len implements sort.Interface function for length
|
||||
func (err Error) Len() int {
|
||||
return len(err.Errors)
|
||||
}
|
||||
|
||||
// Swap implements sort.Interface function for swapping elements
|
||||
func (err Error) Swap(i, j int) {
|
||||
err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i]
|
||||
}
|
||||
|
||||
// Less implements sort.Interface function for determining order
|
||||
func (err Error) Less(i, j int) bool {
|
||||
return err.Errors[i].Error() < err.Errors[j].Error()
|
||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -71,16 +70,23 @@ var (
|
|||
//
|
||||
// See NewClient and ClientConfig for using a Client.
|
||||
type Client struct {
|
||||
config *ClientConfig
|
||||
exited bool
|
||||
doneLogging chan struct{}
|
||||
l sync.Mutex
|
||||
address net.Addr
|
||||
process *os.Process
|
||||
client ClientProtocol
|
||||
protocol Protocol
|
||||
logger hclog.Logger
|
||||
doneCtx context.Context
|
||||
config *ClientConfig
|
||||
exited bool
|
||||
doneLogging chan struct{}
|
||||
l sync.Mutex
|
||||
address net.Addr
|
||||
process *os.Process
|
||||
client ClientProtocol
|
||||
protocol Protocol
|
||||
logger hclog.Logger
|
||||
doneCtx context.Context
|
||||
negotiatedVersion int
|
||||
}
|
||||
|
||||
// NegotiatedVersion returns the protocol version negotiated with the server.
|
||||
// This is only valid after Start() is called.
|
||||
func (c *Client) NegotiatedVersion() int {
|
||||
return c.negotiatedVersion
|
||||
}
|
||||
|
||||
// ClientConfig is the configuration used to initialize a new
|
||||
|
@ -91,7 +97,13 @@ type ClientConfig struct {
|
|||
HandshakeConfig
|
||||
|
||||
// Plugins are the plugins that can be consumed.
|
||||
Plugins map[string]Plugin
|
||||
// The implied version of this PluginSet is the Handshake.ProtocolVersion.
|
||||
Plugins PluginSet
|
||||
|
||||
// VersionedPlugins is a map of PluginSets for specific protocol versions.
|
||||
// These can be used to negotiate a compatible version between client and
|
||||
// server. If this is set, Handshake.ProtocolVersion is not required.
|
||||
VersionedPlugins map[int]PluginSet
|
||||
|
||||
// One of the following must be set, but not both.
|
||||
//
|
||||
|
@ -234,7 +246,6 @@ func CleanupClients() {
|
|||
}
|
||||
managedClientsLock.Unlock()
|
||||
|
||||
log.Println("[DEBUG] plugin: waiting for all plugin processes to complete...")
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
|
@ -479,10 +490,30 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
|||
return c.address, nil
|
||||
}
|
||||
|
||||
if c.config.VersionedPlugins == nil {
|
||||
c.config.VersionedPlugins = make(map[int]PluginSet)
|
||||
}
|
||||
|
||||
// handle all plugins as versioned, using the handshake config as the default.
|
||||
version := int(c.config.ProtocolVersion)
|
||||
|
||||
// Make sure we're not overwriting a real version 0. If ProtocolVersion was
|
||||
// non-zero, then we have to just assume the user made sure that
|
||||
// VersionedPlugins doesn't conflict.
|
||||
if _, ok := c.config.VersionedPlugins[version]; !ok && c.config.Plugins != nil {
|
||||
c.config.VersionedPlugins[version] = c.config.Plugins
|
||||
}
|
||||
|
||||
var versionStrings []string
|
||||
for v := range c.config.VersionedPlugins {
|
||||
versionStrings = append(versionStrings, strconv.Itoa(v))
|
||||
}
|
||||
|
||||
env := []string{
|
||||
fmt.Sprintf("%s=%s", c.config.MagicCookieKey, c.config.MagicCookieValue),
|
||||
fmt.Sprintf("PLUGIN_MIN_PORT=%d", c.config.MinPort),
|
||||
fmt.Sprintf("PLUGIN_MAX_PORT=%d", c.config.MaxPort),
|
||||
fmt.Sprintf("PLUGIN_PROTOCOL_VERSIONS=%s", strings.Join(versionStrings, ",")),
|
||||
}
|
||||
|
||||
stdout_r, stdout_w := io.Pipe()
|
||||
|
@ -624,20 +655,18 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Parse the protocol version
|
||||
var protocol int64
|
||||
protocol, err = strconv.ParseInt(parts[1], 10, 0)
|
||||
// Test the API version
|
||||
version, pluginSet, err := c.checkProtoVersion(parts[1])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error parsing protocol version: %s", err)
|
||||
return
|
||||
return addr, err
|
||||
}
|
||||
|
||||
// Test the API version
|
||||
if uint(protocol) != c.config.ProtocolVersion {
|
||||
err = fmt.Errorf("Incompatible API version with plugin. "+
|
||||
"Plugin version: %s, Core version: %d", parts[1], c.config.ProtocolVersion)
|
||||
return
|
||||
}
|
||||
// set the Plugins value to the compatible set, so the version
|
||||
// doesn't need to be passed through to the ClientProtocol
|
||||
// implementation.
|
||||
c.config.Plugins = pluginSet
|
||||
c.negotiatedVersion = version
|
||||
c.logger.Debug("using plugin", "version", version)
|
||||
|
||||
switch parts[2] {
|
||||
case "tcp":
|
||||
|
@ -665,7 +694,7 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
|||
if !found {
|
||||
err = fmt.Errorf("Unsupported plugin protocol %q. Supported: %v",
|
||||
c.protocol, c.config.AllowedProtocols)
|
||||
return
|
||||
return addr, err
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -674,6 +703,33 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// checkProtoVersion returns the negotiated version and PluginSet.
|
||||
// This returns an error if the server returned an incompatible protocol
|
||||
// version, or an invalid handshake response.
|
||||
func (c *Client) checkProtoVersion(protoVersion string) (int, PluginSet, error) {
|
||||
serverVersion, err := strconv.Atoi(protoVersion)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("Error parsing protocol version %q: %s", protoVersion, err)
|
||||
}
|
||||
|
||||
// record these for the error message
|
||||
var clientVersions []int
|
||||
|
||||
// all versions, including the legacy ProtocolVersion have been added to
|
||||
// the versions set
|
||||
for version, plugins := range c.config.VersionedPlugins {
|
||||
clientVersions = append(clientVersions, version)
|
||||
|
||||
if serverVersion != version {
|
||||
continue
|
||||
}
|
||||
return version, plugins, nil
|
||||
}
|
||||
|
||||
return 0, nil, fmt.Errorf("Incompatible API version with plugin. "+
|
||||
"Plugin version: %d, Client versions: %d", serverVersion, clientVersions)
|
||||
}
|
||||
|
||||
// ReattachConfig returns the information that must be provided to NewClient
|
||||
// to reattach to the plugin process that this client started. This is
|
||||
// useful for plugins that detach from their parent process.
|
||||
|
@ -753,14 +809,14 @@ func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
|
|||
|
||||
func (c *Client) logStderr(r io.Reader) {
|
||||
bufR := bufio.NewReader(r)
|
||||
l := c.logger.Named(filepath.Base(c.config.Cmd.Path))
|
||||
|
||||
for {
|
||||
line, err := bufR.ReadString('\n')
|
||||
if line != "" {
|
||||
c.config.Stderr.Write([]byte(line))
|
||||
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
|
||||
l := c.logger.Named(filepath.Base(c.config.Cmd.Path))
|
||||
|
||||
entry, err := parseJSON(line)
|
||||
// If output is not JSON format, print directly to Debug
|
||||
if err != nil {
|
||||
|
@ -768,7 +824,7 @@ func (c *Client) logStderr(r io.Reader) {
|
|||
} else {
|
||||
out := flattenKVPairs(entry.KVPairs)
|
||||
|
||||
l = l.With("timestamp", entry.Timestamp.Format(hclog.TimeFormat))
|
||||
out = append(out, "timestamp", entry.Timestamp.Format(hclog.TimeFormat))
|
||||
switch hclog.LevelFromString(entry.Level) {
|
||||
case hclog.Trace:
|
||||
l.Trace(entry.Message, out...)
|
||||
|
|
|
@ -11,7 +11,9 @@ import (
|
|||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
|
@ -36,6 +38,8 @@ type HandshakeConfig struct {
|
|||
// ProtocolVersion is the version that clients must match on to
|
||||
// agree they can communicate. This should match the ProtocolVersion
|
||||
// set on ClientConfig when using a plugin.
|
||||
// This field is not required if VersionedPlugins are being used in the
|
||||
// Client or Server configurations.
|
||||
ProtocolVersion uint
|
||||
|
||||
// MagicCookieKey and value are used as a very basic verification
|
||||
|
@ -46,6 +50,10 @@ type HandshakeConfig struct {
|
|||
MagicCookieValue string
|
||||
}
|
||||
|
||||
// PluginSet is a set of plugins provided to be registered in the plugin
|
||||
// server.
|
||||
type PluginSet map[string]Plugin
|
||||
|
||||
// ServeConfig configures what sorts of plugins are served.
|
||||
type ServeConfig struct {
|
||||
// HandshakeConfig is the configuration that must match clients.
|
||||
|
@ -55,7 +63,13 @@ type ServeConfig struct {
|
|||
TLSProvider func() (*tls.Config, error)
|
||||
|
||||
// Plugins are the plugins that are served.
|
||||
Plugins map[string]Plugin
|
||||
// The implied version of this PluginSet is the Handshake.ProtocolVersion.
|
||||
Plugins PluginSet
|
||||
|
||||
// VersionedPlugins is a map of PluginSets for specific protocol versions.
|
||||
// These can be used to negotiate a compatible version between client and
|
||||
// server. If this is set, Handshake.ProtocolVersion is not required.
|
||||
VersionedPlugins map[int]PluginSet
|
||||
|
||||
// GRPCServer should be non-nil to enable serving the plugins over
|
||||
// gRPC. This is a function to create the server when needed with the
|
||||
|
@ -72,14 +86,80 @@ type ServeConfig struct {
|
|||
Logger hclog.Logger
|
||||
}
|
||||
|
||||
// Protocol returns the protocol that this server should speak.
|
||||
func (c *ServeConfig) Protocol() Protocol {
|
||||
result := ProtocolNetRPC
|
||||
if c.GRPCServer != nil {
|
||||
result = ProtocolGRPC
|
||||
// protocolVersion determines the protocol version and plugin set to be used by
|
||||
// the server. In the event that there is no suitable version, the last version
|
||||
// in the config is returned leaving the client to report the incompatibility.
|
||||
func protocolVersion(opts *ServeConfig) (int, Protocol, PluginSet) {
|
||||
protoVersion := int(opts.ProtocolVersion)
|
||||
pluginSet := opts.Plugins
|
||||
protoType := ProtocolNetRPC
|
||||
// check if the client sent a list of acceptable versions
|
||||
var clientVersions []int
|
||||
if vs := os.Getenv("PLUGIN_PROTOCOL_VERSIONS"); vs != "" {
|
||||
for _, s := range strings.Split(vs, ",") {
|
||||
v, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "server sent invalid plugin version %q", s)
|
||||
continue
|
||||
}
|
||||
clientVersions = append(clientVersions, v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
// we want to iterate in reverse order, to ensure we match the newest
|
||||
// compatible plugin version.
|
||||
sort.Sort(sort.Reverse(sort.IntSlice(clientVersions)))
|
||||
|
||||
// set the old un-versioned fields as if they were versioned plugins
|
||||
if opts.VersionedPlugins == nil {
|
||||
opts.VersionedPlugins = make(map[int]PluginSet)
|
||||
}
|
||||
|
||||
if pluginSet != nil {
|
||||
opts.VersionedPlugins[protoVersion] = pluginSet
|
||||
}
|
||||
|
||||
// sort the version to make sure we match the latest first
|
||||
var versions []int
|
||||
for v := range opts.VersionedPlugins {
|
||||
versions = append(versions, v)
|
||||
}
|
||||
|
||||
sort.Sort(sort.Reverse(sort.IntSlice(versions)))
|
||||
|
||||
// see if we have multiple versions of Plugins to choose from
|
||||
for _, version := range versions {
|
||||
// record each version, since we guarantee that this returns valid
|
||||
// values even if they are not a protocol match.
|
||||
protoVersion = version
|
||||
pluginSet = opts.VersionedPlugins[version]
|
||||
|
||||
// all plugins in a set must use the same transport, so check the first
|
||||
// for the protocol type
|
||||
for _, p := range pluginSet {
|
||||
switch p.(type) {
|
||||
case GRPCPlugin:
|
||||
protoType = ProtocolGRPC
|
||||
default:
|
||||
protoType = ProtocolNetRPC
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
for _, clientVersion := range clientVersions {
|
||||
if clientVersion == protoVersion {
|
||||
return protoVersion, protoType, pluginSet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the lowest version as the fallback.
|
||||
// Since we iterated over all the versions in reverse order above, these
|
||||
// values are from the lowest version number plugins (which may be from
|
||||
// a combination of the Handshake.ProtocolVersion and ServeConfig.Plugins
|
||||
// fields). This allows serving the oldest version of our plugins to a
|
||||
// legacy client that did not send a PLUGIN_PROTOCOL_VERSIONS list.
|
||||
return protoVersion, protoType, pluginSet
|
||||
}
|
||||
|
||||
// Serve serves the plugins given by ServeConfig.
|
||||
|
@ -107,6 +187,10 @@ func Serve(opts *ServeConfig) {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
// negotiate the version and plugins
|
||||
// start with default version in the handshake config
|
||||
protoVersion, protoType, pluginSet := protocolVersion(opts)
|
||||
|
||||
// Logging goes to the original stderr
|
||||
log.SetOutput(os.Stderr)
|
||||
|
||||
|
@ -160,7 +244,7 @@ func Serve(opts *ServeConfig) {
|
|||
|
||||
// Build the server type
|
||||
var server ServerProtocol
|
||||
switch opts.Protocol() {
|
||||
switch protoType {
|
||||
case ProtocolNetRPC:
|
||||
// If we have a TLS configuration then we wrap the listener
|
||||
// ourselves and do it at that level.
|
||||
|
@ -170,7 +254,7 @@ func Serve(opts *ServeConfig) {
|
|||
|
||||
// Create the RPC server to dispense
|
||||
server = &RPCServer{
|
||||
Plugins: opts.Plugins,
|
||||
Plugins: pluginSet,
|
||||
Stdout: stdout_r,
|
||||
Stderr: stderr_r,
|
||||
DoneCh: doneCh,
|
||||
|
@ -179,7 +263,7 @@ func Serve(opts *ServeConfig) {
|
|||
case ProtocolGRPC:
|
||||
// Create the gRPC server
|
||||
server = &GRPCServer{
|
||||
Plugins: opts.Plugins,
|
||||
Plugins: pluginSet,
|
||||
Server: opts.GRPCServer,
|
||||
TLS: tlsConfig,
|
||||
Stdout: stdout_r,
|
||||
|
@ -188,7 +272,7 @@ func Serve(opts *ServeConfig) {
|
|||
}
|
||||
|
||||
default:
|
||||
panic("unknown server protocol: " + opts.Protocol())
|
||||
panic("unknown server protocol: " + protoType)
|
||||
}
|
||||
|
||||
// Initialize the servers
|
||||
|
@ -208,13 +292,13 @@ func Serve(opts *ServeConfig) {
|
|||
|
||||
logger.Debug("plugin address", "network", listener.Addr().Network(), "address", listener.Addr().String())
|
||||
|
||||
// Output the address and service name to stdout so that core can bring it up.
|
||||
// Output the address and service name to stdout so that the client can bring it up.
|
||||
fmt.Printf("%d|%d|%s|%s|%s%s\n",
|
||||
CoreProtocolVersion,
|
||||
opts.ProtocolVersion,
|
||||
protoVersion,
|
||||
listener.Addr().Network(),
|
||||
listener.Addr().String(),
|
||||
opts.Protocol(),
|
||||
protoType,
|
||||
extra)
|
||||
os.Stdout.Sync()
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package plugin
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/rpc"
|
||||
|
||||
|
@ -10,6 +11,18 @@ import (
|
|||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// TestOptions allows specifying options that can affect the behavior of the
|
||||
// test functions
|
||||
type TestOptions struct {
|
||||
//ServerStdout causes the given value to be used in place of a blank buffer
|
||||
//for RPCServer's Stdout
|
||||
ServerStdout io.ReadCloser
|
||||
|
||||
//ServerStderr causes the given value to be used in place of a blank buffer
|
||||
//for RPCServer's Stderr
|
||||
ServerStderr io.ReadCloser
|
||||
}
|
||||
|
||||
// The testing file contains test helpers that you can use outside of
|
||||
// this package for making it easier to test plugins themselves.
|
||||
|
||||
|
@ -61,12 +74,20 @@ func TestRPCConn(t testing.T) (*rpc.Client, *rpc.Server) {
|
|||
|
||||
// TestPluginRPCConn returns a plugin RPC client and server that are connected
|
||||
// together and configured.
|
||||
func TestPluginRPCConn(t testing.T, ps map[string]Plugin) (*RPCClient, *RPCServer) {
|
||||
func TestPluginRPCConn(t testing.T, ps map[string]Plugin, opts *TestOptions) (*RPCClient, *RPCServer) {
|
||||
// Create two net.Conns we can use to shuttle our control connection
|
||||
clientConn, serverConn := TestConn(t)
|
||||
|
||||
// Start up the server
|
||||
server := &RPCServer{Plugins: ps, Stdout: new(bytes.Buffer), Stderr: new(bytes.Buffer)}
|
||||
if opts != nil {
|
||||
if opts.ServerStdout != nil {
|
||||
server.Stdout = opts.ServerStdout
|
||||
}
|
||||
if opts.ServerStderr != nil {
|
||||
server.Stderr = opts.ServerStderr
|
||||
}
|
||||
}
|
||||
go server.ServeConn(serverConn)
|
||||
|
||||
// Connect the client to the server
|
||||
|
|
|
@ -43,19 +43,23 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc
|
|||
|
||||
if !eachVal.CanIterateElements() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic for_each value",
|
||||
Detail: fmt.Sprintf("Cannot use a value of type %s in for_each. An iterable collection is required.", eachVal.Type()),
|
||||
Subject: eachAttr.Expr.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic for_each value",
|
||||
Detail: fmt.Sprintf("Cannot use a value of type %s in for_each. An iterable collection is required.", eachVal.Type()),
|
||||
Subject: eachAttr.Expr.Range().Ptr(),
|
||||
Expression: eachAttr.Expr,
|
||||
EvalContext: b.forEachCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
if eachVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic for_each value",
|
||||
Detail: "Cannot use a null value in for_each.",
|
||||
Subject: eachAttr.Expr.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic for_each value",
|
||||
Detail: "Cannot use a null value in for_each.",
|
||||
Subject: eachAttr.Expr.Range().Ptr(),
|
||||
Expression: eachAttr.Expr,
|
||||
EvalContext: b.forEachCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
@ -159,28 +163,34 @@ func (s *expandSpec) newBlock(i *iteration, ctx *hcl.EvalContext) (*hcl.Block, h
|
|||
labelVal, convErr = convert.Convert(labelVal, cty.String)
|
||||
if convErr != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: fmt.Sprintf("Cannot use this value as a dynamic block label: %s.", convErr),
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: fmt.Sprintf("Cannot use this value as a dynamic block label: %s.", convErr),
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Expression: labelExpr,
|
||||
EvalContext: lCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
if labelVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: "Cannot use a null value as a dynamic block label.",
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: "Cannot use a null value as a dynamic block label.",
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Expression: labelExpr,
|
||||
EvalContext: lCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
if !labelVal.IsKnown() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: "This value is not yet known. Dynamic block labels must be immediately-known values.",
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: "This value is not yet known. Dynamic block labels must be immediately-known values.",
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Expression: labelExpr,
|
||||
EvalContext: lCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
|
|
@ -55,8 +55,8 @@ in a type expression:
|
|||
For example:
|
||||
|
||||
* `list(string)`
|
||||
* `object({"name":string,"age":number})`
|
||||
* `map(object({"name":string,"age":number}))`
|
||||
* `object({name=string,age=number})`
|
||||
* `map(object({name=string,age=number}))`
|
||||
|
||||
Note that the object constructor syntax is not fully-general for all possible
|
||||
object types because it requires the attribute names to be valid identifiers.
|
||||
|
|
|
@ -26,14 +26,43 @@ const (
|
|||
type Diagnostic struct {
|
||||
Severity DiagnosticSeverity
|
||||
|
||||
// Summary and detail contain the English-language description of the
|
||||
// Summary and Detail contain the English-language description of the
|
||||
// problem. Summary is a terse description of the general problem and
|
||||
// detail is a more elaborate, often-multi-sentence description of
|
||||
// the probem and what might be done to solve it.
|
||||
Summary string
|
||||
Detail string
|
||||
|
||||
// Subject and Context are both source ranges relating to the diagnostic.
|
||||
//
|
||||
// Subject is a tight range referring to exactly the construct that
|
||||
// is problematic, while Context is an optional broader range (which should
|
||||
// fully contain Subject) that ought to be shown around Subject when
|
||||
// generating isolated source-code snippets in diagnostic messages.
|
||||
// If Context is nil, the Subject is also the Context.
|
||||
//
|
||||
// Some diagnostics have no source ranges at all. If Context is set then
|
||||
// Subject should always also be set.
|
||||
Subject *Range
|
||||
Context *Range
|
||||
|
||||
// For diagnostics that occur when evaluating an expression, Expression
|
||||
// may refer to that expression and EvalContext may point to the
|
||||
// EvalContext that was active when evaluating it. This may allow for the
|
||||
// inclusion of additional useful information when rendering a diagnostic
|
||||
// message to the user.
|
||||
//
|
||||
// It is not always possible to select a single EvalContext for a
|
||||
// diagnostic, and so in some cases this field may be nil even when an
|
||||
// expression causes a problem.
|
||||
//
|
||||
// EvalContexts form a tree, so the given EvalContext may refer to a parent
|
||||
// which in turn refers to another parent, etc. For a full picture of all
|
||||
// of the active variables and functions the caller must walk up this
|
||||
// chain, preferring definitions that are "closer" to the expression in
|
||||
// case of colliding names.
|
||||
Expression Expression
|
||||
EvalContext *EvalContext
|
||||
}
|
||||
|
||||
// Diagnostics is a list of Diagnostic instances.
|
||||
|
@ -96,6 +125,17 @@ func (d Diagnostics) HasErrors() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (d Diagnostics) Errs() []error {
|
||||
var errs []error
|
||||
for _, diag := range d {
|
||||
if diag.Severity == DiagError {
|
||||
errs = append(errs, diag)
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// A DiagnosticWriter emits diagnostics somehow.
|
||||
type DiagnosticWriter interface {
|
||||
WriteDiagnostic(*Diagnostic) error
|
||||
|
|
|
@ -2,11 +2,14 @@ package hcl
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type diagnosticTextWriter struct {
|
||||
|
@ -133,6 +136,62 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
|
|||
|
||||
w.wr.Write([]byte{'\n'})
|
||||
}
|
||||
|
||||
if diag.Expression != nil && diag.EvalContext != nil {
|
||||
// We will attempt to render the values for any variables
|
||||
// referenced in the given expression as additional context, for
|
||||
// situations where the same expression is evaluated multiple
|
||||
// times in different scopes.
|
||||
expr := diag.Expression
|
||||
ctx := diag.EvalContext
|
||||
|
||||
vars := expr.Variables()
|
||||
stmts := make([]string, 0, len(vars))
|
||||
seen := make(map[string]struct{}, len(vars))
|
||||
for _, traversal := range vars {
|
||||
val, diags := traversal.TraverseAbs(ctx)
|
||||
if diags.HasErrors() {
|
||||
// Skip anything that generates errors, since we probably
|
||||
// already have the same error in our diagnostics set
|
||||
// already.
|
||||
continue
|
||||
}
|
||||
|
||||
traversalStr := w.traversalStr(traversal)
|
||||
if _, exists := seen[traversalStr]; exists {
|
||||
continue // don't show duplicates when the same variable is referenced multiple times
|
||||
}
|
||||
switch {
|
||||
case !val.IsKnown():
|
||||
// Can't say anything about this yet, then.
|
||||
continue
|
||||
case val.IsNull():
|
||||
stmts = append(stmts, fmt.Sprintf("%s set to null", traversalStr))
|
||||
default:
|
||||
stmts = append(stmts, fmt.Sprintf("%s as %s", traversalStr, w.valueStr(val)))
|
||||
}
|
||||
seen[traversalStr] = struct{}{}
|
||||
}
|
||||
|
||||
sort.Strings(stmts) // FIXME: Should maybe use a traversal-aware sort that can sort numeric indexes properly?
|
||||
last := len(stmts) - 1
|
||||
|
||||
for i, stmt := range stmts {
|
||||
switch i {
|
||||
case 0:
|
||||
w.wr.Write([]byte{'w', 'i', 't', 'h', ' '})
|
||||
default:
|
||||
w.wr.Write([]byte{' ', ' ', ' ', ' ', ' '})
|
||||
}
|
||||
w.wr.Write([]byte(stmt))
|
||||
switch i {
|
||||
case last:
|
||||
w.wr.Write([]byte{'.', '\n', '\n'})
|
||||
default:
|
||||
w.wr.Write([]byte{',', '\n'})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if diag.Detail != "" {
|
||||
|
@ -156,6 +215,90 @@ func (w *diagnosticTextWriter) WriteDiagnostics(diags Diagnostics) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *diagnosticTextWriter) traversalStr(traversal Traversal) string {
|
||||
// This is a specialized subset of traversal rendering tailored to
|
||||
// producing helpful contextual messages in diagnostics. It is not
|
||||
// comprehensive nor intended to be used for other purposes.
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, step := range traversal {
|
||||
switch tStep := step.(type) {
|
||||
case TraverseRoot:
|
||||
buf.WriteString(tStep.Name)
|
||||
case TraverseAttr:
|
||||
buf.WriteByte('.')
|
||||
buf.WriteString(tStep.Name)
|
||||
case TraverseIndex:
|
||||
buf.WriteByte('[')
|
||||
if keyTy := tStep.Key.Type(); keyTy.IsPrimitiveType() {
|
||||
buf.WriteString(w.valueStr(tStep.Key))
|
||||
} else {
|
||||
// We'll just use a placeholder for more complex values,
|
||||
// since otherwise our result could grow ridiculously long.
|
||||
buf.WriteString("...")
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (w *diagnosticTextWriter) valueStr(val cty.Value) string {
|
||||
// This is a specialized subset of value rendering tailored to producing
|
||||
// helpful but concise messages in diagnostics. It is not comprehensive
|
||||
// nor intended to be used for other purposes.
|
||||
|
||||
ty := val.Type()
|
||||
switch {
|
||||
case val.IsNull():
|
||||
return "null"
|
||||
case !val.IsKnown():
|
||||
// Should never happen here because we should filter before we get
|
||||
// in here, but we'll do something reasonable rather than panic.
|
||||
return "(not yet known)"
|
||||
case ty == cty.Bool:
|
||||
if val.True() {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
case ty == cty.Number:
|
||||
bf := val.AsBigFloat()
|
||||
return bf.Text('g', 10)
|
||||
case ty == cty.String:
|
||||
// Go string syntax is not exactly the same as HCL native string syntax,
|
||||
// but we'll accept the minor edge-cases where this is different here
|
||||
// for now, just to get something reasonable here.
|
||||
return fmt.Sprintf("%q", val.AsString())
|
||||
case ty.IsCollectionType() || ty.IsTupleType():
|
||||
l := val.LengthInt()
|
||||
switch l {
|
||||
case 0:
|
||||
return "empty " + ty.FriendlyName()
|
||||
case 1:
|
||||
return ty.FriendlyName() + " with 1 element"
|
||||
default:
|
||||
return fmt.Sprintf("%s with %d elements", ty.FriendlyName(), l)
|
||||
}
|
||||
case ty.IsObjectType():
|
||||
atys := ty.AttributeTypes()
|
||||
l := len(atys)
|
||||
switch l {
|
||||
case 0:
|
||||
return "object with no attributes"
|
||||
case 1:
|
||||
var name string
|
||||
for k := range atys {
|
||||
name = k
|
||||
}
|
||||
return fmt.Sprintf("object with 1 attribute %q", name)
|
||||
default:
|
||||
return fmt.Sprintf("object with %d attributes", l)
|
||||
}
|
||||
default:
|
||||
return ty.FriendlyName()
|
||||
}
|
||||
}
|
||||
|
||||
func contextString(file *File, offset int) string {
|
||||
type contextStringer interface {
|
||||
ContextString(offset int) string
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package hclsyntax
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
)
|
||||
|
||||
// setDiagEvalContext is an internal helper that will impose a particular
|
||||
// EvalContext on a set of diagnostics in-place, for any diagnostic that
|
||||
// does not already have an EvalContext set.
|
||||
//
|
||||
// We generally expect diagnostics to be immutable, but this is safe to use
|
||||
// on any Diagnostics where none of the contained Diagnostic objects have yet
|
||||
// been seen by a caller. Its purpose is to apply additional context to a
|
||||
// set of diagnostics produced by a "deeper" component as the stack unwinds
|
||||
// during expression evaluation.
|
||||
func setDiagEvalContext(diags hcl.Diagnostics, expr hcl.Expression, ctx *hcl.EvalContext) {
|
||||
for _, diag := range diags {
|
||||
if diag.Expression == nil {
|
||||
diag.Expression = expr
|
||||
diag.EvalContext = ctx
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package hclsyntax
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
@ -104,7 +105,9 @@ func (e *ScopeTraversalExpr) walkChildNodes(w internalWalkFunc) {
|
|||
}
|
||||
|
||||
func (e *ScopeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
return e.Traversal.TraverseAbs(ctx)
|
||||
val, diags := e.Traversal.TraverseAbs(ctx)
|
||||
setDiagEvalContext(diags, e, ctx)
|
||||
return val, diags
|
||||
}
|
||||
|
||||
func (e *ScopeTraversalExpr) Range() hcl.Range {
|
||||
|
@ -129,12 +132,13 @@ type RelativeTraversalExpr struct {
|
|||
}
|
||||
|
||||
func (e *RelativeTraversalExpr) walkChildNodes(w internalWalkFunc) {
|
||||
// Scope traversals have no child nodes
|
||||
e.Source = w(e.Source).(Expression)
|
||||
}
|
||||
|
||||
func (e *RelativeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
src, diags := e.Source.Value(ctx)
|
||||
ret, travDiags := e.Traversal.TraverseRel(src)
|
||||
setDiagEvalContext(travDiags, e, ctx)
|
||||
diags = append(diags, travDiags...)
|
||||
return ret, diags
|
||||
}
|
||||
|
@ -206,10 +210,12 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
if !hasNonNilMap {
|
||||
return cty.DynamicVal, hcl.Diagnostics{
|
||||
{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Function calls not allowed",
|
||||
Detail: "Functions may not be called here.",
|
||||
Subject: e.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Function calls not allowed",
|
||||
Detail: "Functions may not be called here.",
|
||||
Subject: e.Range().Ptr(),
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -225,11 +231,13 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
|
||||
return cty.DynamicVal, hcl.Diagnostics{
|
||||
{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Call to unknown function",
|
||||
Detail: fmt.Sprintf("There is no function named %q.%s", e.Name, suggestion),
|
||||
Subject: &e.NameRange,
|
||||
Context: e.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Call to unknown function",
|
||||
Detail: fmt.Sprintf("There is no function named %q.%s", e.Name, suggestion),
|
||||
Subject: &e.NameRange,
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -254,11 +262,13 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
case expandVal.Type().IsTupleType() || expandVal.Type().IsListType() || expandVal.Type().IsSetType():
|
||||
if expandVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid expanding argument value",
|
||||
Detail: "The expanding argument (indicated by ...) must not be null.",
|
||||
Context: expandExpr.Range().Ptr(),
|
||||
Subject: e.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid expanding argument value",
|
||||
Detail: "The expanding argument (indicated by ...) must not be null.",
|
||||
Subject: expandExpr.Range().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: expandExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
@ -279,11 +289,13 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
args = newArgs
|
||||
default:
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid expanding argument value",
|
||||
Detail: "The expanding argument (indicated by ...) must be of a tuple, list, or set type.",
|
||||
Context: expandExpr.Range().Ptr(),
|
||||
Subject: e.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid expanding argument value",
|
||||
Detail: "The expanding argument (indicated by ...) must be of a tuple, list, or set type.",
|
||||
Subject: expandExpr.Range().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: expandExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
@ -303,8 +315,10 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
"Function %q expects%s %d argument(s). Missing value for %q.",
|
||||
e.Name, qual, len(params), missing.Name,
|
||||
),
|
||||
Subject: &e.CloseParenRange,
|
||||
Context: e.Range().Ptr(),
|
||||
Subject: &e.CloseParenRange,
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -318,8 +332,10 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
"Function %q expects only %d argument(s).",
|
||||
e.Name, len(params),
|
||||
),
|
||||
Subject: args[len(params)].StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Subject: args[len(params)].StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -349,8 +365,10 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
"Invalid value for %q parameter: %s.",
|
||||
param.Name, err,
|
||||
),
|
||||
Subject: argExpr.StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Subject: argExpr.StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: argExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -386,8 +404,10 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
"Invalid value for %q parameter: %s.",
|
||||
param.Name, err,
|
||||
),
|
||||
Subject: argExpr.StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Subject: argExpr.StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: argExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
|
||||
default:
|
||||
|
@ -398,8 +418,10 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
"Call to function %q failed: %s.",
|
||||
e.Name, err,
|
||||
),
|
||||
Subject: e.StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Subject: e.StartRange().Ptr(),
|
||||
Context: e.Range().Ptr(),
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -464,10 +486,12 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
|
|||
// "These expressions are object and object respectively" if the
|
||||
// object types don't exactly match.
|
||||
"The true and false result expressions must have consistent types. The given expressions are %s and %s, respectively.",
|
||||
trueResult.Type(), falseResult.Type(),
|
||||
trueResult.Type().FriendlyName(), falseResult.Type().FriendlyName(),
|
||||
),
|
||||
Subject: hcl.RangeBetween(e.TrueResult.Range(), e.FalseResult.Range()).Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Subject: hcl.RangeBetween(e.TrueResult.Range(), e.FalseResult.Range()).Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -476,11 +500,13 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
|
|||
diags = append(diags, condDiags...)
|
||||
if condResult.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Null condition",
|
||||
Detail: "The condition value is null. Conditions must either be true or false.",
|
||||
Subject: e.Condition.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Null condition",
|
||||
Detail: "The condition value is null. Conditions must either be true or false.",
|
||||
Subject: e.Condition.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.Condition,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.UnknownVal(resultType), diags
|
||||
}
|
||||
|
@ -490,11 +516,13 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
|
|||
condResult, err := convert.Convert(condResult, cty.Bool)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Incorrect condition type",
|
||||
Detail: fmt.Sprintf("The condition expression must be of type bool."),
|
||||
Subject: e.Condition.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Incorrect condition type",
|
||||
Detail: fmt.Sprintf("The condition expression must be of type bool."),
|
||||
Subject: e.Condition.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.Condition,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.UnknownVal(resultType), diags
|
||||
}
|
||||
|
@ -513,8 +541,10 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
|
|||
"The true result value has the wrong type: %s.",
|
||||
err.Error(),
|
||||
),
|
||||
Subject: e.TrueResult.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Subject: e.TrueResult.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.TrueResult,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
trueResult = cty.UnknownVal(resultType)
|
||||
}
|
||||
|
@ -534,8 +564,10 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
|
|||
"The false result value has the wrong type: %s.",
|
||||
err.Error(),
|
||||
),
|
||||
Subject: e.TrueResult.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Subject: e.FalseResult.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.FalseResult,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
falseResult = cty.UnknownVal(resultType)
|
||||
}
|
||||
|
@ -572,7 +604,9 @@ func (e *IndexExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
diags = append(diags, collDiags...)
|
||||
diags = append(diags, keyDiags...)
|
||||
|
||||
return hcl.Index(coll, key, &e.SrcRange)
|
||||
val, diags := hcl.Index(coll, key, &e.SrcRange)
|
||||
setDiagEvalContext(diags, e, ctx)
|
||||
return val, diags
|
||||
}
|
||||
|
||||
func (e *IndexExpr) Range() hcl.Range {
|
||||
|
@ -675,10 +709,12 @@ func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics
|
|||
|
||||
if key.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Null value as key",
|
||||
Detail: "Can't use a null value as a key.",
|
||||
Subject: item.ValueExpr.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Null value as key",
|
||||
Detail: "Can't use a null value as a key.",
|
||||
Subject: item.ValueExpr.Range().Ptr(),
|
||||
Expression: item.KeyExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
known = false
|
||||
continue
|
||||
|
@ -688,10 +724,12 @@ func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics
|
|||
key, err = convert.Convert(key, cty.String)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Incorrect key type",
|
||||
Detail: fmt.Sprintf("Can't use this value as a key: %s.", err.Error()),
|
||||
Subject: item.ValueExpr.Range().Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Incorrect key type",
|
||||
Detail: fmt.Sprintf("Can't use this value as a key: %s.", err.Error()),
|
||||
Subject: item.ValueExpr.Range().Ptr(),
|
||||
Expression: item.ValueExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
known = false
|
||||
continue
|
||||
|
@ -818,11 +856,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
|
||||
if collVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Iteration over null value",
|
||||
Detail: "A null value cannot be used as the collection in a 'for' expression.",
|
||||
Subject: e.CollExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Iteration over null value",
|
||||
Detail: "A null value cannot be used as the collection in a 'for' expression.",
|
||||
Subject: e.CollExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CollExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
@ -837,8 +877,10 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
"A value of type %s cannot be used as the collection in a 'for' expression.",
|
||||
collVal.Type().FriendlyName(),
|
||||
),
|
||||
Subject: e.CollExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Subject: e.CollExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CollExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
@ -846,14 +888,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
childCtx := ctx.NewChild()
|
||||
childCtx.Variables = map[string]cty.Value{}
|
||||
|
||||
// Before we start we'll do an early check to see if any CondExpr we've
|
||||
// been given is of the wrong type. This isn't 100% reliable (it may
|
||||
// be DynamicVal until real values are given) but it should catch some
|
||||
// straightforward cases and prevent a barrage of repeated errors.
|
||||
if e.CondExpr != nil {
|
||||
childCtx := ctx.NewChild()
|
||||
childCtx.Variables = map[string]cty.Value{}
|
||||
if e.KeyVar != "" {
|
||||
childCtx.Variables[e.KeyVar] = cty.DynamicVal
|
||||
}
|
||||
|
@ -863,22 +904,26 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
diags = append(diags, condDiags...)
|
||||
if result.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Condition is null",
|
||||
Detail: "The value of the 'if' clause must not be null.",
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Condition is null",
|
||||
Detail: "The value of the 'if' clause must not be null.",
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CondExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
_, err := convert.Convert(result, cty.Bool)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CondExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
@ -902,6 +947,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
known := true
|
||||
for it.Next() {
|
||||
k, v := it.Element()
|
||||
childCtx := ctx.NewChild()
|
||||
childCtx.Variables = map[string]cty.Value{}
|
||||
if e.KeyVar != "" {
|
||||
childCtx.Variables[e.KeyVar] = k
|
||||
}
|
||||
|
@ -913,11 +960,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if includeRaw.IsNull() {
|
||||
if known {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Condition is null",
|
||||
Detail: "The value of the 'if' clause must not be null.",
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: "The value of the 'if' clause must not be null.",
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CondExpr,
|
||||
EvalContext: childCtx,
|
||||
})
|
||||
}
|
||||
known = false
|
||||
|
@ -927,11 +976,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if err != nil {
|
||||
if known {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CondExpr,
|
||||
EvalContext: childCtx,
|
||||
})
|
||||
}
|
||||
known = false
|
||||
|
@ -953,11 +1004,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if keyRaw.IsNull() {
|
||||
if known {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key",
|
||||
Detail: "Key expression in 'for' expression must not produce a null value.",
|
||||
Subject: e.KeyExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key",
|
||||
Detail: "Key expression in 'for' expression must not produce a null value.",
|
||||
Subject: e.KeyExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.KeyExpr,
|
||||
EvalContext: childCtx,
|
||||
})
|
||||
}
|
||||
known = false
|
||||
|
@ -972,11 +1025,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if err != nil {
|
||||
if known {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key",
|
||||
Detail: fmt.Sprintf("The key expression produced an invalid result: %s.", err.Error()),
|
||||
Subject: e.KeyExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key",
|
||||
Detail: fmt.Sprintf("The key expression produced an invalid result: %s.", err.Error()),
|
||||
Subject: e.KeyExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.KeyExpr,
|
||||
EvalContext: childCtx,
|
||||
})
|
||||
}
|
||||
known = false
|
||||
|
@ -996,11 +1051,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
Severity: hcl.DiagError,
|
||||
Summary: "Duplicate object key",
|
||||
Detail: fmt.Sprintf(
|
||||
"Two different items produced the key %q in this for expression. If duplicates are expected, use the ellipsis (...) after the value expression to enable grouping by key.",
|
||||
"Two different items produced the key %q in this 'for' expression. If duplicates are expected, use the ellipsis (...) after the value expression to enable grouping by key.",
|
||||
k,
|
||||
),
|
||||
Subject: e.KeyExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Subject: e.KeyExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.KeyExpr,
|
||||
EvalContext: childCtx,
|
||||
})
|
||||
} else {
|
||||
vals[key.AsString()] = val
|
||||
|
@ -1030,6 +1087,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
known := true
|
||||
for it.Next() {
|
||||
k, v := it.Element()
|
||||
childCtx := ctx.NewChild()
|
||||
childCtx.Variables = map[string]cty.Value{}
|
||||
if e.KeyVar != "" {
|
||||
childCtx.Variables[e.KeyVar] = k
|
||||
}
|
||||
|
@ -1041,11 +1100,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if includeRaw.IsNull() {
|
||||
if known {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Condition is null",
|
||||
Detail: "The value of the 'if' clause must not be null.",
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: "The value of the 'if' clause must not be null.",
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CondExpr,
|
||||
EvalContext: childCtx,
|
||||
})
|
||||
}
|
||||
known = false
|
||||
|
@ -1063,11 +1124,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if err != nil {
|
||||
if known {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' condition",
|
||||
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
|
||||
Subject: e.CondExpr.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.CondExpr,
|
||||
EvalContext: childCtx,
|
||||
})
|
||||
}
|
||||
known = false
|
||||
|
@ -1153,11 +1216,13 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
|
||||
if sourceVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Splat of null value",
|
||||
Detail: "Splat expressions (with the * symbol) cannot be applied to null values.",
|
||||
Subject: e.Source.Range().Ptr(),
|
||||
Context: hcl.RangeBetween(e.Source.Range(), e.MarkerRange).Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Splat of null value",
|
||||
Detail: "Splat expressions (with the * symbol) cannot be applied to null values.",
|
||||
Subject: e.Source.Range().Ptr(),
|
||||
Context: hcl.RangeBetween(e.Source.Range(), e.MarkerRange).Ptr(),
|
||||
Expression: e.Source,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
@ -1169,7 +1234,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
// both to tuples/lists and to other values, and in the latter case
|
||||
// the value will be treated as an implicit single-value list. We'll
|
||||
// deal with that here first.
|
||||
if !(sourceVal.Type().IsTupleType() || sourceVal.Type().IsListType()) {
|
||||
if !(sourceVal.Type().IsTupleType() || sourceVal.Type().IsListType() || sourceVal.Type().IsSetType()) {
|
||||
sourceVal = cty.ListVal([]cty.Value{sourceVal})
|
||||
}
|
||||
|
||||
|
@ -1226,13 +1291,24 @@ func (e *SplatExpr) StartRange() hcl.Range {
|
|||
// assigns it a value.
|
||||
type AnonSymbolExpr struct {
|
||||
SrcRange hcl.Range
|
||||
values map[*hcl.EvalContext]cty.Value
|
||||
|
||||
// values and its associated lock are used to isolate concurrent
|
||||
// evaluations of a symbol from one another. It is the calling application's
|
||||
// responsibility to ensure that the same splat expression is not evalauted
|
||||
// concurrently within the _same_ EvalContext, but it is fine and safe to
|
||||
// do cuncurrent evaluations with distinct EvalContexts.
|
||||
values map[*hcl.EvalContext]cty.Value
|
||||
valuesLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (e *AnonSymbolExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
if ctx == nil {
|
||||
return cty.DynamicVal, nil
|
||||
}
|
||||
|
||||
e.valuesLock.RLock()
|
||||
defer e.valuesLock.RUnlock()
|
||||
|
||||
val, exists := e.values[ctx]
|
||||
if !exists {
|
||||
return cty.DynamicVal, nil
|
||||
|
@ -1243,6 +1319,9 @@ func (e *AnonSymbolExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics
|
|||
// setValue sets a temporary local value for the expression when evaluated
|
||||
// in the given context, which must be non-nil.
|
||||
func (e *AnonSymbolExpr) setValue(ctx *hcl.EvalContext, val cty.Value) {
|
||||
e.valuesLock.Lock()
|
||||
defer e.valuesLock.Unlock()
|
||||
|
||||
if e.values == nil {
|
||||
e.values = make(map[*hcl.EvalContext]cty.Value)
|
||||
}
|
||||
|
@ -1253,6 +1332,9 @@ func (e *AnonSymbolExpr) setValue(ctx *hcl.EvalContext, val cty.Value) {
|
|||
}
|
||||
|
||||
func (e *AnonSymbolExpr) clearValue(ctx *hcl.EvalContext) {
|
||||
e.valuesLock.Lock()
|
||||
defer e.valuesLock.Unlock()
|
||||
|
||||
if e.values == nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -149,21 +149,25 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||
lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid operand",
|
||||
Detail: fmt.Sprintf("Unsuitable value for left operand: %s.", err),
|
||||
Subject: e.LHS.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid operand",
|
||||
Detail: fmt.Sprintf("Unsuitable value for left operand: %s.", err),
|
||||
Subject: e.LHS.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.LHS,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
}
|
||||
rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid operand",
|
||||
Detail: fmt.Sprintf("Unsuitable value for right operand: %s.", err),
|
||||
Subject: e.RHS.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid operand",
|
||||
Detail: fmt.Sprintf("Unsuitable value for right operand: %s.", err),
|
||||
Subject: e.RHS.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.RHS,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -178,10 +182,12 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
// FIXME: This diagnostic is useless.
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Operation failed",
|
||||
Detail: fmt.Sprintf("Error during operation: %s.", err),
|
||||
Subject: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Operation failed",
|
||||
Detail: fmt.Sprintf("Error during operation: %s.", err),
|
||||
Subject: &e.SrcRange,
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.UnknownVal(e.Op.Type), diags
|
||||
}
|
||||
|
@ -219,11 +225,13 @@ func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
val, err := convert.Convert(givenVal, param.Type)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid operand",
|
||||
Detail: fmt.Sprintf("Unsuitable value for unary operand: %s.", err),
|
||||
Subject: e.Val.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid operand",
|
||||
Detail: fmt.Sprintf("Unsuitable value for unary operand: %s.", err),
|
||||
Subject: e.Val.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: e.Val,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -238,10 +246,12 @@ func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
// FIXME: This diagnostic is useless.
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Operation failed",
|
||||
Detail: fmt.Sprintf("Error during operation: %s.", err),
|
||||
Subject: &e.SrcRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Operation failed",
|
||||
Detail: fmt.Sprintf("Error during operation: %s.", err),
|
||||
Subject: &e.SrcRange,
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
return cty.UnknownVal(e.Op.Type), diags
|
||||
}
|
||||
|
|
|
@ -37,8 +37,10 @@ func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||
Detail: fmt.Sprintf(
|
||||
"The expression result is null. Cannot include a null value in a string template.",
|
||||
),
|
||||
Subject: part.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Subject: part.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: part,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
@ -61,8 +63,10 @@ func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||
"Cannot include the given value in a string template: %s.",
|
||||
err.Error(),
|
||||
),
|
||||
Subject: part.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Subject: part.Range().Ptr(),
|
||||
Context: &e.SrcRange,
|
||||
Expression: part,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
@ -127,7 +131,9 @@ func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
Detail: fmt.Sprintf(
|
||||
"An iteration result is null. Cannot include a null value in a string template.",
|
||||
),
|
||||
Subject: e.Range().Ptr(),
|
||||
Subject: e.Range().Ptr(),
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
@ -143,7 +149,9 @@ func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||
"Cannot include one of the interpolation results into the string template: %s.",
|
||||
err.Error(),
|
||||
),
|
||||
Subject: e.Range().Ptr(),
|
||||
Subject: e.Range().Ptr(),
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ Token:
|
|||
Severity: hcl.DiagError,
|
||||
Summary: "Attribute redefined",
|
||||
Detail: fmt.Sprintf(
|
||||
"The attribute %q was already defined at %s. Each attribute may be defined only once.",
|
||||
"The argument %q was already set at %s. Each argument may be set only once.",
|
||||
titem.Name, existing.NameRange.String(),
|
||||
),
|
||||
Subject: &titem.NameRange,
|
||||
|
@ -80,15 +80,15 @@ Token:
|
|||
if bad.Type == TokenOQuote {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid attribute name",
|
||||
Detail: "Attribute names must not be quoted.",
|
||||
Summary: "Invalid argument name",
|
||||
Detail: "Argument names must not be quoted.",
|
||||
Subject: &bad.Range,
|
||||
})
|
||||
} else {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Attribute or block definition required",
|
||||
Detail: "An attribute or block definition is required here.",
|
||||
Summary: "Argument or block definition required",
|
||||
Detail: "An argument or block definition is required here.",
|
||||
Subject: &bad.Range,
|
||||
})
|
||||
}
|
||||
|
@ -120,8 +120,8 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
|
|||
return nil, hcl.Diagnostics{
|
||||
{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Attribute or block definition required",
|
||||
Detail: "An attribute or block definition is required here.",
|
||||
Summary: "Argument or block definition required",
|
||||
Detail: "An argument or block definition is required here.",
|
||||
Subject: &ident.Range,
|
||||
},
|
||||
}
|
||||
|
@ -139,8 +139,8 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
|
|||
return nil, hcl.Diagnostics{
|
||||
{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Attribute or block definition required",
|
||||
Detail: "An attribute or block definition is required here. To define an attribute, use the equals sign \"=\" to introduce the attribute value.",
|
||||
Summary: "Argument or block definition required",
|
||||
Detail: "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.",
|
||||
Subject: &ident.Range,
|
||||
},
|
||||
}
|
||||
|
@ -171,8 +171,8 @@ func (p *parser) finishParsingBodyAttribute(ident Token) (Node, hcl.Diagnostics)
|
|||
if !p.recovery {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing newline after attribute definition",
|
||||
Detail: "An attribute definition must end with a newline.",
|
||||
Summary: "Missing newline after argument",
|
||||
Detail: "An argument definition must end with a newline.",
|
||||
Subject: &end.Range,
|
||||
Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(),
|
||||
})
|
||||
|
@ -244,7 +244,7 @@ Token:
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid block definition",
|
||||
Detail: "The equals sign \"=\" indicates an attribute definition, and must not be used when defining a block.",
|
||||
Detail: "The equals sign \"=\" indicates an argument definition, and must not be used when defining a block.",
|
||||
Subject: &tok.Range,
|
||||
Context: hcl.RangeBetween(ident.Range, tok.Range).Ptr(),
|
||||
})
|
||||
|
@ -1135,8 +1135,8 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
|
|||
if next.Type == TokenNewline || next.Type == TokenComma {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing item value",
|
||||
Detail: "Expected an item value, introduced by an equals sign (\"=\").",
|
||||
Summary: "Missing attribute value",
|
||||
Detail: "Expected an attribute value, introduced by an equals sign (\"=\").",
|
||||
Subject: &next.Range,
|
||||
Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
|
||||
})
|
||||
|
@ -1144,7 +1144,7 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing key/value separator",
|
||||
Detail: "Expected an equals sign (\"=\") to mark the beginning of the item value.",
|
||||
Detail: "Expected an equals sign (\"=\") to mark the beginning of the attribute value.",
|
||||
Subject: &next.Range,
|
||||
Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
|
||||
})
|
||||
|
@ -1182,8 +1182,8 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
|
|||
if !p.recovery {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing item separator",
|
||||
Detail: "Expected a newline or comma to mark the beginning of the next item.",
|
||||
Summary: "Missing attribute separator",
|
||||
Detail: "Expected a newline or comma to mark the beginning of the next attribute.",
|
||||
Subject: &next.Range,
|
||||
Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
|
||||
})
|
||||
|
@ -1277,7 +1277,7 @@ func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics)
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' expression",
|
||||
Detail: "For expression requires 'in' keyword after names.",
|
||||
Detail: "For expression requires the 'in' keyword after its name declarations.",
|
||||
Subject: p.Peek().Range.Ptr(),
|
||||
Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
|
||||
})
|
||||
|
@ -1305,7 +1305,7 @@ func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics)
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid 'for' expression",
|
||||
Detail: "For expression requires colon after collection expression.",
|
||||
Detail: "For expression requires a colon after the collection expression.",
|
||||
Subject: p.Peek().Range.Ptr(),
|
||||
Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
|
||||
})
|
||||
|
@ -1459,7 +1459,7 @@ Token:
|
|||
case TokenTemplateControl, TokenTemplateInterp:
|
||||
which := "$"
|
||||
if tok.Type == TokenTemplateControl {
|
||||
which = "!"
|
||||
which = "%"
|
||||
}
|
||||
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
|
|
|
@ -4,12 +4,12 @@ This is the specification of the syntax and semantics of the native syntax
|
|||
for HCL. HCL is a system for defining configuration languages for applications.
|
||||
The HCL information model is designed to support multiple concrete syntaxes
|
||||
for configuration, but this native syntax is considered the primary format
|
||||
and is optimized for human authoring and maintenence, as opposed to machine
|
||||
and is optimized for human authoring and maintenance, as opposed to machine
|
||||
generation of configuration.
|
||||
|
||||
The language consists of three integrated sub-languages:
|
||||
|
||||
* The _structural_ language defines the overall heirarchical configuration
|
||||
* The _structural_ language defines the overall hierarchical configuration
|
||||
structure, and is a serialization of HCL bodies, blocks and attributes.
|
||||
|
||||
* The _expression_ language is used to express attribute values, either as
|
||||
|
@ -186,7 +186,7 @@ for later evaluation by the calling application.
|
|||
### Blocks
|
||||
|
||||
A _block_ creates a child body that is annotated with a block _type_ and
|
||||
zero or more block _labels_. Blocks create a structural heirachy which can be
|
||||
zero or more block _labels_. Blocks create a structural hierachy which can be
|
||||
interpreted by the calling application.
|
||||
|
||||
Block labels can either be quoted literal strings or naked identifiers.
|
||||
|
@ -296,7 +296,7 @@ There is a syntax ambiguity between _for expressions_ and collection values
|
|||
whose first element is a reference to a variable named `for`. The
|
||||
_for expression_ interpretation has priority, so to produce a tuple whose
|
||||
first element is the value of a variable named `for`, or an object with a
|
||||
key named `for`, use paretheses to disambiguate:
|
||||
key named `for`, use parentheses to disambiguate:
|
||||
|
||||
* `[for, foo, baz]` is a syntax error.
|
||||
* `[(for), foo, baz]` is a tuple whose first element is the value of variable
|
||||
|
@ -482,7 +482,7 @@ object.
|
|||
In the case of object `for`, it is an error if two input elements produce
|
||||
the same result from the attribute name expression, since duplicate
|
||||
attributes are not possible. If the ellipsis symbol (`...`) appears
|
||||
immediately after the value experssion, this activates the grouping mode in
|
||||
immediately after the value expression, this activates the grouping mode in
|
||||
which each value in the resulting object is a _tuple_ of all of the values
|
||||
that were produced against each distinct key.
|
||||
|
||||
|
@ -769,7 +769,7 @@ sequence is escaped as `%%{`.
|
|||
|
||||
When the template sub-language is embedded in the expression language via
|
||||
_template expressions_, additional constraints and transforms are applied to
|
||||
template literalsas described in the definition of template expressions.
|
||||
template literals as described in the definition of template expressions.
|
||||
|
||||
The value of a template literal can be modified by _strip markers_ in any
|
||||
interpolations or directives that are adjacent to it. A strip marker is
|
||||
|
@ -903,7 +903,7 @@ key/value pairs given are returned as the static pairs, with no further
|
|||
interpretation.
|
||||
|
||||
The usual requirement that an attribute name be interpretable as a string
|
||||
does not apply to this static analyis, allowing callers to provide map-like
|
||||
does not apply to this static analysis, allowing callers to provide map-like
|
||||
constructs with different key types by building on the map syntax.
|
||||
|
||||
### Static Call
|
||||
|
|
|
@ -9,6 +9,10 @@ import (
|
|||
|
||||
// AsHCLBlock returns the block data expressed as a *hcl.Block.
|
||||
func (b *Block) AsHCLBlock() *hcl.Block {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lastHeaderRange := b.TypeRange
|
||||
if len(b.LabelRanges) > 0 {
|
||||
lastHeaderRange = b.LabelRanges[len(b.LabelRanges)-1]
|
||||
|
@ -326,6 +330,9 @@ func (a *Attribute) Range() hcl.Range {
|
|||
|
||||
// AsHCLAttribute returns the block data expressed as a *hcl.Attribute.
|
||||
func (a *Attribute) AsHCLAttribute() *hcl.Attribute {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
return &hcl.Attribute{
|
||||
Name: a.Name,
|
||||
Expr: a.Expr,
|
||||
|
|
118
vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure_at_pos.go
generated
vendored
Normal file
118
vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure_at_pos.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
package hclsyntax
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// The methods in this file are all optional extension methods that serve to
|
||||
// implement the methods of the same name on *hcl.File when its root body
|
||||
// is provided by this package.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// BlocksAtPos implements the method of the same name for an *hcl.File that
|
||||
// is backed by a *Body.
|
||||
func (b *Body) BlocksAtPos(pos hcl.Pos) []*hcl.Block {
|
||||
list, _ := b.blocksAtPos(pos, true)
|
||||
return list
|
||||
}
|
||||
|
||||
// InnermostBlockAtPos implements the method of the same name for an *hcl.File
|
||||
// that is backed by a *Body.
|
||||
func (b *Body) InnermostBlockAtPos(pos hcl.Pos) *hcl.Block {
|
||||
_, innermost := b.blocksAtPos(pos, false)
|
||||
return innermost.AsHCLBlock()
|
||||
}
|
||||
|
||||
// OutermostBlockAtPos implements the method of the same name for an *hcl.File
|
||||
// that is backed by a *Body.
|
||||
func (b *Body) OutermostBlockAtPos(pos hcl.Pos) *hcl.Block {
|
||||
return b.outermostBlockAtPos(pos).AsHCLBlock()
|
||||
}
|
||||
|
||||
// blocksAtPos is the internal engine of both BlocksAtPos and
|
||||
// InnermostBlockAtPos, which both need to do the same logic but return a
|
||||
// differently-shaped result.
|
||||
//
|
||||
// list is nil if makeList is false, avoiding an allocation. Innermost is
|
||||
// always set, and if the returned list is non-nil it will always match the
|
||||
// final element from that list.
|
||||
func (b *Body) blocksAtPos(pos hcl.Pos, makeList bool) (list []*hcl.Block, innermost *Block) {
|
||||
current := b
|
||||
|
||||
Blocks:
|
||||
for current != nil {
|
||||
for _, block := range current.Blocks {
|
||||
wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange)
|
||||
if wholeRange.ContainsPos(pos) {
|
||||
innermost = block
|
||||
if makeList {
|
||||
list = append(list, innermost.AsHCLBlock())
|
||||
}
|
||||
current = block.Body
|
||||
continue Blocks
|
||||
}
|
||||
}
|
||||
|
||||
// If we fall out here then none of the current body's nested blocks
|
||||
// contain the position we are looking for, and so we're done.
|
||||
break
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// outermostBlockAtPos is the internal version of OutermostBlockAtPos that
|
||||
// returns a hclsyntax.Block rather than an hcl.Block, allowing for further
|
||||
// analysis if necessary.
|
||||
func (b *Body) outermostBlockAtPos(pos hcl.Pos) *Block {
|
||||
// This is similar to blocksAtPos, but simpler because we know it only
|
||||
// ever needs to search the first level of nested blocks.
|
||||
|
||||
for _, block := range b.Blocks {
|
||||
wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange)
|
||||
if wholeRange.ContainsPos(pos) {
|
||||
return block
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttributeAtPos implements the method of the same name for an *hcl.File
|
||||
// that is backed by a *Body.
|
||||
func (b *Body) AttributeAtPos(pos hcl.Pos) *hcl.Attribute {
|
||||
return b.attributeAtPos(pos).AsHCLAttribute()
|
||||
}
|
||||
|
||||
// attributeAtPos is the internal version of AttributeAtPos that returns a
|
||||
// hclsyntax.Block rather than an hcl.Block, allowing for further analysis if
|
||||
// necessary.
|
||||
func (b *Body) attributeAtPos(pos hcl.Pos) *Attribute {
|
||||
searchBody := b
|
||||
_, block := b.blocksAtPos(pos, false)
|
||||
if block != nil {
|
||||
searchBody = block.Body
|
||||
}
|
||||
|
||||
for _, attr := range searchBody.Attributes {
|
||||
if attr.SrcRange.ContainsPos(pos) {
|
||||
return attr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OutermostExprAtPos implements the method of the same name for an *hcl.File
|
||||
// that is backed by a *Body.
|
||||
func (b *Body) OutermostExprAtPos(pos hcl.Pos) hcl.Expression {
|
||||
attr := b.attributeAtPos(pos)
|
||||
if attr == nil {
|
||||
return nil
|
||||
}
|
||||
if !attr.Expr.Range().ContainsPos(pos) {
|
||||
return nil
|
||||
}
|
||||
return attr.Expr
|
||||
}
|
|
@ -229,7 +229,7 @@ func checkInvalidTokens(tokens Tokens) hcl.Diagnostics {
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid character",
|
||||
Detail: "The \";\" character is not valid. Use newlines to separate attributes and blocks, and commas to separate items in collection values.",
|
||||
Detail: "The \";\" character is not valid. Use newlines to separate arguments and blocks, and commas to separate items in collection values.",
|
||||
Subject: &tok.Range,
|
||||
})
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ func Walk(node Node, w Walker) hcl.Diagnostics {
|
|||
diags = append(diags, Walk(node, w)...)
|
||||
return node
|
||||
})
|
||||
moreDiags := w.Exit(node)
|
||||
diags = append(diags, moreDiags...)
|
||||
return diags
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ func parseValue(p *peeker) (node, hcl.Diagnostics) {
|
|||
return wrapInvalid(nil, hcl.Diagnostics{
|
||||
{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing attribute value",
|
||||
Summary: "Missing JSON value",
|
||||
Detail: "A JSON value must start with a brace, a bracket, a number, a string, or a keyword.",
|
||||
Subject: &tok.Range,
|
||||
},
|
||||
|
@ -144,8 +144,8 @@ Token:
|
|||
if !ok {
|
||||
return nil, diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object attribute name",
|
||||
Detail: "A JSON object attribute name must be a string",
|
||||
Summary: "Invalid object property name",
|
||||
Detail: "A JSON object property name must be a string",
|
||||
Subject: keyNode.StartRange().Ptr(),
|
||||
})
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ Token:
|
|||
// Possible confusion with native HCL syntax.
|
||||
return nil, diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing attribute value colon",
|
||||
Summary: "Missing property value colon",
|
||||
Detail: "JSON uses a colon as its name/value delimiter, not an equals sign.",
|
||||
Subject: &colon.Range,
|
||||
})
|
||||
|
@ -179,8 +179,8 @@ Token:
|
|||
|
||||
return nil, diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing attribute value colon",
|
||||
Detail: "A colon must appear between an object attribute's name and its value.",
|
||||
Summary: "Missing property value colon",
|
||||
Detail: "A colon must appear between an object property's name and its value.",
|
||||
Subject: &colon.Range,
|
||||
})
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ Token:
|
|||
return nil, diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Trailing comma in object",
|
||||
Detail: "JSON does not permit a trailing comma after the final attribute in an object.",
|
||||
Detail: "JSON does not permit a trailing comma after the final property in an object.",
|
||||
Subject: &comma.Range,
|
||||
})
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ Token:
|
|||
return nil, diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing attribute seperator comma",
|
||||
Detail: "A comma must appear between each attribute declaration in an object.",
|
||||
Detail: "A comma must appear between each property definition in an object.",
|
||||
Subject: p.Peek().Range.Ptr(),
|
||||
})
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ Token:
|
|||
return nil, diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Trailing comma in array",
|
||||
Detail: "JSON does not permit a trailing comma after the final attribute in an array.",
|
||||
Detail: "JSON does not permit a trailing comma after the final value in an array.",
|
||||
Subject: &comma.Range,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ for defining configuration languages for applications. The HCL information
|
|||
model is designed to support multiple concrete syntaxes for configuration,
|
||||
and this JSON-based format complements [the native syntax](../hclsyntax/spec.md)
|
||||
by being easy to machine-generate, whereas the native syntax is oriented
|
||||
towards human authoring and maintenence.
|
||||
towards human authoring and maintenance
|
||||
|
||||
This syntax is defined in terms of JSON as defined in
|
||||
[RFC7159](https://tools.ietf.org/html/rfc7159). As such it inherits the JSON
|
||||
|
@ -280,9 +280,9 @@ When interpreted as an expression, a JSON array represents a value of a HCL
|
|||
tuple type.
|
||||
|
||||
Each element of the JSON array represents an element of the HCL tuple type.
|
||||
The tuple type is constructed by enumerationg the JSON array elements, creating
|
||||
The tuple type is constructed by enumerating the JSON array elements, creating
|
||||
for each an element whose type is the result of recursively applying the
|
||||
expression mapping rules. Correspondance is preserved between the array element
|
||||
expression mapping rules. Correspondence is preserved between the array element
|
||||
indices and the tuple element indices.
|
||||
|
||||
An instance of the constructed tuple type is then created, whose values are
|
||||
|
@ -325,7 +325,7 @@ HCL null value of the dynamic pseudo-type.
|
|||
|
||||
### Strings
|
||||
|
||||
When intepreted as an expression, a JSON string may be interpreted in one of
|
||||
When interpreted as an expression, a JSON string may be interpreted in one of
|
||||
two ways depending on the evaluation mode.
|
||||
|
||||
If evaluating in literal-only mode (as defined by the syntax-agnostic
|
||||
|
|
|
@ -64,7 +64,7 @@ func (b *body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostic
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Extraneous JSON object property",
|
||||
Detail: fmt.Sprintf("No attribute or block type is named %q.%s", k, suggestion),
|
||||
Detail: fmt.Sprintf("No argument or block type is named %q.%s", k, suggestion),
|
||||
Subject: &attr.NameRange,
|
||||
Context: attr.Range().Ptr(),
|
||||
})
|
||||
|
@ -114,8 +114,8 @@ func (b *body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Bod
|
|||
if existing, exists := content.Attributes[attrName]; exists {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Duplicate attribute definition",
|
||||
Detail: fmt.Sprintf("The attribute %q was already defined at %s.", attrName, existing.Range),
|
||||
Summary: "Duplicate argument",
|
||||
Detail: fmt.Sprintf("The argument %q was already set at %s.", attrName, existing.Range),
|
||||
Subject: &jsonAttr.NameRange,
|
||||
Context: jsonAttr.Range().Ptr(),
|
||||
})
|
||||
|
@ -149,8 +149,8 @@ func (b *body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Bod
|
|||
if _, defined := content.Attributes[attrS.Name]; !defined {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing required attribute",
|
||||
Detail: fmt.Sprintf("The attribute %q is required, but no definition was found.", attrS.Name),
|
||||
Summary: "Missing required argument",
|
||||
Detail: fmt.Sprintf("The argument %q is required, but no definition was found.", attrS.Name),
|
||||
Subject: b.MissingItemRange().Ptr(),
|
||||
})
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ func (b *body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Incorrect JSON value type",
|
||||
Detail: "A JSON object is required here, defining the attributes for this block.",
|
||||
Detail: "A JSON object is required here, setting the arguments for this block.",
|
||||
Subject: b.val.StartRange().Ptr(),
|
||||
})
|
||||
return attrs, diags
|
||||
|
@ -197,7 +197,7 @@ func (b *body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Duplicate attribute definition",
|
||||
Detail: fmt.Sprintf("The attribute %q was already defined at %s.", name, existing.Range),
|
||||
Detail: fmt.Sprintf("The argument %q was already set at %s.", name, existing.Range),
|
||||
Subject: &jsonAttr.NameRange,
|
||||
})
|
||||
continue
|
||||
|
@ -345,7 +345,7 @@ func (b *body) collectDeepAttrs(v node, labelName *string) ([]*objectAttr, hcl.D
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Incorrect JSON value type",
|
||||
Detail: "A JSON object is required here, to define attributes and child blocks.",
|
||||
Detail: "A JSON object is required here, to define arguments and child blocks.",
|
||||
Subject: ev.StartRange().Ptr(),
|
||||
})
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ func (b *body) collectDeepAttrs(v node, labelName *string) ([]*objectAttr, hcl.D
|
|||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Incorrect JSON value type",
|
||||
Detail: "Either a JSON object or JSON array of objects is required here, to define attributes and child blocks.",
|
||||
Detail: "Either a JSON object or JSON array of objects is required here, to define arguments and child blocks.",
|
||||
Subject: v.StartRange().Ptr(),
|
||||
})
|
||||
}
|
||||
|
@ -432,7 +432,8 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
Value: jsonAttr.Name,
|
||||
SrcRange: jsonAttr.NameRange,
|
||||
}}).Value(ctx)
|
||||
val, valDiags := (&expression{src: jsonAttr.Value}).Value(ctx)
|
||||
valExpr := &expression{src: jsonAttr.Value}
|
||||
val, valDiags := valExpr.Value(ctx)
|
||||
diags = append(diags, nameDiags...)
|
||||
diags = append(diags, valDiags...)
|
||||
|
||||
|
@ -440,19 +441,23 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
name, err = convert.Convert(name, cty.String)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key expression",
|
||||
Detail: fmt.Sprintf("Cannot use this expression as an object key: %s.", err),
|
||||
Subject: &jsonAttr.NameRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key expression",
|
||||
Detail: fmt.Sprintf("Cannot use this expression as an object key: %s.", err),
|
||||
Subject: &jsonAttr.NameRange,
|
||||
Expression: valExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
continue
|
||||
}
|
||||
if name.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key expression",
|
||||
Detail: "Cannot use null value as an object key.",
|
||||
Subject: &jsonAttr.NameRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid object key expression",
|
||||
Detail: "Cannot use null value as an object key.",
|
||||
Subject: &jsonAttr.NameRange,
|
||||
Expression: valExpr,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
@ -471,10 +476,12 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
nameStr := name.AsString()
|
||||
if _, defined := attrs[nameStr]; defined {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Duplicate object attribute",
|
||||
Detail: fmt.Sprintf("An attribute named %q was already defined at %s.", nameStr, attrRanges[nameStr]),
|
||||
Subject: &jsonAttr.NameRange,
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Duplicate object attribute",
|
||||
Detail: fmt.Sprintf("An attribute named %q was already defined at %s.", nameStr, attrRanges[nameStr]),
|
||||
Subject: &jsonAttr.NameRange,
|
||||
Expression: e,
|
||||
EvalContext: ctx,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -109,9 +109,9 @@ func (mb mergedBodies) JustAttributes() (Attributes, Diagnostics) {
|
|||
if existing := attrs[name]; existing != nil {
|
||||
diags = diags.Append(&Diagnostic{
|
||||
Severity: DiagError,
|
||||
Summary: "Duplicate attribute",
|
||||
Summary: "Duplicate argument",
|
||||
Detail: fmt.Sprintf(
|
||||
"Attribute %q was already assigned at %s",
|
||||
"Argument %q was already set at %s",
|
||||
name, existing.NameRange.String(),
|
||||
),
|
||||
Subject: &attr.NameRange,
|
||||
|
@ -182,9 +182,9 @@ func (mb mergedBodies) mergedContent(schema *BodySchema, partial bool) (*BodyCon
|
|||
if existing := content.Attributes[name]; existing != nil {
|
||||
diags = diags.Append(&Diagnostic{
|
||||
Severity: DiagError,
|
||||
Summary: "Duplicate attribute",
|
||||
Summary: "Duplicate argument",
|
||||
Detail: fmt.Sprintf(
|
||||
"Attribute %q was already assigned at %s",
|
||||
"Argument %q was already set at %s",
|
||||
name, existing.NameRange.String(),
|
||||
),
|
||||
Subject: &attr.NameRange,
|
||||
|
@ -212,9 +212,9 @@ func (mb mergedBodies) mergedContent(schema *BodySchema, partial bool) (*BodyCon
|
|||
// use of required attributes on merged bodies.
|
||||
diags = diags.Append(&Diagnostic{
|
||||
Severity: DiagError,
|
||||
Summary: "Missing required attribute",
|
||||
Summary: "Missing required argument",
|
||||
Detail: fmt.Sprintf(
|
||||
"The attribute %q is required, but was not assigned.",
|
||||
"The argument %q is required, but was not set.",
|
||||
attrS.Name,
|
||||
),
|
||||
})
|
||||
|
|
|
@ -94,6 +94,16 @@ func RangeOver(a, b Range) Range {
|
|||
}
|
||||
}
|
||||
|
||||
// ContainsPos returns true if and only if the given position is contained within
|
||||
// the receiving range.
|
||||
//
|
||||
// In the unlikely case that the line/column information disagree with the byte
|
||||
// offset information in the given position or receiving range, the byte
|
||||
// offsets are given priority.
|
||||
func (r Range) ContainsPos(pos Pos) bool {
|
||||
return r.ContainsOffset(pos.Byte)
|
||||
}
|
||||
|
||||
// ContainsOffset returns true if and only if the given byte offset is within
|
||||
// the receiving Range.
|
||||
func (r Range) ContainsOffset(offset int) bool {
|
||||
|
|
|
@ -29,7 +29,7 @@ which are discussed in detail in a later section.
|
|||
A _block_ is a nested structure that has a _type name_, zero or more string
|
||||
_labels_ (e.g. identifiers), and a nested body.
|
||||
|
||||
Together the structural elements create a heirarchical data structure, with
|
||||
Together the structural elements create a hierarchical data structure, with
|
||||
attributes intended to represent the direct properties of a particular object
|
||||
in the calling application, and blocks intended to represent child objects
|
||||
of a particular object.
|
||||
|
@ -269,7 +269,7 @@ are two structural type _kinds_:
|
|||
has a type. Attribute names are always strings. (_Object_ attributes are a
|
||||
distinct idea from _body_ attributes, though calling applications
|
||||
may choose to blur the distinction by use of common naming schemes.)
|
||||
* _Tuple tupes_ are constructed of a sequence of elements, each of which
|
||||
* _Tuple types_ are constructed of a sequence of elements, each of which
|
||||
has a type.
|
||||
|
||||
Values of structural types are compared for equality in terms of their
|
||||
|
@ -301,10 +301,10 @@ the same element type.
|
|||
|
||||
### Null values
|
||||
|
||||
Each type has a null value. The null value of a type represents the absense
|
||||
Each type has a null value. The null value of a type represents the absence
|
||||
of a value, but with type information retained to allow for type checking.
|
||||
|
||||
Null values are used primarily to represent the conditional absense of a
|
||||
Null values are used primarily to represent the conditional absence of a
|
||||
body attribute. In a syntax with a conditional operator, one of the result
|
||||
values of that conditional may be null to indicate that the attribute should be
|
||||
considered not present in that case.
|
||||
|
@ -458,7 +458,7 @@ If semantic checking succeeds without error, the call is _executed_:
|
|||
definition is used to determine the call's _result value_.
|
||||
|
||||
The result of a function call expression is either an error, if one of the
|
||||
erroenous conditions above applies, or the _result value_.
|
||||
erroneous conditions above applies, or the _result value_.
|
||||
|
||||
## Type Conversions and Unification
|
||||
|
||||
|
@ -505,7 +505,7 @@ Bidirectional conversions are available between the string and number types,
|
|||
and between the string and boolean types.
|
||||
|
||||
The bool value true corresponds to the string containing the characters "true",
|
||||
while the bool value false corresponds to teh string containing the characters
|
||||
while the bool value false corresponds to the string containing the characters
|
||||
"false". Conversion from bool to string is safe, while the converse is
|
||||
unsafe. The strings "1" and "0" are alternative string representations
|
||||
of true and false respectively. It is an error to convert a string other than
|
||||
|
@ -671,7 +671,7 @@ The language-agnosticism of this specification assumes that certain behaviors
|
|||
are implemented separately for each syntax:
|
||||
|
||||
* Matching of a body schema with the physical elements of a body in the
|
||||
source language, to determine correspondance between physical constructs
|
||||
source language, to determine correspondence between physical constructs
|
||||
and schema elements.
|
||||
|
||||
* Implementing the _dynamic attributes_ body processing mode by either
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package hcl
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// The methods in this file all have the general pattern of making a best-effort
|
||||
// to find one or more constructs that contain a given source position.
|
||||
//
|
||||
// These all operate by delegating to an optional method of the same name and
|
||||
// signature on the file's root body, allowing each syntax to potentially
|
||||
// provide its own implementations of these. For syntaxes that don't implement
|
||||
// them, the result is always nil.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// BlocksAtPos attempts to find all of the blocks that contain the given
|
||||
// position, ordered so that the outermost block is first and the innermost
|
||||
// block is last. This is a best-effort method that may not be able to produce
|
||||
// a complete result for all positions or for all HCL syntaxes.
|
||||
//
|
||||
// If the returned slice is non-empty, the first element is guaranteed to
|
||||
// represent the same block as would be the result of OutermostBlockAtPos and
|
||||
// the last element the result of InnermostBlockAtPos. However, the
|
||||
// implementation may return two different objects describing the same block,
|
||||
// so comparison by pointer identity is not possible.
|
||||
//
|
||||
// The result is nil if no blocks at all contain the given position.
|
||||
func (f *File) BlocksAtPos(pos Pos) []*Block {
|
||||
// The root body of the file must implement this interface in order
|
||||
// to support BlocksAtPos.
|
||||
type Interface interface {
|
||||
BlocksAtPos(pos Pos) []*Block
|
||||
}
|
||||
|
||||
impl, ok := f.Body.(Interface)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return impl.BlocksAtPos(pos)
|
||||
}
|
||||
|
||||
// OutermostBlockAtPos attempts to find a top-level block in the receiving file
|
||||
// that contains the given position. This is a best-effort method that may not
|
||||
// be able to produce a result for all positions or for all HCL syntaxes.
|
||||
//
|
||||
// The result is nil if no single block could be selected for any reason.
|
||||
func (f *File) OutermostBlockAtPos(pos Pos) *Block {
|
||||
// The root body of the file must implement this interface in order
|
||||
// to support OutermostBlockAtPos.
|
||||
type Interface interface {
|
||||
OutermostBlockAtPos(pos Pos) *Block
|
||||
}
|
||||
|
||||
impl, ok := f.Body.(Interface)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return impl.OutermostBlockAtPos(pos)
|
||||
}
|
||||
|
||||
// InnermostBlockAtPos attempts to find the most deeply-nested block in the
|
||||
// receiving file that contains the given position. This is a best-effort
|
||||
// method that may not be able to produce a result for all positions or for
|
||||
// all HCL syntaxes.
|
||||
//
|
||||
// The result is nil if no single block could be selected for any reason.
|
||||
func (f *File) InnermostBlockAtPos(pos Pos) *Block {
|
||||
// The root body of the file must implement this interface in order
|
||||
// to support InnermostBlockAtPos.
|
||||
type Interface interface {
|
||||
InnermostBlockAtPos(pos Pos) *Block
|
||||
}
|
||||
|
||||
impl, ok := f.Body.(Interface)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return impl.InnermostBlockAtPos(pos)
|
||||
}
|
||||
|
||||
// OutermostExprAtPos attempts to find an expression in the receiving file
|
||||
// that contains the given position. This is a best-effort method that may not
|
||||
// be able to produce a result for all positions or for all HCL syntaxes.
|
||||
//
|
||||
// Since expressions are often nested inside one another, this method returns
|
||||
// the outermost "root" expression that is not contained by any other.
|
||||
//
|
||||
// The result is nil if no single expression could be selected for any reason.
|
||||
func (f *File) OutermostExprAtPos(pos Pos) Expression {
|
||||
// The root body of the file must implement this interface in order
|
||||
// to support OutermostExprAtPos.
|
||||
type Interface interface {
|
||||
OutermostExprAtPos(pos Pos) Expression
|
||||
}
|
||||
|
||||
impl, ok := f.Body.(Interface)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return impl.OutermostExprAtPos(pos)
|
||||
}
|
||||
|
||||
// AttributeAtPos attempts to find an attribute definition in the receiving
|
||||
// file that contains the given position. This is a best-effort method that may
|
||||
// not be able to produce a result for all positions or for all HCL syntaxes.
|
||||
//
|
||||
// The result is nil if no single attribute could be selected for any reason.
|
||||
func (f *File) AttributeAtPos(pos Pos) *Attribute {
|
||||
// The root body of the file must implement this interface in order
|
||||
// to support OutermostExprAtPos.
|
||||
type Interface interface {
|
||||
AttributeAtPos(pos Pos) *Attribute
|
||||
}
|
||||
|
||||
impl, ok := f.Body.(Interface)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return impl.AttributeAtPos(pos)
|
||||
}
|
|
@ -65,7 +65,10 @@ func ChildBlockTypes(spec Spec) map[string]Spec {
|
|||
visit = func(s Spec) {
|
||||
if bs, ok := s.(blockSpec); ok {
|
||||
for _, blockS := range bs.blockHeaderSchemata() {
|
||||
ret[blockS.Type] = bs.nestedSpec()
|
||||
nested := bs.nestedSpec()
|
||||
if nested != nil { // nil can be returned to dynamically opt out of this interface
|
||||
ret[blockS.Type] = nested
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package hcldec
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
@ -477,6 +478,44 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe
|
|||
if len(elems) == 0 {
|
||||
ret = cty.ListValEmpty(s.Nested.impliedType())
|
||||
} else {
|
||||
// Since our target is a list, all of the decoded elements must have the
|
||||
// same type or cty.ListVal will panic below. Different types can arise
|
||||
// if there is an attribute spec of type cty.DynamicPseudoType in the
|
||||
// nested spec; all given values must be convertable to a single type
|
||||
// in order for the result to be considered valid.
|
||||
etys := make([]cty.Type, len(elems))
|
||||
for i, v := range elems {
|
||||
etys[i] = v.Type()
|
||||
}
|
||||
ety, convs := convert.UnifyUnsafe(etys)
|
||||
if ety == cty.NilType {
|
||||
// FIXME: This is a pretty terrible error message.
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
|
||||
Detail: "Corresponding attributes in all blocks of this type must be the same.",
|
||||
Subject: &sourceRanges[0],
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
for i, v := range elems {
|
||||
if convs[i] != nil {
|
||||
newV, err := convs[i](v)
|
||||
if err != nil {
|
||||
// FIXME: This is a pretty terrible error message.
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
|
||||
Detail: fmt.Sprintf("Block with index %d has inconsistent argument types: %s.", i, err),
|
||||
Subject: &sourceRanges[i],
|
||||
})
|
||||
// Bail early here so we won't panic below in cty.ListVal
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
elems[i] = newV
|
||||
}
|
||||
}
|
||||
|
||||
ret = cty.ListVal(elems)
|
||||
}
|
||||
|
||||
|
@ -508,6 +547,127 @@ func (s *BlockListSpec) sourceRange(content *hcl.BodyContent, blockLabels []bloc
|
|||
return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
|
||||
}
|
||||
|
||||
// A BlockTupleSpec is a Spec that produces a cty tuple of the results of
|
||||
// decoding all of the nested blocks of a given type, using a nested spec.
|
||||
//
|
||||
// This is similar to BlockListSpec, but it permits the nested blocks to have
|
||||
// different result types in situations where cty.DynamicPseudoType attributes
|
||||
// are present.
|
||||
type BlockTupleSpec struct {
|
||||
TypeName string
|
||||
Nested Spec
|
||||
MinItems int
|
||||
MaxItems int
|
||||
}
|
||||
|
||||
func (s *BlockTupleSpec) visitSameBodyChildren(cb visitFunc) {
|
||||
// leaf node ("Nested" does not use the same body)
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *BlockTupleSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
|
||||
return []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: s.TypeName,
|
||||
LabelNames: findLabelSpecs(s.Nested),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *BlockTupleSpec) nestedSpec() Spec {
|
||||
return s.Nested
|
||||
}
|
||||
|
||||
// specNeedingVariables implementation
|
||||
func (s *BlockTupleSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
|
||||
var ret []hcl.Traversal
|
||||
|
||||
for _, childBlock := range content.Blocks {
|
||||
if childBlock.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, Variables(childBlock.Body, s.Nested)...)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
if s.Nested == nil {
|
||||
panic("BlockListSpec with no Nested Spec")
|
||||
}
|
||||
|
||||
var elems []cty.Value
|
||||
var sourceRanges []hcl.Range
|
||||
for _, childBlock := range content.Blocks {
|
||||
if childBlock.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
|
||||
diags = append(diags, childDiags...)
|
||||
elems = append(elems, val)
|
||||
sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
|
||||
}
|
||||
|
||||
if len(elems) < s.MinItems {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName),
|
||||
Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName),
|
||||
Subject: &content.MissingItemRange,
|
||||
})
|
||||
} else if s.MaxItems > 0 && len(elems) > s.MaxItems {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Too many %s blocks", s.TypeName),
|
||||
Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName),
|
||||
Subject: &sourceRanges[s.MaxItems],
|
||||
})
|
||||
}
|
||||
|
||||
var ret cty.Value
|
||||
|
||||
if len(elems) == 0 {
|
||||
ret = cty.EmptyTupleVal
|
||||
} else {
|
||||
ret = cty.TupleVal(elems)
|
||||
}
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
func (s *BlockTupleSpec) impliedType() cty.Type {
|
||||
// We can't predict our type, because we don't know how many blocks
|
||||
// there will be until we decode.
|
||||
return cty.DynamicPseudoType
|
||||
}
|
||||
|
||||
func (s *BlockTupleSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
|
||||
// We return the source range of the _first_ block of the given type,
|
||||
// since they are not guaranteed to form a contiguous range.
|
||||
|
||||
var childBlock *hcl.Block
|
||||
for _, candidate := range content.Blocks {
|
||||
if candidate.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
childBlock = candidate
|
||||
break
|
||||
}
|
||||
|
||||
if childBlock == nil {
|
||||
return content.MissingItemRange
|
||||
}
|
||||
|
||||
return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
|
||||
}
|
||||
|
||||
// A BlockSetSpec is a Spec that produces a cty set of the results of
|
||||
// decoding all of the nested blocks of a given type, using a nested spec.
|
||||
type BlockSetSpec struct {
|
||||
|
@ -592,6 +752,44 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel
|
|||
if len(elems) == 0 {
|
||||
ret = cty.SetValEmpty(s.Nested.impliedType())
|
||||
} else {
|
||||
// Since our target is a set, all of the decoded elements must have the
|
||||
// same type or cty.SetVal will panic below. Different types can arise
|
||||
// if there is an attribute spec of type cty.DynamicPseudoType in the
|
||||
// nested spec; all given values must be convertable to a single type
|
||||
// in order for the result to be considered valid.
|
||||
etys := make([]cty.Type, len(elems))
|
||||
for i, v := range elems {
|
||||
etys[i] = v.Type()
|
||||
}
|
||||
ety, convs := convert.UnifyUnsafe(etys)
|
||||
if ety == cty.NilType {
|
||||
// FIXME: This is a pretty terrible error message.
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
|
||||
Detail: "Corresponding attributes in all blocks of this type must be the same.",
|
||||
Subject: &sourceRanges[0],
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
for i, v := range elems {
|
||||
if convs[i] != nil {
|
||||
newV, err := convs[i](v)
|
||||
if err != nil {
|
||||
// FIXME: This is a pretty terrible error message.
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
|
||||
Detail: fmt.Sprintf("Block with index %d has inconsistent argument types: %s.", i, err),
|
||||
Subject: &sourceRanges[i],
|
||||
})
|
||||
// Bail early here so we won't panic below in cty.ListVal
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
elems[i] = newV
|
||||
}
|
||||
}
|
||||
|
||||
ret = cty.SetVal(elems)
|
||||
}
|
||||
|
||||
|
@ -672,7 +870,10 @@ func (s *BlockMapSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel
|
|||
var diags hcl.Diagnostics
|
||||
|
||||
if s.Nested == nil {
|
||||
panic("BlockSetSpec with no Nested Spec")
|
||||
panic("BlockMapSpec with no Nested Spec")
|
||||
}
|
||||
if ImpliedType(s).HasDynamicTypes() {
|
||||
panic("cty.DynamicPseudoType attributes may not be used inside a BlockMapSpec")
|
||||
}
|
||||
|
||||
elems := map[string]interface{}{}
|
||||
|
@ -765,6 +966,307 @@ func (s *BlockMapSpec) sourceRange(content *hcl.BodyContent, blockLabels []block
|
|||
return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
|
||||
}
|
||||
|
||||
// A BlockObjectSpec is a Spec that produces a cty object of the results of
|
||||
// decoding all of the nested blocks of a given type, using a nested spec.
|
||||
//
|
||||
// One level of object structure is created for each of the given label names.
|
||||
// There must be at least one given label name.
|
||||
//
|
||||
// This is similar to BlockMapSpec, but it permits the nested blocks to have
|
||||
// different result types in situations where cty.DynamicPseudoType attributes
|
||||
// are present.
|
||||
type BlockObjectSpec struct {
|
||||
TypeName string
|
||||
LabelNames []string
|
||||
Nested Spec
|
||||
}
|
||||
|
||||
func (s *BlockObjectSpec) visitSameBodyChildren(cb visitFunc) {
|
||||
// leaf node ("Nested" does not use the same body)
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *BlockObjectSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
|
||||
return []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: s.TypeName,
|
||||
LabelNames: append(s.LabelNames, findLabelSpecs(s.Nested)...),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *BlockObjectSpec) nestedSpec() Spec {
|
||||
return s.Nested
|
||||
}
|
||||
|
||||
// specNeedingVariables implementation
|
||||
func (s *BlockObjectSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
|
||||
var ret []hcl.Traversal
|
||||
|
||||
for _, childBlock := range content.Blocks {
|
||||
if childBlock.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, Variables(childBlock.Body, s.Nested)...)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *BlockObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
if s.Nested == nil {
|
||||
panic("BlockObjectSpec with no Nested Spec")
|
||||
}
|
||||
|
||||
elems := map[string]interface{}{}
|
||||
for _, childBlock := range content.Blocks {
|
||||
if childBlock.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
childLabels := labelsForBlock(childBlock)
|
||||
val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false)
|
||||
targetMap := elems
|
||||
for _, key := range childBlock.Labels[:len(s.LabelNames)-1] {
|
||||
if _, exists := targetMap[key]; !exists {
|
||||
targetMap[key] = make(map[string]interface{})
|
||||
}
|
||||
targetMap = targetMap[key].(map[string]interface{})
|
||||
}
|
||||
|
||||
diags = append(diags, childDiags...)
|
||||
|
||||
key := childBlock.Labels[len(s.LabelNames)-1]
|
||||
if _, exists := targetMap[key]; exists {
|
||||
labelsBuf := bytes.Buffer{}
|
||||
for _, label := range childBlock.Labels {
|
||||
fmt.Fprintf(&labelsBuf, " %q", label)
|
||||
}
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
|
||||
Detail: fmt.Sprintf(
|
||||
"A block for %s%s was already defined. The %s labels must be unique.",
|
||||
s.TypeName, labelsBuf.String(), s.TypeName,
|
||||
),
|
||||
Subject: &childBlock.DefRange,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
targetMap[key] = val
|
||||
}
|
||||
|
||||
if len(elems) == 0 {
|
||||
return cty.EmptyObjectVal, diags
|
||||
}
|
||||
|
||||
var ctyObj func(map[string]interface{}, int) cty.Value
|
||||
ctyObj = func(raw map[string]interface{}, depth int) cty.Value {
|
||||
vals := make(map[string]cty.Value, len(raw))
|
||||
if depth == 1 {
|
||||
for k, v := range raw {
|
||||
vals[k] = v.(cty.Value)
|
||||
}
|
||||
} else {
|
||||
for k, v := range raw {
|
||||
vals[k] = ctyObj(v.(map[string]interface{}), depth-1)
|
||||
}
|
||||
}
|
||||
return cty.ObjectVal(vals)
|
||||
}
|
||||
|
||||
return ctyObj(elems, len(s.LabelNames)), diags
|
||||
}
|
||||
|
||||
func (s *BlockObjectSpec) impliedType() cty.Type {
|
||||
// We can't predict our type, since we don't know how many blocks are
|
||||
// present and what labels they have until we decode.
|
||||
return cty.DynamicPseudoType
|
||||
}
|
||||
|
||||
func (s *BlockObjectSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
|
||||
// We return the source range of the _first_ block of the given type,
|
||||
// since they are not guaranteed to form a contiguous range.
|
||||
|
||||
var childBlock *hcl.Block
|
||||
for _, candidate := range content.Blocks {
|
||||
if candidate.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
childBlock = candidate
|
||||
break
|
||||
}
|
||||
|
||||
if childBlock == nil {
|
||||
return content.MissingItemRange
|
||||
}
|
||||
|
||||
return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
|
||||
}
|
||||
|
||||
// A BlockAttrsSpec is a Spec that interprets a single block as if it were
|
||||
// a map of some element type. That is, each attribute within the block
|
||||
// becomes a key in the resulting map and the attribute's value becomes the
|
||||
// element value, after conversion to the given element type. The resulting
|
||||
// value is a cty.Map of the given element type.
|
||||
//
|
||||
// This spec imposes a validation constraint that there be exactly one block
|
||||
// of the given type name and that this block may contain only attributes. The
|
||||
// block does not accept any labels.
|
||||
//
|
||||
// This is an alternative to an AttrSpec of a map type for situations where
|
||||
// block syntax is desired. Note that block syntax does not permit dynamic
|
||||
// keys, construction of the result via a "for" expression, etc. In most cases
|
||||
// an AttrSpec is preferred if the desired result is a map whose keys are
|
||||
// chosen by the user rather than by schema.
|
||||
type BlockAttrsSpec struct {
|
||||
TypeName string
|
||||
ElementType cty.Type
|
||||
Required bool
|
||||
}
|
||||
|
||||
func (s *BlockAttrsSpec) visitSameBodyChildren(cb visitFunc) {
|
||||
// leaf node
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *BlockAttrsSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
|
||||
return []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: s.TypeName,
|
||||
LabelNames: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *BlockAttrsSpec) nestedSpec() Spec {
|
||||
// This is an odd case: we aren't actually going to apply a nested spec
|
||||
// in this case, since we're going to interpret the body directly as
|
||||
// attributes, but we need to return something non-nil so that the
|
||||
// decoder will recognize this as a block spec. We won't actually be
|
||||
// using this for anything at decode time.
|
||||
return noopSpec{}
|
||||
}
|
||||
|
||||
// specNeedingVariables implementation
|
||||
func (s *BlockAttrsSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
|
||||
|
||||
block, _ := s.findBlock(content)
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var vars []hcl.Traversal
|
||||
|
||||
attrs, diags := block.Body.JustAttributes()
|
||||
if diags.HasErrors() {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
vars = append(vars, attr.Expr.Variables()...)
|
||||
}
|
||||
|
||||
// We'll return the variables references in source order so that any
|
||||
// error messages that result are also in source order.
|
||||
sort.Slice(vars, func(i, j int) bool {
|
||||
return vars[i].SourceRange().Start.Byte < vars[j].SourceRange().Start.Byte
|
||||
})
|
||||
|
||||
return vars
|
||||
}
|
||||
|
||||
func (s *BlockAttrsSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
block, other := s.findBlock(content)
|
||||
if block == nil {
|
||||
if s.Required {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Missing %s block", s.TypeName),
|
||||
Detail: fmt.Sprintf(
|
||||
"A block of type %q is required here.", s.TypeName,
|
||||
),
|
||||
Subject: &content.MissingItemRange,
|
||||
})
|
||||
}
|
||||
return cty.NullVal(cty.Map(s.ElementType)), diags
|
||||
}
|
||||
if other != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
|
||||
Detail: fmt.Sprintf(
|
||||
"Only one block of type %q is allowed. Previous definition was at %s.",
|
||||
s.TypeName, block.DefRange.String(),
|
||||
),
|
||||
Subject: &other.DefRange,
|
||||
})
|
||||
}
|
||||
|
||||
attrs, attrDiags := block.Body.JustAttributes()
|
||||
diags = append(diags, attrDiags...)
|
||||
|
||||
if len(attrs) == 0 {
|
||||
return cty.MapValEmpty(s.ElementType), diags
|
||||
}
|
||||
|
||||
vals := make(map[string]cty.Value, len(attrs))
|
||||
for name, attr := range attrs {
|
||||
attrVal, attrDiags := attr.Expr.Value(ctx)
|
||||
diags = append(diags, attrDiags...)
|
||||
|
||||
attrVal, err := convert.Convert(attrVal, s.ElementType)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid attribute value",
|
||||
Detail: fmt.Sprintf("Invalid value for attribute of %q block: %s.", s.TypeName, err),
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
attrVal = cty.UnknownVal(s.ElementType)
|
||||
}
|
||||
|
||||
vals[name] = attrVal
|
||||
}
|
||||
|
||||
return cty.MapVal(vals), diags
|
||||
}
|
||||
|
||||
func (s *BlockAttrsSpec) impliedType() cty.Type {
|
||||
return cty.Map(s.ElementType)
|
||||
}
|
||||
|
||||
func (s *BlockAttrsSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
|
||||
block, _ := s.findBlock(content)
|
||||
if block == nil {
|
||||
return content.MissingItemRange
|
||||
}
|
||||
return block.DefRange
|
||||
}
|
||||
|
||||
func (s *BlockAttrsSpec) findBlock(content *hcl.BodyContent) (block *hcl.Block, other *hcl.Block) {
|
||||
for _, candidate := range content.Blocks {
|
||||
if candidate.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
if block != nil {
|
||||
return block, candidate
|
||||
}
|
||||
block = candidate
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// A BlockLabelSpec is a Spec that returns a cty.String representing the
|
||||
// label of the block its given body belongs to, if indeed its given body
|
||||
// belongs to a block. It is a programming error to use this in a non-block
|
||||
|
@ -848,6 +1350,16 @@ func findLabelSpecs(spec Spec) []string {
|
|||
//
|
||||
// The two specifications must have the same implied result type for correct
|
||||
// operation. If not, the result is undefined.
|
||||
//
|
||||
// Any requirements imposed by the "Default" spec apply even if "Primary" does
|
||||
// not return null. For example, if the "Default" spec is for a required
|
||||
// attribute then that attribute is always required, regardless of the result
|
||||
// of the "Primary" spec.
|
||||
//
|
||||
// The "Default" spec must not describe a nested block, since otherwise the
|
||||
// result of ChildBlockTypes would not be decidable without evaluation. If
|
||||
// the default spec _does_ describe a nested block then the result is
|
||||
// undefined.
|
||||
type DefaultSpec struct {
|
||||
Primary Spec
|
||||
Default Spec
|
||||
|
@ -872,6 +1384,38 @@ func (s *DefaultSpec) impliedType() cty.Type {
|
|||
return s.Primary.impliedType()
|
||||
}
|
||||
|
||||
// attrSpec implementation
|
||||
func (s *DefaultSpec) attrSchemata() []hcl.AttributeSchema {
|
||||
// We must pass through the union of both of our nested specs so that
|
||||
// we'll have both values available in the result.
|
||||
var ret []hcl.AttributeSchema
|
||||
if as, ok := s.Primary.(attrSpec); ok {
|
||||
ret = append(ret, as.attrSchemata()...)
|
||||
}
|
||||
if as, ok := s.Default.(attrSpec); ok {
|
||||
ret = append(ret, as.attrSchemata()...)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *DefaultSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
|
||||
// Only the primary spec may describe a block, since otherwise
|
||||
// our nestedSpec method below can't know which to return.
|
||||
if bs, ok := s.Primary.(blockSpec); ok {
|
||||
return bs.blockHeaderSchemata()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *DefaultSpec) nestedSpec() Spec {
|
||||
if bs, ok := s.Primary.(blockSpec); ok {
|
||||
return bs.nestedSpec()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DefaultSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
|
||||
// We can't tell from here which of the two specs will ultimately be used
|
||||
// in our result, so we'll just assume the first. This is usually the right
|
||||
|
@ -996,3 +1540,28 @@ func (s *TransformFuncSpec) sourceRange(content *hcl.BodyContent, blockLabels []
|
|||
// not super-accurate, because there's nothing better to return.
|
||||
return s.Wrapped.sourceRange(content, blockLabels)
|
||||
}
|
||||
|
||||
// noopSpec is a placeholder spec that does nothing, used in situations where
|
||||
// a non-nil placeholder spec is required. It is not exported because there is
|
||||
// no reason to use it directly; it is always an implementation detail only.
|
||||
type noopSpec struct {
|
||||
}
|
||||
|
||||
func (s noopSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
return cty.NullVal(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
func (s noopSpec) impliedType() cty.Type {
|
||||
return cty.DynamicPseudoType
|
||||
}
|
||||
|
||||
func (s noopSpec) visitSameBodyChildren(cb visitFunc) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
func (s noopSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
|
||||
// No useful range for a noopSpec, and nobody should be calling this anyway.
|
||||
return hcl.Range{
|
||||
Filename: "noopSpec",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,20 +15,22 @@ import (
|
|||
// be incomplete, but that's assumed to be okay because the eventual call
|
||||
// to Decode will produce error diagnostics anyway.
|
||||
func Variables(body hcl.Body, spec Spec) []hcl.Traversal {
|
||||
schema := ImpliedSchema(spec)
|
||||
|
||||
content, _, _ := body.PartialContent(schema)
|
||||
|
||||
var vars []hcl.Traversal
|
||||
schema := ImpliedSchema(spec)
|
||||
content, _, _ := body.PartialContent(schema)
|
||||
|
||||
if vs, ok := spec.(specNeedingVariables); ok {
|
||||
vars = append(vars, vs.variablesNeeded(content)...)
|
||||
}
|
||||
spec.visitSameBodyChildren(func(s Spec) {
|
||||
|
||||
var visitFn visitFunc
|
||||
visitFn = func(s Spec) {
|
||||
if vs, ok := s.(specNeedingVariables); ok {
|
||||
vars = append(vars, vs.variablesNeeded(content)...)
|
||||
}
|
||||
})
|
||||
s.visitSameBodyChildren(visitFn)
|
||||
}
|
||||
spec.visitSameBodyChildren(visitFn)
|
||||
|
||||
return vars
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ func (b mockBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnos
|
|||
for _, attr := range remain.C.Attributes {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Extraneous attribute in mock body",
|
||||
Detail: fmt.Sprintf("Mock body has extraneous attribute %q.", attr.Name),
|
||||
Summary: "Extraneous argument in mock body",
|
||||
Detail: fmt.Sprintf("Mock body has extraneous argument %q.", attr.Name),
|
||||
Subject: &attr.NameRange,
|
||||
})
|
||||
}
|
||||
|
@ -63,8 +63,8 @@ func (b mockBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.
|
|||
if attrS.Required {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing required attribute",
|
||||
Detail: fmt.Sprintf("Mock body doesn't have attribute %q", name),
|
||||
Summary: "Missing required argument",
|
||||
Detail: fmt.Sprintf("Mock body doesn't have argument %q", name),
|
||||
Subject: b.C.MissingItemRange.Ptr(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,29 +3,29 @@ package hclwrite
|
|||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type Node interface {
|
||||
walkChildNodes(w internalWalkFunc)
|
||||
Tokens() *TokenSeq
|
||||
type File struct {
|
||||
inTree
|
||||
|
||||
srcBytes []byte
|
||||
body *node
|
||||
}
|
||||
|
||||
type internalWalkFunc func(Node)
|
||||
|
||||
type File struct {
|
||||
Name string
|
||||
SrcBytes []byte
|
||||
|
||||
Body *Body
|
||||
AllTokens *TokenSeq
|
||||
// Body returns the root body of the file, which contains the top-level
|
||||
// attributes and blocks.
|
||||
func (f *File) Body() *Body {
|
||||
return f.body.content.(*Body)
|
||||
}
|
||||
|
||||
// WriteTo writes the tokens underlying the receiving file to the given writer.
|
||||
//
|
||||
// The tokens first have a simple formatting pass applied that adjusts only
|
||||
// the spaces between them.
|
||||
func (f *File) WriteTo(wr io.Writer) (int, error) {
|
||||
return f.AllTokens.WriteTo(wr)
|
||||
tokens := f.inTree.children.BuildTokens(nil)
|
||||
format(tokens)
|
||||
return tokens.WriteTo(wr)
|
||||
}
|
||||
|
||||
// Bytes returns a buffer containing the source code resulting from the
|
||||
|
@ -37,155 +37,74 @@ func (f *File) Bytes() []byte {
|
|||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Format makes in-place modifications to the tokens underlying the receiving
|
||||
// file in order to change the whitespace to be in canonical form.
|
||||
func (f *File) Format() {
|
||||
format(f.Body.AllTokens.Tokens())
|
||||
type comments struct {
|
||||
leafNode
|
||||
|
||||
parent *node
|
||||
tokens Tokens
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
// Items may contain Attribute, Block and Unstructured instances.
|
||||
// Items and AllTokens should be updated only by methods of this type,
|
||||
// since they must be kept synchronized for correct operation.
|
||||
Items []Node
|
||||
AllTokens *TokenSeq
|
||||
|
||||
// IndentLevel is the number of spaces that should appear at the start
|
||||
// of lines added within this body.
|
||||
IndentLevel int
|
||||
}
|
||||
|
||||
func (n *Body) walkChildNodes(w internalWalkFunc) {
|
||||
for _, item := range n.Items {
|
||||
w(item)
|
||||
func newComments(tokens Tokens) *comments {
|
||||
return &comments{
|
||||
tokens: tokens,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Body) Tokens() *TokenSeq {
|
||||
return n.AllTokens
|
||||
func (c *comments) BuildTokens(to Tokens) Tokens {
|
||||
return c.tokens.BuildTokens(to)
|
||||
}
|
||||
|
||||
func (n *Body) AppendItem(node Node) {
|
||||
n.Items = append(n.Items, node)
|
||||
n.AppendUnstructuredTokens(node.Tokens())
|
||||
type identifier struct {
|
||||
leafNode
|
||||
|
||||
parent *node
|
||||
token *Token
|
||||
}
|
||||
|
||||
func (n *Body) AppendUnstructuredTokens(seq *TokenSeq) {
|
||||
if n.AllTokens == nil {
|
||||
new := make(TokenSeq, 0, 1)
|
||||
n.AllTokens = &new
|
||||
}
|
||||
*(n.AllTokens) = append(*(n.AllTokens), seq)
|
||||
}
|
||||
|
||||
// FindAttribute returns the first attribute item from the body that has the
|
||||
// given name, or returns nil if there is currently no matching attribute.
|
||||
//
|
||||
// A valid AST has only one definition of each attribute, but that constraint
|
||||
// is not enforced in the hclwrite AST, so a tree that has been mutated by
|
||||
// other calls may contain additional matching attributes that cannot be seen
|
||||
// by this method.
|
||||
func (n *Body) FindAttribute(name string) *Attribute {
|
||||
nameBytes := []byte(name)
|
||||
for _, item := range n.Items {
|
||||
if attr, ok := item.(*Attribute); ok {
|
||||
if attr.NameTokens.IsIdent(nameBytes) {
|
||||
return attr
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAttributeValue either replaces the expression of an existing attribute
|
||||
// of the given name or adds a new attribute definition to the end of the block.
|
||||
//
|
||||
// The value is given as a cty.Value, and must therefore be a literal. To set
|
||||
// a variable reference or other traversal, use SetAttributeTraversal.
|
||||
//
|
||||
// The return value is the attribute that was either modified in-place or
|
||||
// created.
|
||||
func (n *Body) SetAttributeValue(name string, val cty.Value) *Attribute {
|
||||
panic("Body.SetAttributeValue not yet implemented")
|
||||
}
|
||||
|
||||
// SetAttributeTraversal either replaces the expression of an existing attribute
|
||||
// of the given name or adds a new attribute definition to the end of the block.
|
||||
//
|
||||
// The new expression is given as a hcl.Traversal, which must be an absolute
|
||||
// traversal. To set a literal value, use SetAttributeValue.
|
||||
//
|
||||
// The return value is the attribute that was either modified in-place or
|
||||
// created.
|
||||
func (n *Body) SetAttributeTraversal(name string, traversal hcl.Traversal) *Attribute {
|
||||
panic("Body.SetAttributeTraversal not yet implemented")
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
AllTokens *TokenSeq
|
||||
|
||||
LeadCommentTokens *TokenSeq
|
||||
NameTokens *TokenSeq
|
||||
EqualsTokens *TokenSeq
|
||||
Expr *Expression
|
||||
LineCommentTokens *TokenSeq
|
||||
EOLTokens *TokenSeq
|
||||
}
|
||||
|
||||
func (a *Attribute) walkChildNodes(w internalWalkFunc) {
|
||||
w(a.Expr)
|
||||
}
|
||||
|
||||
func (n *Attribute) Tokens() *TokenSeq {
|
||||
return n.AllTokens
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
AllTokens *TokenSeq
|
||||
|
||||
LeadCommentTokens *TokenSeq
|
||||
TypeTokens *TokenSeq
|
||||
LabelTokens []*TokenSeq
|
||||
LabelTokensFlat *TokenSeq
|
||||
OBraceTokens *TokenSeq
|
||||
Body *Body
|
||||
CBraceTokens *TokenSeq
|
||||
EOLTokens *TokenSeq
|
||||
}
|
||||
|
||||
func (n *Block) walkChildNodes(w internalWalkFunc) {
|
||||
w(n.Body)
|
||||
}
|
||||
|
||||
func (n *Block) Tokens() *TokenSeq {
|
||||
return n.AllTokens
|
||||
}
|
||||
|
||||
type Expression struct {
|
||||
AllTokens *TokenSeq
|
||||
VarRefs []*VarRef
|
||||
}
|
||||
|
||||
func (n *Expression) walkChildNodes(w internalWalkFunc) {
|
||||
for _, name := range n.VarRefs {
|
||||
w(name)
|
||||
func newIdentifier(token *Token) *identifier {
|
||||
return &identifier{
|
||||
token: token,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Expression) Tokens() *TokenSeq {
|
||||
return n.AllTokens
|
||||
func (i *identifier) BuildTokens(to Tokens) Tokens {
|
||||
return append(to, i.token)
|
||||
}
|
||||
|
||||
type VarRef struct {
|
||||
// Tokens alternate between TokenIdent and TokenDot, with the first
|
||||
// and last elements always being TokenIdent.
|
||||
AllTokens *TokenSeq
|
||||
func (i *identifier) hasName(name string) bool {
|
||||
return name == string(i.token.Bytes)
|
||||
}
|
||||
|
||||
func (n *VarRef) walkChildNodes(w internalWalkFunc) {
|
||||
// no child nodes of a variable name
|
||||
type number struct {
|
||||
leafNode
|
||||
|
||||
parent *node
|
||||
token *Token
|
||||
}
|
||||
|
||||
func (n *VarRef) Tokens() *TokenSeq {
|
||||
return n.AllTokens
|
||||
func newNumber(token *Token) *number {
|
||||
return &number{
|
||||
token: token,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *number) BuildTokens(to Tokens) Tokens {
|
||||
return append(to, n.token)
|
||||
}
|
||||
|
||||
type quoted struct {
|
||||
leafNode
|
||||
|
||||
parent *node
|
||||
tokens Tokens
|
||||
}
|
||||
|
||||
func newQuoted(tokens Tokens) *quoted {
|
||||
return "ed{
|
||||
tokens: tokens,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *quoted) BuildTokens(to Tokens) Tokens {
|
||||
return q.tokens.BuildTokens(to)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package hclwrite
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type Body struct {
|
||||
inTree
|
||||
|
||||
items nodeSet
|
||||
}
|
||||
|
||||
func (b *Body) appendItem(c nodeContent) *node {
|
||||
nn := b.children.Append(c)
|
||||
b.items.Add(nn)
|
||||
return nn
|
||||
}
|
||||
|
||||
func (b *Body) appendItemNode(nn *node) *node {
|
||||
nn.assertUnattached()
|
||||
b.children.AppendNode(nn)
|
||||
b.items.Add(nn)
|
||||
return nn
|
||||
}
|
||||
|
||||
func (b *Body) AppendUnstructuredTokens(ts Tokens) {
|
||||
b.inTree.children.Append(ts)
|
||||
}
|
||||
|
||||
// GetAttribute returns the attribute from the body that has the given name,
|
||||
// or returns nil if there is currently no matching attribute.
|
||||
func (b *Body) GetAttribute(name string) *Attribute {
|
||||
for n := range b.items {
|
||||
if attr, isAttr := n.content.(*Attribute); isAttr {
|
||||
nameObj := attr.name.content.(*identifier)
|
||||
if nameObj.hasName(name) {
|
||||
// We've found it!
|
||||
return attr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAttributeValue either replaces the expression of an existing attribute
|
||||
// of the given name or adds a new attribute definition to the end of the block.
|
||||
//
|
||||
// The value is given as a cty.Value, and must therefore be a literal. To set
|
||||
// a variable reference or other traversal, use SetAttributeTraversal.
|
||||
//
|
||||
// The return value is the attribute that was either modified in-place or
|
||||
// created.
|
||||
func (b *Body) SetAttributeValue(name string, val cty.Value) *Attribute {
|
||||
attr := b.GetAttribute(name)
|
||||
expr := NewExpressionLiteral(val)
|
||||
if attr != nil {
|
||||
attr.expr = attr.expr.ReplaceWith(expr)
|
||||
} else {
|
||||
attr := newAttribute()
|
||||
attr.init(name, expr)
|
||||
b.appendItem(attr)
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
// SetAttributeTraversal either replaces the expression of an existing attribute
|
||||
// of the given name or adds a new attribute definition to the end of the block.
|
||||
//
|
||||
// The new expression is given as a hcl.Traversal, which must be an absolute
|
||||
// traversal. To set a literal value, use SetAttributeValue.
|
||||
//
|
||||
// The return value is the attribute that was either modified in-place or
|
||||
// created.
|
||||
func (b *Body) SetAttributeTraversal(name string, traversal hcl.Traversal) *Attribute {
|
||||
panic("Body.SetAttributeTraversal not yet implemented")
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
inTree
|
||||
|
||||
leadComments *node
|
||||
name *node
|
||||
expr *node
|
||||
lineComments *node
|
||||
}
|
||||
|
||||
func newAttribute() *Attribute {
|
||||
return &Attribute{
|
||||
inTree: newInTree(),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Attribute) init(name string, expr *Expression) {
|
||||
expr.assertUnattached()
|
||||
|
||||
nameTok := newIdentToken(name)
|
||||
nameObj := newIdentifier(nameTok)
|
||||
a.leadComments = a.children.Append(newComments(nil))
|
||||
a.name = a.children.Append(nameObj)
|
||||
a.children.AppendUnstructuredTokens(Tokens{
|
||||
{
|
||||
Type: hclsyntax.TokenEqual,
|
||||
Bytes: []byte{'='},
|
||||
},
|
||||
})
|
||||
a.expr = a.children.Append(expr)
|
||||
a.expr.list = a.children
|
||||
a.lineComments = a.children.Append(newComments(nil))
|
||||
a.children.AppendUnstructuredTokens(Tokens{
|
||||
{
|
||||
Type: hclsyntax.TokenNewline,
|
||||
Bytes: []byte{'\n'},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Attribute) Expr() *Expression {
|
||||
return a.expr.content.(*Expression)
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
inTree
|
||||
|
||||
leadComments *node
|
||||
typeName *node
|
||||
labels nodeSet
|
||||
open *node
|
||||
body *node
|
||||
close *node
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package hclwrite
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type Expression struct {
|
||||
inTree
|
||||
|
||||
absTraversals nodeSet
|
||||
}
|
||||
|
||||
func newExpression() *Expression {
|
||||
return &Expression{
|
||||
inTree: newInTree(),
|
||||
absTraversals: newNodeSet(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewExpressionLiteral constructs an an expression that represents the given
|
||||
// literal value.
|
||||
//
|
||||
// Since an unknown value cannot be represented in source code, this function
|
||||
// will panic if the given value is unknown or contains a nested unknown value.
|
||||
// Use val.IsWhollyKnown before calling to be sure.
|
||||
//
|
||||
// HCL native syntax does not directly represent lists, maps, and sets, and
|
||||
// instead relies on the automatic conversions to those collection types from
|
||||
// either list or tuple constructor syntax. Therefore converting collection
|
||||
// values to source code and re-reading them will lose type information, and
|
||||
// the reader must provide a suitable type at decode time to recover the
|
||||
// original value.
|
||||
func NewExpressionLiteral(val cty.Value) *Expression {
|
||||
toks := TokensForValue(val)
|
||||
expr := newExpression()
|
||||
expr.children.AppendUnstructuredTokens(toks)
|
||||
return expr
|
||||
}
|
||||
|
||||
// NewExpressionAbsTraversal constructs an expression that represents the
|
||||
// given traversal, which must be absolute or this function will panic.
|
||||
func NewExpressionAbsTraversal(traversal hcl.Traversal) *Expression {
|
||||
panic("NewExpressionAbsTraversal not yet implemented")
|
||||
}
|
||||
|
||||
type Traversal struct {
|
||||
inTree
|
||||
|
||||
steps nodeSet
|
||||
}
|
||||
|
||||
func newTraversal() *Traversal {
|
||||
return &Traversal{
|
||||
inTree: newInTree(),
|
||||
steps: newNodeSet(),
|
||||
}
|
||||
}
|
||||
|
||||
type TraverseName struct {
|
||||
inTree
|
||||
|
||||
name *node
|
||||
}
|
||||
|
||||
func newTraverseName() *TraverseName {
|
||||
return &TraverseName{
|
||||
inTree: newInTree(),
|
||||
}
|
||||
}
|
||||
|
||||
type TraverseIndex struct {
|
||||
inTree
|
||||
|
||||
key *node
|
||||
}
|
||||
|
||||
func newTraverseIndex() *TraverseIndex {
|
||||
return &TraverseIndex{
|
||||
inTree: newInTree(),
|
||||
}
|
||||
}
|
|
@ -239,10 +239,17 @@ func spaceAfterToken(subject, before, after *Token) bool {
|
|||
// No space right before a comma in an argument list
|
||||
return false
|
||||
|
||||
case subject.Type == hclsyntax.TokenComma:
|
||||
// Always a space after a comma
|
||||
return true
|
||||
|
||||
case subject.Type == hclsyntax.TokenQuotedLit || subject.Type == hclsyntax.TokenStringLit || subject.Type == hclsyntax.TokenOQuote || subject.Type == hclsyntax.TokenOHeredoc || after.Type == hclsyntax.TokenQuotedLit || after.Type == hclsyntax.TokenStringLit || after.Type == hclsyntax.TokenCQuote || after.Type == hclsyntax.TokenCHeredoc:
|
||||
// No extra spaces within templates
|
||||
return false
|
||||
|
||||
case after.Type == hclsyntax.TokenOBrack && (subject.Type == hclsyntax.TokenIdent || subject.Type == hclsyntax.TokenNumberLit || tokenBracketChange(subject) < 0):
|
||||
return false
|
||||
|
||||
case subject.Type == hclsyntax.TokenMinus:
|
||||
// Since a minus can either be subtraction or negation, and the latter
|
||||
// should _not_ have a space after it, we need to use some heuristics
|
||||
|
@ -293,8 +300,6 @@ func spaceAfterToken(subject, before, after *Token) bool {
|
|||
|
||||
func linesForFormat(tokens Tokens) []formatLine {
|
||||
if len(tokens) == 0 {
|
||||
// should never happen, since we should always have EOF, but let's
|
||||
// not crash anyway.
|
||||
return make([]formatLine, 0)
|
||||
}
|
||||
|
||||
|
@ -328,6 +333,16 @@ func linesForFormat(tokens Tokens) []formatLine {
|
|||
}
|
||||
}
|
||||
|
||||
// If a set of tokens doesn't end in TokenEOF (e.g. because it's a
|
||||
// fragment of tokens from the middle of a file) then we might fall
|
||||
// out here with a line still pending.
|
||||
if lineStart < len(tokens) {
|
||||
lines[li].lead = tokens[lineStart:]
|
||||
if lines[li].lead[len(lines[li].lead)-1].Type == hclsyntax.TokenEOF {
|
||||
lines[li].lead = lines[li].lead[:len(lines[li].lead)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// Now we'll pick off any trailing comments and attribute assignments
|
||||
// to shuffle off into the "comment" and "assign" cells.
|
||||
for i := range lines {
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
package hclwrite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// TokensForValue returns a sequence of tokens that represents the given
|
||||
// constant value.
|
||||
//
|
||||
// This function only supports types that are used by HCL. In particular, it
|
||||
// does not support capsule types and will panic if given one.
|
||||
//
|
||||
// It is not possible to express an unknown value in source code, so this
|
||||
// function will panic if the given value is unknown or contains any unknown
|
||||
// values. A caller can call the value's IsWhollyKnown method to verify that
|
||||
// no unknown values are present before calling TokensForValue.
|
||||
func TokensForValue(val cty.Value) Tokens {
|
||||
toks := appendTokensForValue(val, nil)
|
||||
format(toks) // fiddle with the SpacesBefore field to get canonical spacing
|
||||
return toks
|
||||
}
|
||||
|
||||
func appendTokensForValue(val cty.Value, toks Tokens) Tokens {
|
||||
switch {
|
||||
|
||||
case !val.IsKnown():
|
||||
panic("cannot produce tokens for unknown value")
|
||||
|
||||
case val.IsNull():
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenIdent,
|
||||
Bytes: []byte(`null`),
|
||||
})
|
||||
|
||||
case val.Type() == cty.Bool:
|
||||
var src []byte
|
||||
if val.True() {
|
||||
src = []byte(`true`)
|
||||
} else {
|
||||
src = []byte(`false`)
|
||||
}
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenIdent,
|
||||
Bytes: src,
|
||||
})
|
||||
|
||||
case val.Type() == cty.Number:
|
||||
bf := val.AsBigFloat()
|
||||
srcStr := bf.Text('f', -1)
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenNumberLit,
|
||||
Bytes: []byte(srcStr),
|
||||
})
|
||||
|
||||
case val.Type() == cty.String:
|
||||
// TODO: If it's a multi-line string ending in a newline, format
|
||||
// it as a HEREDOC instead.
|
||||
src := escapeQuotedStringLit(val.AsString())
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenOQuote,
|
||||
Bytes: []byte{'"'},
|
||||
})
|
||||
if len(src) > 0 {
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenQuotedLit,
|
||||
Bytes: src,
|
||||
})
|
||||
}
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenCQuote,
|
||||
Bytes: []byte{'"'},
|
||||
})
|
||||
|
||||
case val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType():
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenOBrack,
|
||||
Bytes: []byte{'['},
|
||||
})
|
||||
|
||||
i := 0
|
||||
for it := val.ElementIterator(); it.Next(); {
|
||||
if i > 0 {
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenComma,
|
||||
Bytes: []byte{','},
|
||||
})
|
||||
}
|
||||
_, eVal := it.Element()
|
||||
toks = appendTokensForValue(eVal, toks)
|
||||
i++
|
||||
}
|
||||
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenCBrack,
|
||||
Bytes: []byte{']'},
|
||||
})
|
||||
|
||||
case val.Type().IsMapType() || val.Type().IsObjectType():
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenOBrace,
|
||||
Bytes: []byte{'{'},
|
||||
})
|
||||
|
||||
i := 0
|
||||
for it := val.ElementIterator(); it.Next(); {
|
||||
if i > 0 {
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenComma,
|
||||
Bytes: []byte{','},
|
||||
})
|
||||
}
|
||||
eKey, eVal := it.Element()
|
||||
if hclsyntax.ValidIdentifier(eKey.AsString()) {
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenIdent,
|
||||
Bytes: []byte(eKey.AsString()),
|
||||
})
|
||||
} else {
|
||||
toks = appendTokensForValue(eKey, toks)
|
||||
}
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenEqual,
|
||||
Bytes: []byte{'='},
|
||||
})
|
||||
toks = appendTokensForValue(eVal, toks)
|
||||
i++
|
||||
}
|
||||
|
||||
toks = append(toks, &Token{
|
||||
Type: hclsyntax.TokenCBrace,
|
||||
Bytes: []byte{'}'},
|
||||
})
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("cannot produce tokens for %#v", val))
|
||||
}
|
||||
|
||||
return toks
|
||||
}
|
||||
|
||||
func escapeQuotedStringLit(s string) []byte {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
buf := make([]byte, 0, len(s))
|
||||
for i, r := range s {
|
||||
switch r {
|
||||
case '\n':
|
||||
buf = append(buf, '\\', 'n')
|
||||
case '\r':
|
||||
buf = append(buf, '\\', 'r')
|
||||
case '\t':
|
||||
buf = append(buf, '\\', 't')
|
||||
case '"':
|
||||
buf = append(buf, '\\', '"')
|
||||
case '\\':
|
||||
buf = append(buf, '\\', '\\')
|
||||
case '$', '%':
|
||||
buf = appendRune(buf, r)
|
||||
remain := s[i+1:]
|
||||
if len(remain) > 0 && remain[0] == '{' {
|
||||
// Double up our template introducer symbol to escape it.
|
||||
buf = appendRune(buf, r)
|
||||
}
|
||||
default:
|
||||
if !unicode.IsPrint(r) {
|
||||
var fmted string
|
||||
if r < 65536 {
|
||||
fmted = fmt.Sprintf("\\u%04x", r)
|
||||
} else {
|
||||
fmted = fmt.Sprintf("\\U%08x", r)
|
||||
}
|
||||
buf = append(buf, fmted...)
|
||||
} else {
|
||||
buf = appendRune(buf, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func appendRune(b []byte, r rune) []byte {
|
||||
l := utf8.RuneLen(r)
|
||||
for i := 0; i < l; i++ {
|
||||
b = append(b, 0) // make room at the end of our buffer
|
||||
}
|
||||
ch := b[len(b)-l:]
|
||||
utf8.EncodeRune(ch, r)
|
||||
return b
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
package hclwrite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
// node represents a node in the AST.
|
||||
type node struct {
|
||||
content nodeContent
|
||||
|
||||
list *nodes
|
||||
before, after *node
|
||||
}
|
||||
|
||||
func newNode(c nodeContent) *node {
|
||||
return &node{
|
||||
content: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) Equal(other *node) bool {
|
||||
return cmp.Equal(n.content, other.content)
|
||||
}
|
||||
|
||||
func (n *node) BuildTokens(to Tokens) Tokens {
|
||||
return n.content.BuildTokens(to)
|
||||
}
|
||||
|
||||
// Detach removes the receiver from the list it currently belongs to. If the
|
||||
// node is not currently in a list, this is a no-op.
|
||||
func (n *node) Detach() {
|
||||
if n.list == nil {
|
||||
return
|
||||
}
|
||||
if n.before != nil {
|
||||
n.before.after = n.after
|
||||
}
|
||||
if n.after != nil {
|
||||
n.after.before = n.before
|
||||
}
|
||||
if n.list.first == n {
|
||||
n.list.first = n.after
|
||||
}
|
||||
if n.list.last == n {
|
||||
n.list.last = n.before
|
||||
}
|
||||
n.list = nil
|
||||
n.before = nil
|
||||
n.after = nil
|
||||
}
|
||||
|
||||
// ReplaceWith removes the receiver from the list it currently belongs to and
|
||||
// inserts a new node with the given content in its place. If the node is not
|
||||
// currently in a list, this function will panic.
|
||||
//
|
||||
// The return value is the newly-constructed node, containing the given content.
|
||||
// After this function returns, the reciever is no longer attached to a list.
|
||||
func (n *node) ReplaceWith(c nodeContent) *node {
|
||||
if n.list == nil {
|
||||
panic("can't replace node that is not in a list")
|
||||
}
|
||||
|
||||
before := n.before
|
||||
after := n.after
|
||||
list := n.list
|
||||
n.before, n.after, n.list = nil, nil, nil
|
||||
|
||||
nn := newNode(c)
|
||||
nn.before = before
|
||||
nn.after = after
|
||||
nn.list = list
|
||||
if before != nil {
|
||||
before.after = nn
|
||||
}
|
||||
if after != nil {
|
||||
after.before = nn
|
||||
}
|
||||
return nn
|
||||
}
|
||||
|
||||
func (n *node) assertUnattached() {
|
||||
if n.list != nil {
|
||||
panic(fmt.Sprintf("attempt to attach already-attached node %#v", n))
|
||||
}
|
||||
}
|
||||
|
||||
// nodeContent is the interface type implemented by all AST content types.
|
||||
type nodeContent interface {
|
||||
walkChildNodes(w internalWalkFunc)
|
||||
BuildTokens(to Tokens) Tokens
|
||||
}
|
||||
|
||||
// nodes is a list of nodes.
|
||||
type nodes struct {
|
||||
first, last *node
|
||||
}
|
||||
|
||||
func (ns *nodes) BuildTokens(to Tokens) Tokens {
|
||||
for n := ns.first; n != nil; n = n.after {
|
||||
to = n.BuildTokens(to)
|
||||
}
|
||||
return to
|
||||
}
|
||||
|
||||
func (ns *nodes) Append(c nodeContent) *node {
|
||||
n := &node{
|
||||
content: c,
|
||||
}
|
||||
ns.AppendNode(n)
|
||||
n.list = ns
|
||||
return n
|
||||
}
|
||||
|
||||
func (ns *nodes) AppendNode(n *node) {
|
||||
if ns.last != nil {
|
||||
n.before = ns.last
|
||||
ns.last.after = n
|
||||
}
|
||||
n.list = ns
|
||||
ns.last = n
|
||||
if ns.first == nil {
|
||||
ns.first = n
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *nodes) AppendUnstructuredTokens(tokens Tokens) *node {
|
||||
if len(tokens) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := newNode(tokens)
|
||||
ns.AppendNode(n)
|
||||
n.list = ns
|
||||
return n
|
||||
}
|
||||
|
||||
// nodeSet is an unordered set of nodes. It is used to describe a set of nodes
|
||||
// that all belong to the same list that have some role or characteristic
|
||||
// in common.
|
||||
type nodeSet map[*node]struct{}
|
||||
|
||||
func newNodeSet() nodeSet {
|
||||
return make(nodeSet)
|
||||
}
|
||||
|
||||
func (ns nodeSet) Has(n *node) bool {
|
||||
if ns == nil {
|
||||
return false
|
||||
}
|
||||
_, exists := ns[n]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (ns nodeSet) Add(n *node) {
|
||||
ns[n] = struct{}{}
|
||||
}
|
||||
|
||||
func (ns nodeSet) Remove(n *node) {
|
||||
delete(ns, n)
|
||||
}
|
||||
|
||||
func (ns nodeSet) List() []*node {
|
||||
if len(ns) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make([]*node, 0, len(ns))
|
||||
|
||||
// Determine which list we are working with. We assume here that all of
|
||||
// the nodes belong to the same list, since that is part of the contract
|
||||
// for nodeSet.
|
||||
var list *nodes
|
||||
for n := range ns {
|
||||
list = n.list
|
||||
break
|
||||
}
|
||||
|
||||
// We recover the order by iterating over the whole list. This is not
|
||||
// the most efficient way to do it, but our node lists should always be
|
||||
// small so not worth making things more complex.
|
||||
for n := list.first; n != nil; n = n.after {
|
||||
if ns.Has(n) {
|
||||
ret = append(ret, n)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type internalWalkFunc func(*node)
|
||||
|
||||
// inTree can be embedded into a content struct that has child nodes to get
|
||||
// a standard implementation of the NodeContent interface and a record of
|
||||
// a potential parent node.
|
||||
type inTree struct {
|
||||
parent *node
|
||||
children *nodes
|
||||
}
|
||||
|
||||
func newInTree() inTree {
|
||||
return inTree{
|
||||
children: &nodes{},
|
||||
}
|
||||
}
|
||||
|
||||
func (it *inTree) assertUnattached() {
|
||||
if it.parent != nil {
|
||||
panic(fmt.Sprintf("node is already attached to %T", it.parent.content))
|
||||
}
|
||||
}
|
||||
|
||||
func (it *inTree) walkChildNodes(w internalWalkFunc) {
|
||||
for n := it.children.first; n != nil; n = n.after {
|
||||
w(n)
|
||||
}
|
||||
}
|
||||
|
||||
func (it *inTree) BuildTokens(to Tokens) Tokens {
|
||||
for n := it.children.first; n != nil; n = n.after {
|
||||
to = n.BuildTokens(to)
|
||||
}
|
||||
return to
|
||||
}
|
||||
|
||||
// leafNode can be embedded into a content struct to give it a do-nothing
|
||||
// implementation of walkChildNodes
|
||||
type leafNode struct {
|
||||
}
|
||||
|
||||
func (n *leafNode) walkChildNodes(w internalWalkFunc) {
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
package hclwrite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Our "parser" here is actually not doing any parsing of its own. Instead,
|
||||
|
@ -49,18 +51,19 @@ func parse(src []byte, filename string, start hcl.Pos) (*File, hcl.Diagnostics)
|
|||
}
|
||||
|
||||
before, root, after := parseBody(file.Body.(*hclsyntax.Body), from)
|
||||
ret := &File{
|
||||
inTree: newInTree(),
|
||||
|
||||
return &File{
|
||||
Name: filename,
|
||||
SrcBytes: src,
|
||||
srcBytes: src,
|
||||
body: root,
|
||||
}
|
||||
|
||||
Body: root,
|
||||
AllTokens: &TokenSeq{
|
||||
before.Seq(),
|
||||
root.AllTokens,
|
||||
after.Seq(),
|
||||
},
|
||||
}, nil
|
||||
nodes := ret.inTree.children
|
||||
nodes.Append(before.Tokens())
|
||||
nodes.AppendNode(root)
|
||||
nodes.Append(after.Tokens())
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
type inputTokens struct {
|
||||
|
@ -76,6 +79,23 @@ func (it inputTokens) Partition(rng hcl.Range) (before, within, after inputToken
|
|||
return
|
||||
}
|
||||
|
||||
func (it inputTokens) PartitionType(ty hclsyntax.TokenType) (before, within, after inputTokens) {
|
||||
for i, t := range it.writerTokens {
|
||||
if t.Type == ty {
|
||||
return it.Slice(0, i), it.Slice(i, i+1), it.Slice(i+1, len(it.nativeTokens))
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("didn't find any token of type %s", ty))
|
||||
}
|
||||
|
||||
func (it inputTokens) PartitionTypeSingle(ty hclsyntax.TokenType) (before inputTokens, found *Token, after inputTokens) {
|
||||
before, within, after := it.PartitionType(ty)
|
||||
if within.Len() != 1 {
|
||||
panic("PartitionType found more than one token")
|
||||
}
|
||||
return before, within.Tokens()[0], after
|
||||
}
|
||||
|
||||
// PartitionIncludeComments is like Partition except the returned "within"
|
||||
// range includes any lead and line comments associated with the range.
|
||||
func (it inputTokens) PartitionIncludingComments(rng hcl.Range) (before, within, after inputTokens) {
|
||||
|
@ -133,8 +153,8 @@ func (it inputTokens) Len() int {
|
|||
return len(it.nativeTokens)
|
||||
}
|
||||
|
||||
func (it inputTokens) Seq() *TokenSeq {
|
||||
return &TokenSeq{it.writerTokens}
|
||||
func (it inputTokens) Tokens() Tokens {
|
||||
return it.writerTokens
|
||||
}
|
||||
|
||||
func (it inputTokens) Types() []hclsyntax.TokenType {
|
||||
|
@ -148,7 +168,7 @@ func (it inputTokens) Types() []hclsyntax.TokenType {
|
|||
// parseBody locates the given body within the given input tokens and returns
|
||||
// the resulting *Body object as well as the tokens that appeared before and
|
||||
// after it.
|
||||
func parseBody(nativeBody *hclsyntax.Body, from inputTokens) (inputTokens, *Body, inputTokens) {
|
||||
func parseBody(nativeBody *hclsyntax.Body, from inputTokens) (inputTokens, *node, inputTokens) {
|
||||
before, within, after := from.PartitionIncludingComments(nativeBody.SrcRange)
|
||||
|
||||
// The main AST doesn't retain the original source ordering of the
|
||||
|
@ -164,7 +184,8 @@ func parseBody(nativeBody *hclsyntax.Body, from inputTokens) (inputTokens, *Body
|
|||
sort.Sort(nativeNodeSorter{nativeItems})
|
||||
|
||||
body := &Body{
|
||||
IndentLevel: 0, // TODO: deal with this
|
||||
inTree: newInTree(),
|
||||
items: newNodeSet(),
|
||||
}
|
||||
|
||||
remain := within
|
||||
|
@ -172,24 +193,24 @@ func parseBody(nativeBody *hclsyntax.Body, from inputTokens) (inputTokens, *Body
|
|||
beforeItem, item, afterItem := parseBodyItem(nativeItem, remain)
|
||||
|
||||
if beforeItem.Len() > 0 {
|
||||
body.AppendUnstructuredTokens(beforeItem.Seq())
|
||||
body.AppendUnstructuredTokens(beforeItem.Tokens())
|
||||
}
|
||||
body.AppendItem(item)
|
||||
body.appendItemNode(item)
|
||||
|
||||
remain = afterItem
|
||||
}
|
||||
|
||||
if remain.Len() > 0 {
|
||||
body.AppendUnstructuredTokens(remain.Seq())
|
||||
body.AppendUnstructuredTokens(remain.Tokens())
|
||||
}
|
||||
|
||||
return before, body, after
|
||||
return before, newNode(body), after
|
||||
}
|
||||
|
||||
func parseBodyItem(nativeItem hclsyntax.Node, from inputTokens) (inputTokens, Node, inputTokens) {
|
||||
func parseBodyItem(nativeItem hclsyntax.Node, from inputTokens) (inputTokens, *node, inputTokens) {
|
||||
before, leadComments, within, lineComments, newline, after := from.PartitionBlockItem(nativeItem.Range())
|
||||
|
||||
var item Node
|
||||
var item *node
|
||||
|
||||
switch tItem := nativeItem.(type) {
|
||||
case *hclsyntax.Attribute:
|
||||
|
@ -204,90 +225,96 @@ func parseBodyItem(nativeItem hclsyntax.Node, from inputTokens) (inputTokens, No
|
|||
return before, item, after
|
||||
}
|
||||
|
||||
func parseAttribute(nativeAttr *hclsyntax.Attribute, from, leadComments, lineComments, newline inputTokens) *Attribute {
|
||||
var allTokens TokenSeq
|
||||
attr := &Attribute{}
|
||||
func parseAttribute(nativeAttr *hclsyntax.Attribute, from, leadComments, lineComments, newline inputTokens) *node {
|
||||
attr := &Attribute{
|
||||
inTree: newInTree(),
|
||||
}
|
||||
children := attr.inTree.children
|
||||
|
||||
if leadComments.Len() > 0 {
|
||||
attr.LeadCommentTokens = leadComments.Seq()
|
||||
allTokens = append(allTokens, attr.LeadCommentTokens)
|
||||
{
|
||||
cn := newNode(newComments(leadComments.Tokens()))
|
||||
attr.leadComments = cn
|
||||
children.AppendNode(cn)
|
||||
}
|
||||
|
||||
before, nameTokens, from := from.Partition(nativeAttr.NameRange)
|
||||
if before.Len() > 0 {
|
||||
allTokens = append(allTokens, before.Seq())
|
||||
{
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
if nameTokens.Len() != 1 {
|
||||
// Should never happen with valid input
|
||||
panic("attribute name is not exactly one token")
|
||||
}
|
||||
token := nameTokens.Tokens()[0]
|
||||
in := newNode(newIdentifier(token))
|
||||
attr.name = in
|
||||
children.AppendNode(in)
|
||||
}
|
||||
attr.NameTokens = nameTokens.Seq()
|
||||
allTokens = append(allTokens, attr.NameTokens)
|
||||
|
||||
before, equalsTokens, from := from.Partition(nativeAttr.EqualsRange)
|
||||
if before.Len() > 0 {
|
||||
allTokens = append(allTokens, before.Seq())
|
||||
}
|
||||
attr.EqualsTokens = equalsTokens.Seq()
|
||||
allTokens = append(allTokens, attr.EqualsTokens)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
children.AppendUnstructuredTokens(equalsTokens.Tokens())
|
||||
|
||||
before, exprTokens, from := from.Partition(nativeAttr.Expr.Range())
|
||||
if before.Len() > 0 {
|
||||
allTokens = append(allTokens, before.Seq())
|
||||
}
|
||||
attr.Expr = parseExpression(nativeAttr.Expr, exprTokens)
|
||||
allTokens = append(allTokens, attr.Expr.AllTokens)
|
||||
|
||||
if lineComments.Len() > 0 {
|
||||
attr.LineCommentTokens = lineComments.Seq()
|
||||
allTokens = append(allTokens, attr.LineCommentTokens)
|
||||
{
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
exprNode := parseExpression(nativeAttr.Expr, exprTokens)
|
||||
attr.expr = exprNode
|
||||
children.AppendNode(exprNode)
|
||||
}
|
||||
|
||||
if newline.Len() > 0 {
|
||||
attr.EOLTokens = newline.Seq()
|
||||
allTokens = append(allTokens, attr.EOLTokens)
|
||||
{
|
||||
cn := newNode(newComments(lineComments.Tokens()))
|
||||
attr.lineComments = cn
|
||||
children.AppendNode(cn)
|
||||
}
|
||||
|
||||
children.AppendUnstructuredTokens(newline.Tokens())
|
||||
|
||||
// Collect any stragglers, though there shouldn't be any
|
||||
if from.Len() > 0 {
|
||||
allTokens = append(allTokens, from.Seq())
|
||||
}
|
||||
children.AppendUnstructuredTokens(from.Tokens())
|
||||
|
||||
attr.AllTokens = &allTokens
|
||||
|
||||
return attr
|
||||
return newNode(attr)
|
||||
}
|
||||
|
||||
func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments, newline inputTokens) *Block {
|
||||
var allTokens TokenSeq
|
||||
block := &Block{}
|
||||
func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments, newline inputTokens) *node {
|
||||
block := &Block{
|
||||
inTree: newInTree(),
|
||||
labels: newNodeSet(),
|
||||
}
|
||||
children := block.inTree.children
|
||||
|
||||
if leadComments.Len() > 0 {
|
||||
block.LeadCommentTokens = leadComments.Seq()
|
||||
allTokens = append(allTokens, block.LeadCommentTokens)
|
||||
{
|
||||
cn := newNode(newComments(leadComments.Tokens()))
|
||||
block.leadComments = cn
|
||||
children.AppendNode(cn)
|
||||
}
|
||||
|
||||
before, typeTokens, from := from.Partition(nativeBlock.TypeRange)
|
||||
if before.Len() > 0 {
|
||||
allTokens = append(allTokens, before.Seq())
|
||||
{
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
if typeTokens.Len() != 1 {
|
||||
// Should never happen with valid input
|
||||
panic("block type name is not exactly one token")
|
||||
}
|
||||
token := typeTokens.Tokens()[0]
|
||||
in := newNode(newIdentifier(token))
|
||||
block.typeName = in
|
||||
children.AppendNode(in)
|
||||
}
|
||||
block.TypeTokens = typeTokens.Seq()
|
||||
allTokens = append(allTokens, block.TypeTokens)
|
||||
|
||||
for _, rng := range nativeBlock.LabelRanges {
|
||||
var labelTokens inputTokens
|
||||
before, labelTokens, from = from.Partition(rng)
|
||||
if before.Len() > 0 {
|
||||
allTokens = append(allTokens, before.Seq())
|
||||
}
|
||||
seq := labelTokens.Seq()
|
||||
block.LabelTokens = append(block.LabelTokens, seq)
|
||||
*(block.LabelTokensFlat) = append(*(block.LabelTokensFlat), seq)
|
||||
allTokens = append(allTokens, seq)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
tokens := labelTokens.Tokens()
|
||||
ln := newNode(newQuoted(tokens))
|
||||
block.labels.Add(ln)
|
||||
children.AppendNode(ln)
|
||||
}
|
||||
|
||||
before, oBrace, from := from.Partition(nativeBlock.OpenBraceRange)
|
||||
if before.Len() > 0 {
|
||||
allTokens = append(allTokens, before.Seq())
|
||||
}
|
||||
block.OBraceTokens = oBrace.Seq()
|
||||
allTokens = append(allTokens, block.OBraceTokens)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
children.AppendUnstructuredTokens(oBrace.Tokens())
|
||||
|
||||
// We go a bit out of order here: we go hunting for the closing brace
|
||||
// so that we have a delimited body, but then we'll deal with the body
|
||||
|
@ -295,42 +322,109 @@ func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments,
|
|||
// that appear after it.
|
||||
bodyTokens, cBrace, from := from.Partition(nativeBlock.CloseBraceRange)
|
||||
before, body, after := parseBody(nativeBlock.Body, bodyTokens)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
block.body = body
|
||||
children.AppendNode(body)
|
||||
children.AppendUnstructuredTokens(after.Tokens())
|
||||
|
||||
if before.Len() > 0 {
|
||||
allTokens = append(allTokens, before.Seq())
|
||||
}
|
||||
block.Body = body
|
||||
allTokens = append(allTokens, body.AllTokens)
|
||||
if after.Len() > 0 {
|
||||
allTokens = append(allTokens, after.Seq())
|
||||
}
|
||||
|
||||
block.CBraceTokens = cBrace.Seq()
|
||||
allTokens = append(allTokens, block.CBraceTokens)
|
||||
children.AppendUnstructuredTokens(cBrace.Tokens())
|
||||
|
||||
// stragglers
|
||||
if after.Len() > 0 {
|
||||
allTokens = append(allTokens, from.Seq())
|
||||
}
|
||||
children.AppendUnstructuredTokens(from.Tokens())
|
||||
if lineComments.Len() > 0 {
|
||||
// blocks don't actually have line comments, so we'll just treat
|
||||
// them as extra stragglers
|
||||
allTokens = append(allTokens, lineComments.Seq())
|
||||
}
|
||||
if newline.Len() > 0 {
|
||||
block.EOLTokens = newline.Seq()
|
||||
allTokens = append(allTokens, block.EOLTokens)
|
||||
children.AppendUnstructuredTokens(lineComments.Tokens())
|
||||
}
|
||||
children.AppendUnstructuredTokens(newline.Tokens())
|
||||
|
||||
block.AllTokens = &allTokens
|
||||
return block
|
||||
return newNode(block)
|
||||
}
|
||||
|
||||
func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *Expression {
|
||||
// TODO: Populate VarRefs by analyzing the result of nativeExpr.Variables()
|
||||
return &Expression{
|
||||
AllTokens: from.Seq(),
|
||||
func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node {
|
||||
expr := newExpression()
|
||||
children := expr.inTree.children
|
||||
|
||||
nativeVars := nativeExpr.Variables()
|
||||
|
||||
for _, nativeTraversal := range nativeVars {
|
||||
before, traversal, after := parseTraversal(nativeTraversal, from)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
children.AppendNode(traversal)
|
||||
expr.absTraversals.Add(traversal)
|
||||
from = after
|
||||
}
|
||||
// Attach any stragglers that don't belong to a traversal to the expression
|
||||
// itself. In an expression with no traversals at all, this is just the
|
||||
// entirety of "from".
|
||||
children.AppendUnstructuredTokens(from.Tokens())
|
||||
|
||||
return newNode(expr)
|
||||
}
|
||||
|
||||
func parseTraversal(nativeTraversal hcl.Traversal, from inputTokens) (before inputTokens, n *node, after inputTokens) {
|
||||
traversal := newTraversal()
|
||||
children := traversal.inTree.children
|
||||
before, from, after = from.Partition(nativeTraversal.SourceRange())
|
||||
|
||||
stepAfter := from
|
||||
for _, nativeStep := range nativeTraversal {
|
||||
before, step, after := parseTraversalStep(nativeStep, stepAfter)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
children.AppendNode(step)
|
||||
stepAfter = after
|
||||
}
|
||||
|
||||
return before, newNode(traversal), after
|
||||
}
|
||||
|
||||
func parseTraversalStep(nativeStep hcl.Traverser, from inputTokens) (before inputTokens, n *node, after inputTokens) {
|
||||
var children *nodes
|
||||
switch tNativeStep := nativeStep.(type) {
|
||||
|
||||
case hcl.TraverseRoot, hcl.TraverseAttr:
|
||||
step := newTraverseName()
|
||||
children = step.inTree.children
|
||||
before, from, after = from.Partition(nativeStep.SourceRange())
|
||||
inBefore, token, inAfter := from.PartitionTypeSingle(hclsyntax.TokenIdent)
|
||||
name := newIdentifier(token)
|
||||
children.AppendUnstructuredTokens(inBefore.Tokens())
|
||||
step.name = children.Append(name)
|
||||
children.AppendUnstructuredTokens(inAfter.Tokens())
|
||||
return before, newNode(step), after
|
||||
|
||||
case hcl.TraverseIndex:
|
||||
step := newTraverseIndex()
|
||||
children = step.inTree.children
|
||||
before, from, after = from.Partition(nativeStep.SourceRange())
|
||||
|
||||
var inBefore, oBrack, keyTokens, cBrack inputTokens
|
||||
inBefore, oBrack, from = from.PartitionType(hclsyntax.TokenOBrack)
|
||||
children.AppendUnstructuredTokens(inBefore.Tokens())
|
||||
children.AppendUnstructuredTokens(oBrack.Tokens())
|
||||
keyTokens, cBrack, from = from.PartitionType(hclsyntax.TokenCBrack)
|
||||
|
||||
keyVal := tNativeStep.Key
|
||||
switch keyVal.Type() {
|
||||
case cty.String:
|
||||
key := newQuoted(keyTokens.Tokens())
|
||||
step.key = children.Append(key)
|
||||
case cty.Number:
|
||||
valBefore, valToken, valAfter := keyTokens.PartitionTypeSingle(hclsyntax.TokenNumberLit)
|
||||
children.AppendUnstructuredTokens(valBefore.Tokens())
|
||||
key := newNumber(valToken)
|
||||
step.key = children.Append(key)
|
||||
children.AppendUnstructuredTokens(valAfter.Tokens())
|
||||
}
|
||||
|
||||
children.AppendUnstructuredTokens(cBrack.Tokens())
|
||||
children.AppendUnstructuredTokens(from.Tokens())
|
||||
|
||||
return before, newNode(step), after
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported traversal step type %T", nativeStep))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// writerTokens takes a sequence of tokens as produced by the main hclsyntax
|
||||
|
|
|
@ -6,6 +6,20 @@ import (
|
|||
"github.com/hashicorp/hcl2/hcl"
|
||||
)
|
||||
|
||||
// NewFile creates a new file object that is empty and ready to have constructs
|
||||
// added t it.
|
||||
func NewFile() *File {
|
||||
body := &Body{
|
||||
inTree: newInTree(),
|
||||
items: newNodeSet(),
|
||||
}
|
||||
file := &File{
|
||||
inTree: newInTree(),
|
||||
}
|
||||
file.body = file.inTree.children.Append(body)
|
||||
return file
|
||||
}
|
||||
|
||||
// ParseConfig interprets the given source bytes into a *hclwrite.File. The
|
||||
// resulting AST can be used to perform surgical edits on the source code
|
||||
// before turning it back into bytes again.
|
||||
|
@ -25,6 +39,6 @@ func Format(src []byte) []byte {
|
|||
tokens := lexConfig(src)
|
||||
format(tokens)
|
||||
buf := &bytes.Buffer{}
|
||||
(&TokenSeq{tokens}).WriteTo(buf)
|
||||
tokens.WriteTo(buf)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
|
|
@ -8,19 +8,6 @@ import (
|
|||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||
)
|
||||
|
||||
// TokenGen is an abstract type that can append tokens to a list. It is the
|
||||
// low-level foundation underlying the hclwrite AST; the AST provides a
|
||||
// convenient abstraction over raw token sequences to facilitate common tasks,
|
||||
// but it's also possible to directly manipulate the tree of token generators
|
||||
// to make changes that the AST API doesn't directly allow.
|
||||
type TokenGen interface {
|
||||
EachToken(TokenCallback)
|
||||
}
|
||||
|
||||
// TokenCallback is used with TokenGen implementations to specify the action
|
||||
// that is to be taken for each token in the flattened token sequence.
|
||||
type TokenCallback func(*Token)
|
||||
|
||||
// Token is a single sequence of bytes annotated with a type. It is similar
|
||||
// in purpose to hclsyntax.Token, but discards the source position information
|
||||
// since that is not useful in code generation.
|
||||
|
@ -38,17 +25,16 @@ type Token struct {
|
|||
// Tokens is a flat list of tokens.
|
||||
type Tokens []*Token
|
||||
|
||||
func (ts Tokens) WriteTo(wr io.Writer) (int, error) {
|
||||
seq := &TokenSeq{ts}
|
||||
return seq.WriteTo(wr)
|
||||
}
|
||||
|
||||
func (ts Tokens) Bytes() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
ts.WriteTo(buf)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (ts Tokens) testValue() string {
|
||||
return string(ts.Bytes())
|
||||
}
|
||||
|
||||
// Columns returns the number of columns (grapheme clusters) the token sequence
|
||||
// occupies. The result is not meaningful if there are newline or single-line
|
||||
// comment tokens in the sequence.
|
||||
|
@ -62,43 +48,10 @@ func (ts Tokens) Columns() int {
|
|||
return ret
|
||||
}
|
||||
|
||||
// TokenSeq combines zero or more TokenGens together to produce a flat sequence
|
||||
// of tokens from a tree of TokenGens.
|
||||
type TokenSeq []TokenGen
|
||||
|
||||
func (t *Token) EachToken(cb TokenCallback) {
|
||||
cb(t)
|
||||
}
|
||||
|
||||
func (ts Tokens) EachToken(cb TokenCallback) {
|
||||
for _, t := range ts {
|
||||
cb(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *TokenSeq) EachToken(cb TokenCallback) {
|
||||
if ts == nil {
|
||||
return
|
||||
}
|
||||
for _, gen := range *ts {
|
||||
gen.EachToken(cb)
|
||||
}
|
||||
}
|
||||
|
||||
// Tokens returns the flat list of tokens represented by the receiving
|
||||
// token sequence.
|
||||
func (ts *TokenSeq) Tokens() Tokens {
|
||||
var tokens Tokens
|
||||
ts.EachToken(func(token *Token) {
|
||||
tokens = append(tokens, token)
|
||||
})
|
||||
return tokens
|
||||
}
|
||||
|
||||
// WriteTo takes an io.Writer and writes the bytes for each token to it,
|
||||
// along with the spacing that separates each token. In other words, this
|
||||
// allows serializing the tokens to a file or other such byte stream.
|
||||
func (ts *TokenSeq) WriteTo(wr io.Writer) (int, error) {
|
||||
func (ts Tokens) WriteTo(wr io.Writer) (int, error) {
|
||||
// We know we're going to be writing a lot of small chunks of repeated
|
||||
// space characters, so we'll prepare a buffer of these that we can
|
||||
// easily pass to wr.Write without any further allocation.
|
||||
|
@ -109,9 +62,9 @@ func (ts *TokenSeq) WriteTo(wr io.Writer) (int, error) {
|
|||
|
||||
var n int
|
||||
var err error
|
||||
ts.EachToken(func(token *Token) {
|
||||
for _, token := range ts {
|
||||
if err != nil {
|
||||
return
|
||||
return n, err
|
||||
}
|
||||
|
||||
for spacesBefore := token.SpacesBefore; spacesBefore > 0; spacesBefore -= len(spaces) {
|
||||
|
@ -123,48 +76,29 @@ func (ts *TokenSeq) WriteTo(wr io.Writer) (int, error) {
|
|||
thisN, err = wr.Write(spaces[:thisChunk])
|
||||
n += thisN
|
||||
if err != nil {
|
||||
return
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
var thisN int
|
||||
thisN, err = wr.Write(token.Bytes)
|
||||
n += thisN
|
||||
})
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// SoloToken returns the single token represented by the receiving sequence,
|
||||
// or nil if the sequence does not represent exactly one token.
|
||||
func (ts *TokenSeq) SoloToken() *Token {
|
||||
var ret *Token
|
||||
found := false
|
||||
ts.EachToken(func(tok *Token) {
|
||||
if ret == nil && !found {
|
||||
ret = tok
|
||||
found = true
|
||||
} else if ret != nil && found {
|
||||
ret = nil
|
||||
}
|
||||
})
|
||||
return ret
|
||||
func (ts Tokens) walkChildNodes(w internalWalkFunc) {
|
||||
// Unstructured tokens have no child nodes
|
||||
}
|
||||
|
||||
// IsIdent returns true if and only if the token sequence represents a single
|
||||
// ident token whose name matches the given string.
|
||||
func (ts *TokenSeq) IsIdent(name []byte) bool {
|
||||
tok := ts.SoloToken()
|
||||
if tok == nil {
|
||||
return false
|
||||
}
|
||||
if tok.Type != hclsyntax.TokenIdent {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(tok.Bytes, name)
|
||||
func (ts Tokens) BuildTokens(to Tokens) Tokens {
|
||||
return append(to, ts...)
|
||||
}
|
||||
|
||||
// TokenSeqEmpty is a TokenSeq that contains no tokens. It can be used anywhere,
|
||||
// but its primary purpose is to be assigned as a replacement for a non-empty
|
||||
// TokenSeq when eliminating a section of an input file.
|
||||
var TokenSeqEmpty = TokenSeq([]TokenGen(nil))
|
||||
func newIdentToken(name string) *Token {
|
||||
return &Token{
|
||||
Type: hclsyntax.TokenIdent,
|
||||
Bytes: []byte(name),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module github.com/mitchellh/go-wordwrap
|
|
@ -0,0 +1,385 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var FormatDateFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "format",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "time",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
formatStr := args[0].AsString()
|
||||
timeStr := args[1].AsString()
|
||||
t, err := parseTimestamp(timeStr)
|
||||
if err != nil {
|
||||
return cty.DynamicVal, function.NewArgError(1, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
sc := bufio.NewScanner(strings.NewReader(formatStr))
|
||||
sc.Split(splitDateFormat)
|
||||
const esc = '\''
|
||||
for sc.Scan() {
|
||||
tok := sc.Bytes()
|
||||
|
||||
// The leading byte signals the token type
|
||||
switch {
|
||||
case tok[0] == esc:
|
||||
if tok[len(tok)-1] != esc || len(tok) == 1 {
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "unterminated literal '")
|
||||
}
|
||||
if len(tok) == 2 {
|
||||
// Must be a single escaped quote, ''
|
||||
buf.WriteByte(esc)
|
||||
} else {
|
||||
// The content (until a closing esc) is printed out verbatim
|
||||
// except that we must un-double any double-esc escapes in
|
||||
// the middle of the string.
|
||||
raw := tok[1 : len(tok)-1]
|
||||
for i := 0; i < len(raw); i++ {
|
||||
buf.WriteByte(raw[i])
|
||||
if raw[i] == esc {
|
||||
i++ // skip the escaped quote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case startsDateFormatVerb(tok[0]):
|
||||
switch tok[0] {
|
||||
case 'Y':
|
||||
y := t.Year()
|
||||
switch len(tok) {
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", y%100)
|
||||
case 4:
|
||||
fmt.Fprintf(&buf, "%04d", y)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: year must either be \"YY\" or \"YYYY\"", tok)
|
||||
}
|
||||
case 'M':
|
||||
m := t.Month()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", m)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", m)
|
||||
case 3:
|
||||
buf.WriteString(m.String()[:3])
|
||||
case 4:
|
||||
buf.WriteString(m.String())
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: month must be \"M\", \"MM\", \"MMM\", or \"MMMM\"", tok)
|
||||
}
|
||||
case 'D':
|
||||
d := t.Day()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", d)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", d)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of month must either be \"D\" or \"DD\"", tok)
|
||||
}
|
||||
case 'E':
|
||||
d := t.Weekday()
|
||||
switch len(tok) {
|
||||
case 3:
|
||||
buf.WriteString(d.String()[:3])
|
||||
case 4:
|
||||
buf.WriteString(d.String())
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of week must either be \"EEE\" or \"EEEE\"", tok)
|
||||
}
|
||||
case 'h':
|
||||
h := t.Hour()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", h)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", h)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 24-hour must either be \"h\" or \"hh\"", tok)
|
||||
}
|
||||
case 'H':
|
||||
h := t.Hour() % 12
|
||||
if h == 0 {
|
||||
h = 12
|
||||
}
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", h)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", h)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 12-hour must either be \"H\" or \"HH\"", tok)
|
||||
}
|
||||
case 'A', 'a':
|
||||
if len(tok) != 2 {
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: must be \"%s%s\"", tok, tok[0:1], tok[0:1])
|
||||
}
|
||||
upper := tok[0] == 'A'
|
||||
switch t.Hour() / 12 {
|
||||
case 0:
|
||||
if upper {
|
||||
buf.WriteString("AM")
|
||||
} else {
|
||||
buf.WriteString("am")
|
||||
}
|
||||
case 1:
|
||||
if upper {
|
||||
buf.WriteString("PM")
|
||||
} else {
|
||||
buf.WriteString("pm")
|
||||
}
|
||||
}
|
||||
case 'm':
|
||||
m := t.Minute()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", m)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", m)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: minute must either be \"m\" or \"mm\"", tok)
|
||||
}
|
||||
case 's':
|
||||
s := t.Second()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", s)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", s)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: second must either be \"s\" or \"ss\"", tok)
|
||||
}
|
||||
case 'Z':
|
||||
// We'll just lean on Go's own formatter for this one, since
|
||||
// the necessary information is unexported.
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
buf.WriteString(t.Format("Z07:00"))
|
||||
case 3:
|
||||
str := t.Format("-0700")
|
||||
switch str {
|
||||
case "+0000":
|
||||
buf.WriteString("UTC")
|
||||
default:
|
||||
buf.WriteString(str)
|
||||
}
|
||||
case 4:
|
||||
buf.WriteString(t.Format("-0700"))
|
||||
case 5:
|
||||
buf.WriteString(t.Format("-07:00"))
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: timezone must be Z, ZZZZ, or ZZZZZ", tok)
|
||||
}
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q", tok)
|
||||
}
|
||||
|
||||
default:
|
||||
// Any other starting character indicates a literal sequence
|
||||
buf.Write(tok)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.StringVal(buf.String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// FormatDate reformats a timestamp given in RFC3339 syntax into another time
|
||||
// syntax defined by a given format string.
|
||||
//
|
||||
// The format string uses letter mnemonics to represent portions of the
|
||||
// timestamp, with repetition signifying length variants of each portion.
|
||||
// Single quote characters ' can be used to quote sequences of literal letters
|
||||
// that should not be interpreted as formatting mnemonics.
|
||||
//
|
||||
// The full set of supported mnemonic sequences is listed below:
|
||||
//
|
||||
// YY Year modulo 100 zero-padded to two digits, like "06".
|
||||
// YYYY Four (or more) digit year, like "2006".
|
||||
// M Month number, like "1" for January.
|
||||
// MM Month number zero-padded to two digits, like "01".
|
||||
// MMM English month name abbreviated to three letters, like "Jan".
|
||||
// MMMM English month name unabbreviated, like "January".
|
||||
// D Day of month number, like "2".
|
||||
// DD Day of month number zero-padded to two digits, like "02".
|
||||
// EEE English day of week name abbreviated to three letters, like "Mon".
|
||||
// EEEE English day of week name unabbreviated, like "Monday".
|
||||
// h 24-hour number, like "2".
|
||||
// hh 24-hour number zero-padded to two digits, like "02".
|
||||
// H 12-hour number, like "2".
|
||||
// HH 12-hour number zero-padded to two digits, like "02".
|
||||
// AA Hour AM/PM marker in uppercase, like "AM".
|
||||
// aa Hour AM/PM marker in lowercase, like "am".
|
||||
// m Minute within hour, like "5".
|
||||
// mm Minute within hour zero-padded to two digits, like "05".
|
||||
// s Second within minute, like "9".
|
||||
// ss Second within minute zero-padded to two digits, like "09".
|
||||
// ZZZZ Timezone offset with just sign and digit, like "-0800".
|
||||
// ZZZZZ Timezone offset with colon separating hours and minutes, like "-08:00".
|
||||
// Z Like ZZZZZ but with a special case "Z" for UTC.
|
||||
// ZZZ Like ZZZZ but with a special case "UTC" for UTC.
|
||||
//
|
||||
// The format syntax is optimized mainly for generating machine-oriented
|
||||
// timestamps rather than human-oriented timestamps; the English language
|
||||
// portions of the output reflect the use of English names in a number of
|
||||
// machine-readable date formatting standards. For presentation to humans,
|
||||
// a locale-aware time formatter (not included in this package) is a better
|
||||
// choice.
|
||||
//
|
||||
// The format syntax is not compatible with that of any other language, but
|
||||
// is optimized so that patterns for common standard date formats can be
|
||||
// recognized quickly even by a reader unfamiliar with the format syntax.
|
||||
func FormatDate(format cty.Value, timestamp cty.Value) (cty.Value, error) {
|
||||
return FormatDateFunc.Call([]cty.Value{format, timestamp})
|
||||
}
|
||||
|
||||
func parseTimestamp(ts string) (time.Time, error) {
|
||||
t, err := time.Parse(time.RFC3339, ts)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *time.ParseError:
|
||||
// If err is s time.ParseError then its string representation is not
|
||||
// appropriate since it relies on details of Go's strange date format
|
||||
// representation, which a caller of our functions is not expected
|
||||
// to be familiar with.
|
||||
//
|
||||
// Therefore we do some light transformation to get a more suitable
|
||||
// error that should make more sense to our callers. These are
|
||||
// still not awesome error messages, but at least they refer to
|
||||
// the timestamp portions by name rather than by Go's example
|
||||
// values.
|
||||
if err.LayoutElem == "" && err.ValueElem == "" && err.Message != "" {
|
||||
// For some reason err.Message is populated with a ": " prefix
|
||||
// by the time package.
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp%s", err.Message)
|
||||
}
|
||||
var what string
|
||||
switch err.LayoutElem {
|
||||
case "2006":
|
||||
what = "year"
|
||||
case "01":
|
||||
what = "month"
|
||||
case "02":
|
||||
what = "day of month"
|
||||
case "15":
|
||||
what = "hour"
|
||||
case "04":
|
||||
what = "minute"
|
||||
case "05":
|
||||
what = "second"
|
||||
case "Z07:00":
|
||||
what = "UTC offset"
|
||||
case "T":
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: missing required time introducer 'T'")
|
||||
case ":", "-":
|
||||
if err.ValueElem == "" {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string where %q is expected", err.LayoutElem)
|
||||
} else {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: found %q where %q is expected", err.ValueElem, err.LayoutElem)
|
||||
}
|
||||
default:
|
||||
// Should never get here, because time.RFC3339 includes only the
|
||||
// above portions, but since that might change in future we'll
|
||||
// be robust here.
|
||||
what = "timestamp segment"
|
||||
}
|
||||
if err.ValueElem == "" {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string before %s", what)
|
||||
} else {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: cannot use %q as %s", err.ValueElem, what)
|
||||
}
|
||||
}
|
||||
return time.Time{}, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// splitDataFormat is a bufio.SplitFunc used to tokenize a date format.
|
||||
func splitDateFormat(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
const esc = '\''
|
||||
|
||||
switch {
|
||||
|
||||
case data[0] == esc:
|
||||
// If we have another quote immediately after then this is a single
|
||||
// escaped escape.
|
||||
if len(data) > 1 && data[1] == esc {
|
||||
return 2, data[:2], nil
|
||||
}
|
||||
|
||||
// Beginning of quoted sequence, so we will seek forward until we find
|
||||
// the closing quote, ignoring escaped quotes along the way.
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc {
|
||||
if (i + 1) == len(data) {
|
||||
// We need at least one more byte to decide if this is an
|
||||
// escape or a terminator.
|
||||
return 0, nil, nil
|
||||
}
|
||||
if data[i+1] == esc {
|
||||
i++ // doubled-up quotes are an escape sequence
|
||||
continue
|
||||
}
|
||||
// We've found the closing quote
|
||||
return i + 1, data[:i+1], nil
|
||||
}
|
||||
}
|
||||
// If we fall out here then we need more bytes to find the end,
|
||||
// unless we're already at the end with an unclosed quote.
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
return 0, nil, nil
|
||||
|
||||
case startsDateFormatVerb(data[0]):
|
||||
rep := data[0]
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] != rep {
|
||||
return i, data[:i], nil
|
||||
}
|
||||
}
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
// We need more data to decide if we've found the end
|
||||
return 0, nil, nil
|
||||
|
||||
default:
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc || startsDateFormatVerb(data[i]) {
|
||||
return i, data[:i], nil
|
||||
}
|
||||
}
|
||||
// We might not actually be at the end of a literal sequence,
|
||||
// but that doesn't matter since we'll concat them back together
|
||||
// anyway.
|
||||
return len(data), data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func startsDateFormatVerb(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
|
||||
}
|
|
@ -138,7 +138,10 @@ func unmarshalList(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value
|
|||
return cty.DynamicVal, path.NewErrorf("a list is required")
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
switch {
|
||||
case length < 0:
|
||||
return cty.NullVal(cty.List(ety)), nil
|
||||
case length == 0:
|
||||
return cty.ListValEmpty(ety), nil
|
||||
}
|
||||
|
||||
|
@ -166,7 +169,10 @@ func unmarshalSet(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value,
|
|||
return cty.DynamicVal, path.NewErrorf("a set is required")
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
switch {
|
||||
case length < 0:
|
||||
return cty.NullVal(cty.Set(ety)), nil
|
||||
case length == 0:
|
||||
return cty.SetValEmpty(ety), nil
|
||||
}
|
||||
|
||||
|
@ -194,7 +200,10 @@ func unmarshalMap(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value,
|
|||
return cty.DynamicVal, path.NewErrorf("a map is required")
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
switch {
|
||||
case length < 0:
|
||||
return cty.NullVal(cty.Map(ety)), nil
|
||||
case length == 0:
|
||||
return cty.MapValEmpty(ety), nil
|
||||
}
|
||||
|
||||
|
@ -227,7 +236,12 @@ func unmarshalTuple(dec *msgpack.Decoder, etys []cty.Type, path cty.Path) (cty.V
|
|||
return cty.DynamicVal, path.NewErrorf("a tuple is required")
|
||||
}
|
||||
|
||||
if length != len(etys) {
|
||||
switch {
|
||||
case length < 0:
|
||||
return cty.NullVal(cty.Tuple(etys)), nil
|
||||
case length == 0:
|
||||
return cty.TupleVal(nil), nil
|
||||
case length != len(etys):
|
||||
return cty.DynamicVal, path.NewErrorf("a tuple of length %d is required", len(etys))
|
||||
}
|
||||
|
||||
|
@ -256,7 +270,12 @@ func unmarshalObject(dec *msgpack.Decoder, atys map[string]cty.Type, path cty.Pa
|
|||
return cty.DynamicVal, path.NewErrorf("an object is required")
|
||||
}
|
||||
|
||||
if length != len(atys) {
|
||||
switch {
|
||||
case length < 0:
|
||||
return cty.NullVal(cty.Object(atys)), nil
|
||||
case length == 0:
|
||||
return cty.ObjectVal(nil), nil
|
||||
case length != len(atys):
|
||||
return cty.DynamicVal, path.NewErrorf("an object with %d attributes is required", len(atys))
|
||||
}
|
||||
|
||||
|
@ -293,7 +312,10 @@ func unmarshalDynamic(dec *msgpack.Decoder, path cty.Path) (cty.Value, error) {
|
|||
return cty.DynamicVal, path.NewError(err)
|
||||
}
|
||||
|
||||
if length != 2 {
|
||||
switch {
|
||||
case length == -1:
|
||||
return cty.NullVal(cty.DynamicPseudoType), nil
|
||||
case length != 2:
|
||||
return cty.DynamicVal, path.NewErrorf(
|
||||
"dynamic value array must have exactly two elements",
|
||||
)
|
||||
|
|
|
@ -14,16 +14,15 @@ func (val Value) GoString() string {
|
|||
return "cty.NilVal"
|
||||
}
|
||||
|
||||
if val.ty == DynamicPseudoType {
|
||||
return "cty.DynamicVal"
|
||||
}
|
||||
|
||||
if !val.IsKnown() {
|
||||
return fmt.Sprintf("cty.UnknownVal(%#v)", val.ty)
|
||||
}
|
||||
if val.IsNull() {
|
||||
return fmt.Sprintf("cty.NullVal(%#v)", val.ty)
|
||||
}
|
||||
if val == DynamicVal { // is unknown, so must be before the IsKnown check below
|
||||
return "cty.DynamicVal"
|
||||
}
|
||||
if !val.IsKnown() {
|
||||
return fmt.Sprintf("cty.UnknownVal(%#v)", val.ty)
|
||||
}
|
||||
|
||||
// By the time we reach here we've dealt with all of the exceptions around
|
||||
// unknowns and nulls, so we're guaranteed that the values are the
|
||||
|
|
|
@ -333,7 +333,6 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) {
|
|||
return nil, errors.StructuralError("primary key cannot be used for signatures")
|
||||
}
|
||||
|
||||
var current *Identity
|
||||
var revocations []*packet.Signature
|
||||
EachPacket:
|
||||
for {
|
||||
|
@ -349,7 +348,7 @@ EachPacket:
|
|||
// Make a new Identity object, that we might wind up throwing away.
|
||||
// We'll only add it if we get a valid self-signature over this
|
||||
// userID.
|
||||
current = new(Identity)
|
||||
current := new(Identity)
|
||||
current.Name = pkt.Id
|
||||
current.UserId = pkt
|
||||
|
||||
|
@ -384,11 +383,9 @@ EachPacket:
|
|||
// TODO: RFC4880 5.2.1 permits signatures
|
||||
// directly on keys (eg. to bind additional
|
||||
// revocation keys).
|
||||
} else if current == nil {
|
||||
return nil, errors.StructuralError("signature packet found before user id packet")
|
||||
} else {
|
||||
current.Signatures = append(current.Signatures, pkt)
|
||||
}
|
||||
// Else, ignoring the signature as it does not follow anything
|
||||
// we would know to attach it to.
|
||||
case *packet.PrivateKey:
|
||||
if pkt.IsSubkey == false {
|
||||
packets.Unread(p)
|
||||
|
@ -433,26 +430,45 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
|
|||
var subKey Subkey
|
||||
subKey.PublicKey = pub
|
||||
subKey.PrivateKey = priv
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
packets.Unread(p)
|
||||
break
|
||||
}
|
||||
|
||||
if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation {
|
||||
return errors.StructuralError("subkey signature with wrong type")
|
||||
}
|
||||
|
||||
if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
|
||||
switch sig.SigType {
|
||||
case packet.SigTypeSubkeyRevocation:
|
||||
subKey.Sig = sig
|
||||
case packet.SigTypeSubkeyBinding:
|
||||
if subKey.Sig == nil {
|
||||
subKey.Sig = sig
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
var ok bool
|
||||
subKey.Sig, ok = p.(*packet.Signature)
|
||||
if !ok {
|
||||
|
||||
if subKey.Sig == nil {
|
||||
return errors.StructuralError("subkey packet not followed by signature")
|
||||
}
|
||||
if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation {
|
||||
return errors.StructuralError("subkey signature with wrong type")
|
||||
}
|
||||
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)
|
||||
if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
|
||||
e.Subkeys = append(e.Subkeys, subKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -903,8 +903,8 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
|||
// Implemented based on the documentation at
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||
func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||
magic := append([]byte("openssh-key-v1"), 0)
|
||||
if !bytes.Equal(magic, key[0:len(magic)]) {
|
||||
const magic = "openssh-key-v1\x00"
|
||||
if len(key) < len(magic) || string(key[:len(magic)]) != magic {
|
||||
return nil, errors.New("ssh: invalid openssh private key format")
|
||||
}
|
||||
remaining := key[len(magic):]
|
||||
|
|
|
@ -97,8 +97,16 @@ func isSpecialElement(element *Node) bool {
|
|||
switch element.Namespace {
|
||||
case "", "html":
|
||||
return isSpecialElementMap[element.Data]
|
||||
case "math":
|
||||
switch element.Data {
|
||||
case "mi", "mo", "mn", "ms", "mtext", "annotation-xml":
|
||||
return true
|
||||
}
|
||||
case "svg":
|
||||
return element.Data == "foreignObject"
|
||||
switch element.Data {
|
||||
case "foreignObject", "desc", "title":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -470,6 +470,10 @@ func (p *parser) resetInsertionMode() {
|
|||
case a.Table:
|
||||
p.im = inTableIM
|
||||
case a.Template:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
if n.Namespace != "" {
|
||||
continue
|
||||
}
|
||||
p.im = p.templateStack.top()
|
||||
case a.Head:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
|
@ -984,6 +988,14 @@ func inBodyIM(p *parser) bool {
|
|||
p.acknowledgeSelfClosingTag()
|
||||
p.popUntil(buttonScope, a.P)
|
||||
p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())
|
||||
if p.form == nil {
|
||||
// NOTE: The 'isindex' element has been removed,
|
||||
// and the 'template' element has not been designed to be
|
||||
// collaborative with the index element.
|
||||
//
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
if action != "" {
|
||||
p.form.Attr = []Attribute{{Key: "action", Val: action}}
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
_obj/
|
||||
unix.test
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// CPU affinity functions
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
||||
|
||||
// CPUSet represents a CPU affinity mask.
|
||||
type CPUSet [cpuSetSize]cpuMask
|
||||
|
||||
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
||||
if e != 0 {
|
||||
return errnoErr(e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
func SchedGetaffinity(pid int, set *CPUSet) error {
|
||||
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
|
||||
}
|
||||
|
||||
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
func SchedSetaffinity(pid int, set *CPUSet) error {
|
||||
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
|
||||
}
|
||||
|
||||
// Zero clears the set s, so that it contains no CPUs.
|
||||
func (s *CPUSet) Zero() {
|
||||
for i := range s {
|
||||
s[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func cpuBitsIndex(cpu int) int {
|
||||
return cpu / _NCPUBITS
|
||||
}
|
||||
|
||||
func cpuBitsMask(cpu int) cpuMask {
|
||||
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
|
||||
}
|
||||
|
||||
// Set adds cpu to the set s.
|
||||
func (s *CPUSet) Set(cpu int) {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
s[i] |= cpuBitsMask(cpu)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes cpu from the set s.
|
||||
func (s *CPUSet) Clear(cpu int) {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
s[i] &^= cpuBitsMask(cpu)
|
||||
}
|
||||
}
|
||||
|
||||
// IsSet reports whether cpu is in the set s.
|
||||
func (s *CPUSet) IsSet(cpu int) bool {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
return s[i]&cpuBitsMask(cpu) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Count returns the number of CPUs in the set s.
|
||||
func (s *CPUSet) Count() int {
|
||||
c := 0
|
||||
for _, b := range s {
|
||||
c += onesCount64(uint64(b))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// onesCount64 is a copy of Go 1.9's math/bits.OnesCount64.
|
||||
// Once this package can require Go 1.9, we can delete this
|
||||
// and update the caller to use bits.OnesCount64.
|
||||
func onesCount64(x uint64) int {
|
||||
const m0 = 0x5555555555555555 // 01010101 ...
|
||||
const m1 = 0x3333333333333333 // 00110011 ...
|
||||
const m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ...
|
||||
const m3 = 0x00ff00ff00ff00ff // etc.
|
||||
const m4 = 0x0000ffff0000ffff
|
||||
|
||||
// Implementation: Parallel summing of adjacent bits.
|
||||
// See "Hacker's Delight", Chap. 5: Counting Bits.
|
||||
// The following pattern shows the general approach:
|
||||
//
|
||||
// x = x>>1&(m0&m) + x&(m0&m)
|
||||
// x = x>>2&(m1&m) + x&(m1&m)
|
||||
// x = x>>4&(m2&m) + x&(m2&m)
|
||||
// x = x>>8&(m3&m) + x&(m3&m)
|
||||
// x = x>>16&(m4&m) + x&(m4&m)
|
||||
// x = x>>32&(m5&m) + x&(m5&m)
|
||||
// return int(x)
|
||||
//
|
||||
// Masking (& operations) can be left away when there's no
|
||||
// danger that a field's sum will carry over into the next
|
||||
// field: Since the result cannot be > 64, 8 bits is enough
|
||||
// and we can ignore the masks for the shifts by 8 and up.
|
||||
// Per "Hacker's Delight", the first line can be simplified
|
||||
// more, but it saves at best one instruction, so we leave
|
||||
// it alone for clarity.
|
||||
const m = 1<<64 - 1
|
||||
x = x>>1&(m0&m) + x&(m0&m)
|
||||
x = x>>2&(m1&m) + x&(m1&m)
|
||||
x = (x>>4 + x) & (m2 & m)
|
||||
x += x >> 8
|
||||
x += x >> 16
|
||||
x += x >> 32
|
||||
return int(x) & (1<<7 - 1)
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
// +build go1.9
|
||||
|
||||
package unix
|
||||
|
||||
import "syscall"
|
||||
|
||||
type Signal = syscall.Signal
|
||||
type Errno = syscall.Errno
|
||||
type SysProcAttr = syscall.SysProcAttr
|
|
@ -13,17 +13,17 @@
|
|||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-64
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-88
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-112
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-64
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
|
|
@ -10,21 +10,51 @@
|
|||
// System calls for 386, Linux
|
||||
//
|
||||
|
||||
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
|
||||
// instead of the glibc-specific "CALL 0x10(GS)".
|
||||
#define INVOKE_SYSCALL INT $0x80
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
MOVL a1+4(FP), BX
|
||||
MOVL a2+8(FP), CX
|
||||
MOVL a3+12(FP), DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
INVOKE_SYSCALL
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL DX, r2+20(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
MOVL a1+4(FP), BX
|
||||
MOVL a2+8(FP), CX
|
||||
MOVL a3+12(FP), DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
INVOKE_SYSCALL
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL DX, r2+20(FP)
|
||||
RET
|
||||
|
||||
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
||||
JMP syscall·socketcall(SB)
|
||||
|
||||
|
|
|
@ -13,17 +13,45 @@
|
|||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ $0, R10
|
||||
MOVQ $0, R8
|
||||
MOVQ $0, R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
SYSCALL
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ DX, r2+40(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ $0, R10
|
||||
MOVQ $0, R8
|
||||
MOVQ $0, R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
SYSCALL
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ DX, r2+40(FP)
|
||||
RET
|
||||
|
||||
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
|
||||
JMP syscall·gettimeofday(SB)
|
||||
|
|
|
@ -13,17 +13,44 @@
|
|||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW trap+0(FP), R7
|
||||
MOVW a1+4(FP), R0
|
||||
MOVW a2+8(FP), R1
|
||||
MOVW a3+12(FP), R2
|
||||
MOVW $0, R3
|
||||
MOVW $0, R4
|
||||
MOVW $0, R5
|
||||
SWI $0
|
||||
MOVW R0, r1+16(FP)
|
||||
MOVW $0, R0
|
||||
MOVW R0, r2+20(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-32
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVW trap+0(FP), R7 // syscall entry
|
||||
MOVW a1+4(FP), R0
|
||||
MOVW a2+8(FP), R1
|
||||
MOVW a3+12(FP), R2
|
||||
SWI $0
|
||||
MOVW R0, r1+16(FP)
|
||||
MOVW $0, R0
|
||||
MOVW R0, r2+20(FP)
|
||||
RET
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||
B syscall·seek(SB)
|
||||
|
|
|
@ -11,14 +11,42 @@
|
|||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD $0, R3
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
SVC
|
||||
MOVD R0, r1+32(FP) // r1
|
||||
MOVD R1, r2+40(FP) // r2
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
B syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD $0, R3
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
SVC
|
||||
MOVD R0, r1+32(FP)
|
||||
MOVD R1, r2+40(FP)
|
||||
RET
|
||||
|
|
|
@ -15,14 +15,42 @@
|
|||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R2, r1+32(FP)
|
||||
MOVV R3, r2+40(FP)
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R2, r1+32(FP)
|
||||
MOVV R3, r2+40(FP)
|
||||
RET
|
||||
|
|
|
@ -15,17 +15,40 @@
|
|||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVW a1+4(FP), R4
|
||||
MOVW a2+8(FP), R5
|
||||
MOVW a3+12(FP), R6
|
||||
MOVW R0, R7
|
||||
MOVW trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVW R2, r1+16(FP) // r1
|
||||
MOVW R3, r2+20(FP) // r2
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVW a1+4(FP), R4
|
||||
MOVW a2+8(FP), R5
|
||||
MOVW a3+12(FP), R6
|
||||
MOVW trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
MOVW R2, r1+16(FP)
|
||||
MOVW R3, r2+20(FP)
|
||||
RET
|
||||
|
|
|
@ -15,14 +15,42 @@
|
|||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
BR syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
BR syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVD a1+8(FP), R3
|
||||
MOVD a2+16(FP), R4
|
||||
MOVD a3+24(FP), R5
|
||||
MOVD R0, R6
|
||||
MOVD R0, R7
|
||||
MOVD R0, R8
|
||||
MOVD trap+0(FP), R9 // syscall entry
|
||||
SYSCALL R9
|
||||
MOVD R3, r1+32(FP)
|
||||
MOVD R4, r2+40(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
BR syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
BR syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R3
|
||||
MOVD a2+16(FP), R4
|
||||
MOVD a3+24(FP), R5
|
||||
MOVD R0, R6
|
||||
MOVD R0, R7
|
||||
MOVD R0, R8
|
||||
MOVD trap+0(FP), R9 // syscall entry
|
||||
SYSCALL R9
|
||||
MOVD R3, r1+32(FP)
|
||||
MOVD R4, r2+40(FP)
|
||||
RET
|
||||
|
|
|
@ -21,8 +21,36 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
BR syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVD a1+8(FP), R2
|
||||
MOVD a2+16(FP), R3
|
||||
MOVD a3+24(FP), R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
MOVD trap+0(FP), R1 // syscall entry
|
||||
SYSCALL
|
||||
MOVD R2, r1+32(FP)
|
||||
MOVD R3, r2+40(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
BR syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
BR syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R2
|
||||
MOVD a2+16(FP), R3
|
||||
MOVD a3+24(FP), R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
MOVD trap+0(FP), R1 // syscall entry
|
||||
SYSCALL
|
||||
MOVD R2, r1+32(FP)
|
||||
MOVD R3, r2+40(FP)
|
||||
RET
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, OpenBSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
B syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
B syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
B syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
B syscall·RawSyscall6(SB)
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build freebsd
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Go implementation of C mostly found in /usr/src/sys/kern/subr_capability.c
|
||||
|
||||
const (
|
||||
// This is the version of CapRights this package understands. See C implementation for parallels.
|
||||
capRightsGoVersion = CAP_RIGHTS_VERSION_00
|
||||
capArSizeMin = CAP_RIGHTS_VERSION_00 + 2
|
||||
capArSizeMax = capRightsGoVersion + 2
|
||||
)
|
||||
|
||||
var (
|
||||
bit2idx = []int{
|
||||
-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,
|
||||
4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
}
|
||||
)
|
||||
|
||||
func capidxbit(right uint64) int {
|
||||
return int((right >> 57) & 0x1f)
|
||||
}
|
||||
|
||||
func rightToIndex(right uint64) (int, error) {
|
||||
idx := capidxbit(right)
|
||||
if idx < 0 || idx >= len(bit2idx) {
|
||||
return -2, fmt.Errorf("index for right 0x%x out of range", right)
|
||||
}
|
||||
return bit2idx[idx], nil
|
||||
}
|
||||
|
||||
func caprver(right uint64) int {
|
||||
return int(right >> 62)
|
||||
}
|
||||
|
||||
func capver(rights *CapRights) int {
|
||||
return caprver(rights.Rights[0])
|
||||
}
|
||||
|
||||
func caparsize(rights *CapRights) int {
|
||||
return capver(rights) + 2
|
||||
}
|
||||
|
||||
// CapRightsSet sets the permissions in setrights in rights.
|
||||
func CapRightsSet(rights *CapRights, setrights []uint64) error {
|
||||
// This is essentially a copy of cap_rights_vset()
|
||||
if capver(rights) != CAP_RIGHTS_VERSION_00 {
|
||||
return fmt.Errorf("bad rights version %d", capver(rights))
|
||||
}
|
||||
|
||||
n := caparsize(rights)
|
||||
if n < capArSizeMin || n > capArSizeMax {
|
||||
return errors.New("bad rights size")
|
||||
}
|
||||
|
||||
for _, right := range setrights {
|
||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||
return errors.New("bad right version")
|
||||
}
|
||||
i, err := rightToIndex(right)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i >= n {
|
||||
return errors.New("index overflow")
|
||||
}
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch")
|
||||
}
|
||||
rights.Rights[i] |= right
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch (after assign)")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CapRightsClear clears the permissions in clearrights from rights.
|
||||
func CapRightsClear(rights *CapRights, clearrights []uint64) error {
|
||||
// This is essentially a copy of cap_rights_vclear()
|
||||
if capver(rights) != CAP_RIGHTS_VERSION_00 {
|
||||
return fmt.Errorf("bad rights version %d", capver(rights))
|
||||
}
|
||||
|
||||
n := caparsize(rights)
|
||||
if n < capArSizeMin || n > capArSizeMax {
|
||||
return errors.New("bad rights size")
|
||||
}
|
||||
|
||||
for _, right := range clearrights {
|
||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||
return errors.New("bad right version")
|
||||
}
|
||||
i, err := rightToIndex(right)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i >= n {
|
||||
return errors.New("index overflow")
|
||||
}
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch")
|
||||
}
|
||||
rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF)
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return errors.New("index mismatch (after assign)")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CapRightsIsSet checks whether all the permissions in setrights are present in rights.
|
||||
func CapRightsIsSet(rights *CapRights, setrights []uint64) (bool, error) {
|
||||
// This is essentially a copy of cap_rights_is_vset()
|
||||
if capver(rights) != CAP_RIGHTS_VERSION_00 {
|
||||
return false, fmt.Errorf("bad rights version %d", capver(rights))
|
||||
}
|
||||
|
||||
n := caparsize(rights)
|
||||
if n < capArSizeMin || n > capArSizeMax {
|
||||
return false, errors.New("bad rights size")
|
||||
}
|
||||
|
||||
for _, right := range setrights {
|
||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||
return false, errors.New("bad right version")
|
||||
}
|
||||
i, err := rightToIndex(right)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if i >= n {
|
||||
return false, errors.New("index overflow")
|
||||
}
|
||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||
return false, errors.New("index mismatch")
|
||||
}
|
||||
if (rights.Rights[i] & right) != right {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func capright(idx uint64, bit uint64) uint64 {
|
||||
return ((1 << (57 + idx)) | bit)
|
||||
}
|
||||
|
||||
// CapRightsInit returns a pointer to an initialised CapRights structure filled with rights.
|
||||
// See man cap_rights_init(3) and rights(4).
|
||||
func CapRightsInit(rights []uint64) (*CapRights, error) {
|
||||
var r CapRights
|
||||
r.Rights[0] = (capRightsGoVersion << 62) | capright(0, 0)
|
||||
r.Rights[1] = capright(1, 0)
|
||||
|
||||
err := CapRightsSet(&r, rights)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// CapRightsLimit reduces the operations permitted on fd to at most those contained in rights.
|
||||
// The capability rights on fd can never be increased by CapRightsLimit.
|
||||
// See man cap_rights_limit(2) and rights(4).
|
||||
func CapRightsLimit(fd uintptr, rights *CapRights) error {
|
||||
return capRightsLimit(int(fd), rights)
|
||||
}
|
||||
|
||||
// CapRightsGet returns a CapRights structure containing the operations permitted on fd.
|
||||
// See man cap_rights_get(3) and rights(4).
|
||||
func CapRightsGet(fd uintptr) (*CapRights, error) {
|
||||
r, err := CapRightsInit(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = capRightsGet(capRightsGoVersion, int(fd), r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix
|
||||
// +build ppc
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used by AIX.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of a Linux device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
return uint32((dev >> 16) & 0xffff)
|
||||
}
|
||||
|
||||
// Minor returns the minor component of a Linux device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
return uint32(dev & 0xffff)
|
||||
}
|
||||
|
||||
// Mkdev returns a Linux device number generated from the given major and minor
|
||||
// components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
return uint64(((major) << 16) | (minor))
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix
|
||||
// +build ppc64
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used AIX.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of a Linux device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
return uint32((dev & 0x3fffffff00000000) >> 32)
|
||||
}
|
||||
|
||||
// Minor returns the minor component of a Linux device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
return uint32((dev & 0x00000000ffffffff) >> 0)
|
||||
}
|
||||
|
||||
// Mkdev returns a Linux device number generated from the given major and minor
|
||||
// components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
var DEVNO64 uint64
|
||||
DEVNO64 = 0x8000000000000000
|
||||
return ((uint64(major) << 32) | (uint64(minor) & 0x00000000FFFFFFFF) | DEVNO64)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used in Darwin's sys/types.h header.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of a Darwin device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
return uint32((dev >> 24) & 0xff)
|
||||
}
|
||||
|
||||
// Minor returns the minor component of a Darwin device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
return uint32(dev & 0xffffff)
|
||||
}
|
||||
|
||||
// Mkdev returns a Darwin device number generated from the given major and minor
|
||||
// components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
return (uint64(major) << 24) | uint64(minor)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used in Dragonfly's sys/types.h header.
|
||||
//
|
||||
// The information below is extracted and adapted from sys/types.h:
|
||||
//
|
||||
// Minor gives a cookie instead of an index since in order to avoid changing the
|
||||
// meanings of bits 0-15 or wasting time and space shifting bits 16-31 for
|
||||
// devices that don't use them.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of a DragonFlyBSD device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
return uint32((dev >> 8) & 0xff)
|
||||
}
|
||||
|
||||
// Minor returns the minor component of a DragonFlyBSD device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
return uint32(dev & 0xffff00ff)
|
||||
}
|
||||
|
||||
// Mkdev returns a DragonFlyBSD device number generated from the given major and
|
||||
// minor components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
return (uint64(major) << 8) | uint64(minor)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used in FreeBSD's sys/types.h header.
|
||||
//
|
||||
// The information below is extracted and adapted from sys/types.h:
|
||||
//
|
||||
// Minor gives a cookie instead of an index since in order to avoid changing the
|
||||
// meanings of bits 0-15 or wasting time and space shifting bits 16-31 for
|
||||
// devices that don't use them.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of a FreeBSD device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
return uint32((dev >> 8) & 0xff)
|
||||
}
|
||||
|
||||
// Minor returns the minor component of a FreeBSD device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
return uint32(dev & 0xffff00ff)
|
||||
}
|
||||
|
||||
// Mkdev returns a FreeBSD device number generated from the given major and
|
||||
// minor components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
return (uint64(major) << 8) | uint64(minor)
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used by the Linux kernel and glibc.
|
||||
//
|
||||
// The information below is extracted and adapted from bits/sysmacros.h in the
|
||||
// glibc sources:
|
||||
//
|
||||
// dev_t in glibc is 64-bit, with 32-bit major and minor numbers. glibc's
|
||||
// default encoding is MMMM Mmmm mmmM MMmm, where M is a hex digit of the major
|
||||
// number and m is a hex digit of the minor number. This is backward compatible
|
||||
// with legacy systems where dev_t is 16 bits wide, encoded as MMmm. It is also
|
||||
// backward compatible with the Linux kernel, which for some architectures uses
|
||||
// 32-bit dev_t, encoded as mmmM MMmm.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of a Linux device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
major := uint32((dev & 0x00000000000fff00) >> 8)
|
||||
major |= uint32((dev & 0xfffff00000000000) >> 32)
|
||||
return major
|
||||
}
|
||||
|
||||
// Minor returns the minor component of a Linux device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
minor := uint32((dev & 0x00000000000000ff) >> 0)
|
||||
minor |= uint32((dev & 0x00000ffffff00000) >> 12)
|
||||
return minor
|
||||
}
|
||||
|
||||
// Mkdev returns a Linux device number generated from the given major and minor
|
||||
// components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
dev := (uint64(major) & 0x00000fff) << 8
|
||||
dev |= (uint64(major) & 0xfffff000) << 32
|
||||
dev |= (uint64(minor) & 0x000000ff) << 0
|
||||
dev |= (uint64(minor) & 0xffffff00) << 12
|
||||
return dev
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used in NetBSD's sys/types.h header.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of a NetBSD device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
return uint32((dev & 0x000fff00) >> 8)
|
||||
}
|
||||
|
||||
// Minor returns the minor component of a NetBSD device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
minor := uint32((dev & 0x000000ff) >> 0)
|
||||
minor |= uint32((dev & 0xfff00000) >> 12)
|
||||
return minor
|
||||
}
|
||||
|
||||
// Mkdev returns a NetBSD device number generated from the given major and minor
|
||||
// components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
dev := (uint64(major) << 8) & 0x000fff00
|
||||
dev |= (uint64(minor) << 12) & 0xfff00000
|
||||
dev |= (uint64(minor) << 0) & 0x000000ff
|
||||
return dev
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Functions to access/create device major and minor numbers matching the
|
||||
// encoding used in OpenBSD's sys/types.h header.
|
||||
|
||||
package unix
|
||||
|
||||
// Major returns the major component of an OpenBSD device number.
|
||||
func Major(dev uint64) uint32 {
|
||||
return uint32((dev & 0x0000ff00) >> 8)
|
||||
}
|
||||
|
||||
// Minor returns the minor component of an OpenBSD device number.
|
||||
func Minor(dev uint64) uint32 {
|
||||
minor := uint32((dev & 0x000000ff) >> 0)
|
||||
minor |= uint32((dev & 0xffff0000) >> 8)
|
||||
return minor
|
||||
}
|
||||
|
||||
// Mkdev returns an OpenBSD device number generated from the given major and minor
|
||||
// components.
|
||||
func Mkdev(major, minor uint32) uint64 {
|
||||
dev := (uint64(major) << 8) & 0x0000ff00
|
||||
dev |= (uint64(minor) << 8) & 0xffff0000
|
||||
dev |= (uint64(minor) << 0) & 0x000000ff
|
||||
return dev
|
||||
}
|
|
@ -2,101 +2,16 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
||||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
|
||||
if len(b) < int(off+size) {
|
||||
return 0, false
|
||||
}
|
||||
if isBigEndian {
|
||||
return readIntBE(b[off:], size), true
|
||||
}
|
||||
return readIntLE(b[off:], size), true
|
||||
}
|
||||
|
||||
func readIntBE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[1]) | uint64(b[0])<<8
|
||||
case 4:
|
||||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
|
||||
case 8:
|
||||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
}
|
||||
|
||||
func readIntLE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8
|
||||
case 4:
|
||||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
|
||||
case 8:
|
||||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
}
|
||||
import "syscall"
|
||||
|
||||
// ParseDirent parses up to max directory entries in buf,
|
||||
// appending the names to names. It returns the number of
|
||||
// bytes consumed from buf, the number of entries added
|
||||
// to names, and the new names slice.
|
||||
func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
|
||||
origlen := len(buf)
|
||||
count = 0
|
||||
for max != 0 && len(buf) > 0 {
|
||||
reclen, ok := direntReclen(buf)
|
||||
if !ok || reclen > uint64(len(buf)) {
|
||||
return origlen, count, names
|
||||
}
|
||||
rec := buf[:reclen]
|
||||
buf = buf[reclen:]
|
||||
ino, ok := direntIno(rec)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if ino == 0 { // File absent in directory.
|
||||
continue
|
||||
}
|
||||
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
|
||||
namlen, ok := direntNamlen(rec)
|
||||
if !ok || namoff+namlen > uint64(len(rec)) {
|
||||
break
|
||||
}
|
||||
name := rec[namoff : namoff+namlen]
|
||||
for i, c := range name {
|
||||
if c == 0 {
|
||||
name = name[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check for useless names before allocating a string.
|
||||
if string(name) == "." || string(name) == ".." {
|
||||
continue
|
||||
}
|
||||
max--
|
||||
count++
|
||||
names = append(names, string(name))
|
||||
}
|
||||
return origlen - len(buf), count, names
|
||||
return syscall.ParseDirent(buf, max, names)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
// Unix environment variables.
|
||||
|
||||
|
@ -25,3 +25,7 @@ func Clearenv() {
|
|||
func Environ() []string {
|
||||
return syscall.Environ()
|
||||
}
|
||||
|
||||
func Unsetenv(key string) error {
|
||||
return syscall.Unsetenv(key)
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.4
|
||||
|
||||
package unix
|
||||
|
||||
import "syscall"
|
||||
|
||||
func Unsetenv(key string) error {
|
||||
// This was added in Go 1.4.
|
||||
return syscall.Unsetenv(key)
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep
|
||||
// them here for backwards compatibility.
|
||||
|
||||
package unix
|
||||
|
||||
const (
|
||||
IFF_SMART = 0x20
|
||||
IFT_1822 = 0x2
|
||||
IFT_A12MPPSWITCH = 0x82
|
||||
IFT_AAL2 = 0xbb
|
||||
IFT_AAL5 = 0x31
|
||||
IFT_ADSL = 0x5e
|
||||
IFT_AFLANE8023 = 0x3b
|
||||
IFT_AFLANE8025 = 0x3c
|
||||
IFT_ARAP = 0x58
|
||||
IFT_ARCNET = 0x23
|
||||
IFT_ARCNETPLUS = 0x24
|
||||
IFT_ASYNC = 0x54
|
||||
IFT_ATM = 0x25
|
||||
IFT_ATMDXI = 0x69
|
||||
IFT_ATMFUNI = 0x6a
|
||||
IFT_ATMIMA = 0x6b
|
||||
IFT_ATMLOGICAL = 0x50
|
||||
IFT_ATMRADIO = 0xbd
|
||||
IFT_ATMSUBINTERFACE = 0x86
|
||||
IFT_ATMVCIENDPT = 0xc2
|
||||
IFT_ATMVIRTUAL = 0x95
|
||||
IFT_BGPPOLICYACCOUNTING = 0xa2
|
||||
IFT_BSC = 0x53
|
||||
IFT_CCTEMUL = 0x3d
|
||||
IFT_CEPT = 0x13
|
||||
IFT_CES = 0x85
|
||||
IFT_CHANNEL = 0x46
|
||||
IFT_CNR = 0x55
|
||||
IFT_COFFEE = 0x84
|
||||
IFT_COMPOSITELINK = 0x9b
|
||||
IFT_DCN = 0x8d
|
||||
IFT_DIGITALPOWERLINE = 0x8a
|
||||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
|
||||
IFT_DLSW = 0x4a
|
||||
IFT_DOCSCABLEDOWNSTREAM = 0x80
|
||||
IFT_DOCSCABLEMACLAYER = 0x7f
|
||||
IFT_DOCSCABLEUPSTREAM = 0x81
|
||||
IFT_DS0 = 0x51
|
||||
IFT_DS0BUNDLE = 0x52
|
||||
IFT_DS1FDL = 0xaa
|
||||
IFT_DS3 = 0x1e
|
||||
IFT_DTM = 0x8c
|
||||
IFT_DVBASILN = 0xac
|
||||
IFT_DVBASIOUT = 0xad
|
||||
IFT_DVBRCCDOWNSTREAM = 0x93
|
||||
IFT_DVBRCCMACLAYER = 0x92
|
||||
IFT_DVBRCCUPSTREAM = 0x94
|
||||
IFT_ENC = 0xf4
|
||||
IFT_EON = 0x19
|
||||
IFT_EPLRS = 0x57
|
||||
IFT_ESCON = 0x49
|
||||
IFT_ETHER = 0x6
|
||||
IFT_FAITH = 0xf2
|
||||
IFT_FAST = 0x7d
|
||||
IFT_FASTETHER = 0x3e
|
||||
IFT_FASTETHERFX = 0x45
|
||||
IFT_FDDI = 0xf
|
||||
IFT_FIBRECHANNEL = 0x38
|
||||
IFT_FRAMERELAYINTERCONNECT = 0x3a
|
||||
IFT_FRAMERELAYMPI = 0x5c
|
||||
IFT_FRDLCIENDPT = 0xc1
|
||||
IFT_FRELAY = 0x20
|
||||
IFT_FRELAYDCE = 0x2c
|
||||
IFT_FRF16MFRBUNDLE = 0xa3
|
||||
IFT_FRFORWARD = 0x9e
|
||||
IFT_G703AT2MB = 0x43
|
||||
IFT_G703AT64K = 0x42
|
||||
IFT_GIF = 0xf0
|
||||
IFT_GIGABITETHERNET = 0x75
|
||||
IFT_GR303IDT = 0xb2
|
||||
IFT_GR303RDT = 0xb1
|
||||
IFT_H323GATEKEEPER = 0xa4
|
||||
IFT_H323PROXY = 0xa5
|
||||
IFT_HDH1822 = 0x3
|
||||
IFT_HDLC = 0x76
|
||||
IFT_HDSL2 = 0xa8
|
||||
IFT_HIPERLAN2 = 0xb7
|
||||
IFT_HIPPI = 0x2f
|
||||
IFT_HIPPIINTERFACE = 0x39
|
||||
IFT_HOSTPAD = 0x5a
|
||||
IFT_HSSI = 0x2e
|
||||
IFT_HY = 0xe
|
||||
IFT_IBM370PARCHAN = 0x48
|
||||
IFT_IDSL = 0x9a
|
||||
IFT_IEEE80211 = 0x47
|
||||
IFT_IEEE80212 = 0x37
|
||||
IFT_IEEE8023ADLAG = 0xa1
|
||||
IFT_IFGSN = 0x91
|
||||
IFT_IMT = 0xbe
|
||||
IFT_INTERLEAVE = 0x7c
|
||||
IFT_IP = 0x7e
|
||||
IFT_IPFORWARD = 0x8e
|
||||
IFT_IPOVERATM = 0x72
|
||||
IFT_IPOVERCDLC = 0x6d
|
||||
IFT_IPOVERCLAW = 0x6e
|
||||
IFT_IPSWITCH = 0x4e
|
||||
IFT_IPXIP = 0xf9
|
||||
IFT_ISDN = 0x3f
|
||||
IFT_ISDNBASIC = 0x14
|
||||
IFT_ISDNPRIMARY = 0x15
|
||||
IFT_ISDNS = 0x4b
|
||||
IFT_ISDNU = 0x4c
|
||||
IFT_ISO88022LLC = 0x29
|
||||
IFT_ISO88023 = 0x7
|
||||
IFT_ISO88024 = 0x8
|
||||
IFT_ISO88025 = 0x9
|
||||
IFT_ISO88025CRFPINT = 0x62
|
||||
IFT_ISO88025DTR = 0x56
|
||||
IFT_ISO88025FIBER = 0x73
|
||||
IFT_ISO88026 = 0xa
|
||||
IFT_ISUP = 0xb3
|
||||
IFT_L3IPXVLAN = 0x89
|
||||
IFT_LAPB = 0x10
|
||||
IFT_LAPD = 0x4d
|
||||
IFT_LAPF = 0x77
|
||||
IFT_LOCALTALK = 0x2a
|
||||
IFT_LOOP = 0x18
|
||||
IFT_MEDIAMAILOVERIP = 0x8b
|
||||
IFT_MFSIGLINK = 0xa7
|
||||
IFT_MIOX25 = 0x26
|
||||
IFT_MODEM = 0x30
|
||||
IFT_MPC = 0x71
|
||||
IFT_MPLS = 0xa6
|
||||
IFT_MPLSTUNNEL = 0x96
|
||||
IFT_MSDSL = 0x8f
|
||||
IFT_MVL = 0xbf
|
||||
IFT_MYRINET = 0x63
|
||||
IFT_NFAS = 0xaf
|
||||
IFT_NSIP = 0x1b
|
||||
IFT_OPTICALCHANNEL = 0xc3
|
||||
IFT_OPTICALTRANSPORT = 0xc4
|
||||
IFT_OTHER = 0x1
|
||||
IFT_P10 = 0xc
|
||||
IFT_P80 = 0xd
|
||||
IFT_PARA = 0x22
|
||||
IFT_PFLOG = 0xf6
|
||||
IFT_PFSYNC = 0xf7
|
||||
IFT_PLC = 0xae
|
||||
IFT_POS = 0xab
|
||||
IFT_PPPMULTILINKBUNDLE = 0x6c
|
||||
IFT_PROPBWAP2MP = 0xb8
|
||||
IFT_PROPCNLS = 0x59
|
||||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
|
||||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
|
||||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
|
||||
IFT_PROPMUX = 0x36
|
||||
IFT_PROPWIRELESSP2P = 0x9d
|
||||
IFT_PTPSERIAL = 0x16
|
||||
IFT_PVC = 0xf1
|
||||
IFT_QLLC = 0x44
|
||||
IFT_RADIOMAC = 0xbc
|
||||
IFT_RADSL = 0x5f
|
||||
IFT_REACHDSL = 0xc0
|
||||
IFT_RFC1483 = 0x9f
|
||||
IFT_RS232 = 0x21
|
||||
IFT_RSRB = 0x4f
|
||||
IFT_SDLC = 0x11
|
||||
IFT_SDSL = 0x60
|
||||
IFT_SHDSL = 0xa9
|
||||
IFT_SIP = 0x1f
|
||||
IFT_SLIP = 0x1c
|
||||
IFT_SMDSDXI = 0x2b
|
||||
IFT_SMDSICIP = 0x34
|
||||
IFT_SONET = 0x27
|
||||
IFT_SONETOVERHEADCHANNEL = 0xb9
|
||||
IFT_SONETPATH = 0x32
|
||||
IFT_SONETVT = 0x33
|
||||
IFT_SRP = 0x97
|
||||
IFT_SS7SIGLINK = 0x9c
|
||||
IFT_STACKTOSTACK = 0x6f
|
||||
IFT_STARLAN = 0xb
|
||||
IFT_STF = 0xd7
|
||||
IFT_T1 = 0x12
|
||||
IFT_TDLC = 0x74
|
||||
IFT_TERMPAD = 0x5b
|
||||
IFT_TR008 = 0xb0
|
||||
IFT_TRANSPHDLC = 0x7b
|
||||
IFT_TUNNEL = 0x83
|
||||
IFT_ULTRA = 0x1d
|
||||
IFT_USB = 0xa0
|
||||
IFT_V11 = 0x40
|
||||
IFT_V35 = 0x2d
|
||||
IFT_V36 = 0x41
|
||||
IFT_V37 = 0x78
|
||||
IFT_VDSL = 0x61
|
||||
IFT_VIRTUALIPADDRESS = 0x70
|
||||
IFT_VOICEEM = 0x64
|
||||
IFT_VOICEENCAP = 0x67
|
||||
IFT_VOICEFXO = 0x65
|
||||
IFT_VOICEFXS = 0x66
|
||||
IFT_VOICEOVERATM = 0x98
|
||||
IFT_VOICEOVERFRAMERELAY = 0x99
|
||||
IFT_VOICEOVERIP = 0x68
|
||||
IFT_X213 = 0x5d
|
||||
IFT_X25 = 0x5
|
||||
IFT_X25DDN = 0x4
|
||||
IFT_X25HUNTGROUP = 0x7a
|
||||
IFT_X25MLP = 0x79
|
||||
IFT_X25PLE = 0x28
|
||||
IFT_XETHER = 0x1a
|
||||
IPPROTO_MAXID = 0x34
|
||||
IPV6_FAITH = 0x1d
|
||||
IP_FAITH = 0x16
|
||||
MAP_NORESERVE = 0x40
|
||||
MAP_RENAME = 0x20
|
||||
NET_RT_MAXID = 0x6
|
||||
RTF_PRCLONING = 0x10000
|
||||
RTM_OLDADD = 0x9
|
||||
RTM_OLDDEL = 0xa
|
||||
SIOCADDRT = 0x8030720a
|
||||
SIOCALIFADDR = 0x8118691b
|
||||
SIOCDELRT = 0x8030720b
|
||||
SIOCDLIFADDR = 0x8118691d
|
||||
SIOCGLIFADDR = 0xc118691c
|
||||
SIOCGLIFPHYADDR = 0xc118694b
|
||||
SIOCSLIFPHYADDR = 0x8118694a
|
||||
)
|
|
@ -0,0 +1,227 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep
|
||||
// them here for backwards compatibility.
|
||||
|
||||
package unix
|
||||
|
||||
const (
|
||||
IFF_SMART = 0x20
|
||||
IFT_1822 = 0x2
|
||||
IFT_A12MPPSWITCH = 0x82
|
||||
IFT_AAL2 = 0xbb
|
||||
IFT_AAL5 = 0x31
|
||||
IFT_ADSL = 0x5e
|
||||
IFT_AFLANE8023 = 0x3b
|
||||
IFT_AFLANE8025 = 0x3c
|
||||
IFT_ARAP = 0x58
|
||||
IFT_ARCNET = 0x23
|
||||
IFT_ARCNETPLUS = 0x24
|
||||
IFT_ASYNC = 0x54
|
||||
IFT_ATM = 0x25
|
||||
IFT_ATMDXI = 0x69
|
||||
IFT_ATMFUNI = 0x6a
|
||||
IFT_ATMIMA = 0x6b
|
||||
IFT_ATMLOGICAL = 0x50
|
||||
IFT_ATMRADIO = 0xbd
|
||||
IFT_ATMSUBINTERFACE = 0x86
|
||||
IFT_ATMVCIENDPT = 0xc2
|
||||
IFT_ATMVIRTUAL = 0x95
|
||||
IFT_BGPPOLICYACCOUNTING = 0xa2
|
||||
IFT_BSC = 0x53
|
||||
IFT_CCTEMUL = 0x3d
|
||||
IFT_CEPT = 0x13
|
||||
IFT_CES = 0x85
|
||||
IFT_CHANNEL = 0x46
|
||||
IFT_CNR = 0x55
|
||||
IFT_COFFEE = 0x84
|
||||
IFT_COMPOSITELINK = 0x9b
|
||||
IFT_DCN = 0x8d
|
||||
IFT_DIGITALPOWERLINE = 0x8a
|
||||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
|
||||
IFT_DLSW = 0x4a
|
||||
IFT_DOCSCABLEDOWNSTREAM = 0x80
|
||||
IFT_DOCSCABLEMACLAYER = 0x7f
|
||||
IFT_DOCSCABLEUPSTREAM = 0x81
|
||||
IFT_DS0 = 0x51
|
||||
IFT_DS0BUNDLE = 0x52
|
||||
IFT_DS1FDL = 0xaa
|
||||
IFT_DS3 = 0x1e
|
||||
IFT_DTM = 0x8c
|
||||
IFT_DVBASILN = 0xac
|
||||
IFT_DVBASIOUT = 0xad
|
||||
IFT_DVBRCCDOWNSTREAM = 0x93
|
||||
IFT_DVBRCCMACLAYER = 0x92
|
||||
IFT_DVBRCCUPSTREAM = 0x94
|
||||
IFT_ENC = 0xf4
|
||||
IFT_EON = 0x19
|
||||
IFT_EPLRS = 0x57
|
||||
IFT_ESCON = 0x49
|
||||
IFT_ETHER = 0x6
|
||||
IFT_FAITH = 0xf2
|
||||
IFT_FAST = 0x7d
|
||||
IFT_FASTETHER = 0x3e
|
||||
IFT_FASTETHERFX = 0x45
|
||||
IFT_FDDI = 0xf
|
||||
IFT_FIBRECHANNEL = 0x38
|
||||
IFT_FRAMERELAYINTERCONNECT = 0x3a
|
||||
IFT_FRAMERELAYMPI = 0x5c
|
||||
IFT_FRDLCIENDPT = 0xc1
|
||||
IFT_FRELAY = 0x20
|
||||
IFT_FRELAYDCE = 0x2c
|
||||
IFT_FRF16MFRBUNDLE = 0xa3
|
||||
IFT_FRFORWARD = 0x9e
|
||||
IFT_G703AT2MB = 0x43
|
||||
IFT_G703AT64K = 0x42
|
||||
IFT_GIF = 0xf0
|
||||
IFT_GIGABITETHERNET = 0x75
|
||||
IFT_GR303IDT = 0xb2
|
||||
IFT_GR303RDT = 0xb1
|
||||
IFT_H323GATEKEEPER = 0xa4
|
||||
IFT_H323PROXY = 0xa5
|
||||
IFT_HDH1822 = 0x3
|
||||
IFT_HDLC = 0x76
|
||||
IFT_HDSL2 = 0xa8
|
||||
IFT_HIPERLAN2 = 0xb7
|
||||
IFT_HIPPI = 0x2f
|
||||
IFT_HIPPIINTERFACE = 0x39
|
||||
IFT_HOSTPAD = 0x5a
|
||||
IFT_HSSI = 0x2e
|
||||
IFT_HY = 0xe
|
||||
IFT_IBM370PARCHAN = 0x48
|
||||
IFT_IDSL = 0x9a
|
||||
IFT_IEEE80211 = 0x47
|
||||
IFT_IEEE80212 = 0x37
|
||||
IFT_IEEE8023ADLAG = 0xa1
|
||||
IFT_IFGSN = 0x91
|
||||
IFT_IMT = 0xbe
|
||||
IFT_INTERLEAVE = 0x7c
|
||||
IFT_IP = 0x7e
|
||||
IFT_IPFORWARD = 0x8e
|
||||
IFT_IPOVERATM = 0x72
|
||||
IFT_IPOVERCDLC = 0x6d
|
||||
IFT_IPOVERCLAW = 0x6e
|
||||
IFT_IPSWITCH = 0x4e
|
||||
IFT_IPXIP = 0xf9
|
||||
IFT_ISDN = 0x3f
|
||||
IFT_ISDNBASIC = 0x14
|
||||
IFT_ISDNPRIMARY = 0x15
|
||||
IFT_ISDNS = 0x4b
|
||||
IFT_ISDNU = 0x4c
|
||||
IFT_ISO88022LLC = 0x29
|
||||
IFT_ISO88023 = 0x7
|
||||
IFT_ISO88024 = 0x8
|
||||
IFT_ISO88025 = 0x9
|
||||
IFT_ISO88025CRFPINT = 0x62
|
||||
IFT_ISO88025DTR = 0x56
|
||||
IFT_ISO88025FIBER = 0x73
|
||||
IFT_ISO88026 = 0xa
|
||||
IFT_ISUP = 0xb3
|
||||
IFT_L3IPXVLAN = 0x89
|
||||
IFT_LAPB = 0x10
|
||||
IFT_LAPD = 0x4d
|
||||
IFT_LAPF = 0x77
|
||||
IFT_LOCALTALK = 0x2a
|
||||
IFT_LOOP = 0x18
|
||||
IFT_MEDIAMAILOVERIP = 0x8b
|
||||
IFT_MFSIGLINK = 0xa7
|
||||
IFT_MIOX25 = 0x26
|
||||
IFT_MODEM = 0x30
|
||||
IFT_MPC = 0x71
|
||||
IFT_MPLS = 0xa6
|
||||
IFT_MPLSTUNNEL = 0x96
|
||||
IFT_MSDSL = 0x8f
|
||||
IFT_MVL = 0xbf
|
||||
IFT_MYRINET = 0x63
|
||||
IFT_NFAS = 0xaf
|
||||
IFT_NSIP = 0x1b
|
||||
IFT_OPTICALCHANNEL = 0xc3
|
||||
IFT_OPTICALTRANSPORT = 0xc4
|
||||
IFT_OTHER = 0x1
|
||||
IFT_P10 = 0xc
|
||||
IFT_P80 = 0xd
|
||||
IFT_PARA = 0x22
|
||||
IFT_PFLOG = 0xf6
|
||||
IFT_PFSYNC = 0xf7
|
||||
IFT_PLC = 0xae
|
||||
IFT_POS = 0xab
|
||||
IFT_PPPMULTILINKBUNDLE = 0x6c
|
||||
IFT_PROPBWAP2MP = 0xb8
|
||||
IFT_PROPCNLS = 0x59
|
||||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
|
||||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
|
||||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
|
||||
IFT_PROPMUX = 0x36
|
||||
IFT_PROPWIRELESSP2P = 0x9d
|
||||
IFT_PTPSERIAL = 0x16
|
||||
IFT_PVC = 0xf1
|
||||
IFT_QLLC = 0x44
|
||||
IFT_RADIOMAC = 0xbc
|
||||
IFT_RADSL = 0x5f
|
||||
IFT_REACHDSL = 0xc0
|
||||
IFT_RFC1483 = 0x9f
|
||||
IFT_RS232 = 0x21
|
||||
IFT_RSRB = 0x4f
|
||||
IFT_SDLC = 0x11
|
||||
IFT_SDSL = 0x60
|
||||
IFT_SHDSL = 0xa9
|
||||
IFT_SIP = 0x1f
|
||||
IFT_SLIP = 0x1c
|
||||
IFT_SMDSDXI = 0x2b
|
||||
IFT_SMDSICIP = 0x34
|
||||
IFT_SONET = 0x27
|
||||
IFT_SONETOVERHEADCHANNEL = 0xb9
|
||||
IFT_SONETPATH = 0x32
|
||||
IFT_SONETVT = 0x33
|
||||
IFT_SRP = 0x97
|
||||
IFT_SS7SIGLINK = 0x9c
|
||||
IFT_STACKTOSTACK = 0x6f
|
||||
IFT_STARLAN = 0xb
|
||||
IFT_STF = 0xd7
|
||||
IFT_T1 = 0x12
|
||||
IFT_TDLC = 0x74
|
||||
IFT_TERMPAD = 0x5b
|
||||
IFT_TR008 = 0xb0
|
||||
IFT_TRANSPHDLC = 0x7b
|
||||
IFT_TUNNEL = 0x83
|
||||
IFT_ULTRA = 0x1d
|
||||
IFT_USB = 0xa0
|
||||
IFT_V11 = 0x40
|
||||
IFT_V35 = 0x2d
|
||||
IFT_V36 = 0x41
|
||||
IFT_V37 = 0x78
|
||||
IFT_VDSL = 0x61
|
||||
IFT_VIRTUALIPADDRESS = 0x70
|
||||
IFT_VOICEEM = 0x64
|
||||
IFT_VOICEENCAP = 0x67
|
||||
IFT_VOICEFXO = 0x65
|
||||
IFT_VOICEFXS = 0x66
|
||||
IFT_VOICEOVERATM = 0x98
|
||||
IFT_VOICEOVERFRAMERELAY = 0x99
|
||||
IFT_VOICEOVERIP = 0x68
|
||||
IFT_X213 = 0x5d
|
||||
IFT_X25 = 0x5
|
||||
IFT_X25DDN = 0x4
|
||||
IFT_X25HUNTGROUP = 0x7a
|
||||
IFT_X25MLP = 0x79
|
||||
IFT_X25PLE = 0x28
|
||||
IFT_XETHER = 0x1a
|
||||
IPPROTO_MAXID = 0x34
|
||||
IPV6_FAITH = 0x1d
|
||||
IP_FAITH = 0x16
|
||||
MAP_NORESERVE = 0x40
|
||||
MAP_RENAME = 0x20
|
||||
NET_RT_MAXID = 0x6
|
||||
RTF_PRCLONING = 0x10000
|
||||
RTM_OLDADD = 0x9
|
||||
RTM_OLDDEL = 0xa
|
||||
SIOCADDRT = 0x8040720a
|
||||
SIOCALIFADDR = 0x8118691b
|
||||
SIOCDELRT = 0x8040720b
|
||||
SIOCDLIFADDR = 0x8118691d
|
||||
SIOCGLIFADDR = 0xc118691c
|
||||
SIOCGLIFPHYADDR = 0xc118694b
|
||||
SIOCSLIFPHYADDR = 0x8118694a
|
||||
)
|
|
@ -0,0 +1,226 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package unix
|
||||
|
||||
const (
|
||||
IFT_1822 = 0x2
|
||||
IFT_A12MPPSWITCH = 0x82
|
||||
IFT_AAL2 = 0xbb
|
||||
IFT_AAL5 = 0x31
|
||||
IFT_ADSL = 0x5e
|
||||
IFT_AFLANE8023 = 0x3b
|
||||
IFT_AFLANE8025 = 0x3c
|
||||
IFT_ARAP = 0x58
|
||||
IFT_ARCNET = 0x23
|
||||
IFT_ARCNETPLUS = 0x24
|
||||
IFT_ASYNC = 0x54
|
||||
IFT_ATM = 0x25
|
||||
IFT_ATMDXI = 0x69
|
||||
IFT_ATMFUNI = 0x6a
|
||||
IFT_ATMIMA = 0x6b
|
||||
IFT_ATMLOGICAL = 0x50
|
||||
IFT_ATMRADIO = 0xbd
|
||||
IFT_ATMSUBINTERFACE = 0x86
|
||||
IFT_ATMVCIENDPT = 0xc2
|
||||
IFT_ATMVIRTUAL = 0x95
|
||||
IFT_BGPPOLICYACCOUNTING = 0xa2
|
||||
IFT_BSC = 0x53
|
||||
IFT_CCTEMUL = 0x3d
|
||||
IFT_CEPT = 0x13
|
||||
IFT_CES = 0x85
|
||||
IFT_CHANNEL = 0x46
|
||||
IFT_CNR = 0x55
|
||||
IFT_COFFEE = 0x84
|
||||
IFT_COMPOSITELINK = 0x9b
|
||||
IFT_DCN = 0x8d
|
||||
IFT_DIGITALPOWERLINE = 0x8a
|
||||
IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
|
||||
IFT_DLSW = 0x4a
|
||||
IFT_DOCSCABLEDOWNSTREAM = 0x80
|
||||
IFT_DOCSCABLEMACLAYER = 0x7f
|
||||
IFT_DOCSCABLEUPSTREAM = 0x81
|
||||
IFT_DS0 = 0x51
|
||||
IFT_DS0BUNDLE = 0x52
|
||||
IFT_DS1FDL = 0xaa
|
||||
IFT_DS3 = 0x1e
|
||||
IFT_DTM = 0x8c
|
||||
IFT_DVBASILN = 0xac
|
||||
IFT_DVBASIOUT = 0xad
|
||||
IFT_DVBRCCDOWNSTREAM = 0x93
|
||||
IFT_DVBRCCMACLAYER = 0x92
|
||||
IFT_DVBRCCUPSTREAM = 0x94
|
||||
IFT_ENC = 0xf4
|
||||
IFT_EON = 0x19
|
||||
IFT_EPLRS = 0x57
|
||||
IFT_ESCON = 0x49
|
||||
IFT_ETHER = 0x6
|
||||
IFT_FAST = 0x7d
|
||||
IFT_FASTETHER = 0x3e
|
||||
IFT_FASTETHERFX = 0x45
|
||||
IFT_FDDI = 0xf
|
||||
IFT_FIBRECHANNEL = 0x38
|
||||
IFT_FRAMERELAYINTERCONNECT = 0x3a
|
||||
IFT_FRAMERELAYMPI = 0x5c
|
||||
IFT_FRDLCIENDPT = 0xc1
|
||||
IFT_FRELAY = 0x20
|
||||
IFT_FRELAYDCE = 0x2c
|
||||
IFT_FRF16MFRBUNDLE = 0xa3
|
||||
IFT_FRFORWARD = 0x9e
|
||||
IFT_G703AT2MB = 0x43
|
||||
IFT_G703AT64K = 0x42
|
||||
IFT_GIF = 0xf0
|
||||
IFT_GIGABITETHERNET = 0x75
|
||||
IFT_GR303IDT = 0xb2
|
||||
IFT_GR303RDT = 0xb1
|
||||
IFT_H323GATEKEEPER = 0xa4
|
||||
IFT_H323PROXY = 0xa5
|
||||
IFT_HDH1822 = 0x3
|
||||
IFT_HDLC = 0x76
|
||||
IFT_HDSL2 = 0xa8
|
||||
IFT_HIPERLAN2 = 0xb7
|
||||
IFT_HIPPI = 0x2f
|
||||
IFT_HIPPIINTERFACE = 0x39
|
||||
IFT_HOSTPAD = 0x5a
|
||||
IFT_HSSI = 0x2e
|
||||
IFT_HY = 0xe
|
||||
IFT_IBM370PARCHAN = 0x48
|
||||
IFT_IDSL = 0x9a
|
||||
IFT_IEEE80211 = 0x47
|
||||
IFT_IEEE80212 = 0x37
|
||||
IFT_IEEE8023ADLAG = 0xa1
|
||||
IFT_IFGSN = 0x91
|
||||
IFT_IMT = 0xbe
|
||||
IFT_INTERLEAVE = 0x7c
|
||||
IFT_IP = 0x7e
|
||||
IFT_IPFORWARD = 0x8e
|
||||
IFT_IPOVERATM = 0x72
|
||||
IFT_IPOVERCDLC = 0x6d
|
||||
IFT_IPOVERCLAW = 0x6e
|
||||
IFT_IPSWITCH = 0x4e
|
||||
IFT_ISDN = 0x3f
|
||||
IFT_ISDNBASIC = 0x14
|
||||
IFT_ISDNPRIMARY = 0x15
|
||||
IFT_ISDNS = 0x4b
|
||||
IFT_ISDNU = 0x4c
|
||||
IFT_ISO88022LLC = 0x29
|
||||
IFT_ISO88023 = 0x7
|
||||
IFT_ISO88024 = 0x8
|
||||
IFT_ISO88025 = 0x9
|
||||
IFT_ISO88025CRFPINT = 0x62
|
||||
IFT_ISO88025DTR = 0x56
|
||||
IFT_ISO88025FIBER = 0x73
|
||||
IFT_ISO88026 = 0xa
|
||||
IFT_ISUP = 0xb3
|
||||
IFT_L3IPXVLAN = 0x89
|
||||
IFT_LAPB = 0x10
|
||||
IFT_LAPD = 0x4d
|
||||
IFT_LAPF = 0x77
|
||||
IFT_LOCALTALK = 0x2a
|
||||
IFT_LOOP = 0x18
|
||||
IFT_MEDIAMAILOVERIP = 0x8b
|
||||
IFT_MFSIGLINK = 0xa7
|
||||
IFT_MIOX25 = 0x26
|
||||
IFT_MODEM = 0x30
|
||||
IFT_MPC = 0x71
|
||||
IFT_MPLS = 0xa6
|
||||
IFT_MPLSTUNNEL = 0x96
|
||||
IFT_MSDSL = 0x8f
|
||||
IFT_MVL = 0xbf
|
||||
IFT_MYRINET = 0x63
|
||||
IFT_NFAS = 0xaf
|
||||
IFT_NSIP = 0x1b
|
||||
IFT_OPTICALCHANNEL = 0xc3
|
||||
IFT_OPTICALTRANSPORT = 0xc4
|
||||
IFT_OTHER = 0x1
|
||||
IFT_P10 = 0xc
|
||||
IFT_P80 = 0xd
|
||||
IFT_PARA = 0x22
|
||||
IFT_PFLOG = 0xf6
|
||||
IFT_PFSYNC = 0xf7
|
||||
IFT_PLC = 0xae
|
||||
IFT_POS = 0xab
|
||||
IFT_PPPMULTILINKBUNDLE = 0x6c
|
||||
IFT_PROPBWAP2MP = 0xb8
|
||||
IFT_PROPCNLS = 0x59
|
||||
IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
|
||||
IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
|
||||
IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
|
||||
IFT_PROPMUX = 0x36
|
||||
IFT_PROPWIRELESSP2P = 0x9d
|
||||
IFT_PTPSERIAL = 0x16
|
||||
IFT_PVC = 0xf1
|
||||
IFT_QLLC = 0x44
|
||||
IFT_RADIOMAC = 0xbc
|
||||
IFT_RADSL = 0x5f
|
||||
IFT_REACHDSL = 0xc0
|
||||
IFT_RFC1483 = 0x9f
|
||||
IFT_RS232 = 0x21
|
||||
IFT_RSRB = 0x4f
|
||||
IFT_SDLC = 0x11
|
||||
IFT_SDSL = 0x60
|
||||
IFT_SHDSL = 0xa9
|
||||
IFT_SIP = 0x1f
|
||||
IFT_SLIP = 0x1c
|
||||
IFT_SMDSDXI = 0x2b
|
||||
IFT_SMDSICIP = 0x34
|
||||
IFT_SONET = 0x27
|
||||
IFT_SONETOVERHEADCHANNEL = 0xb9
|
||||
IFT_SONETPATH = 0x32
|
||||
IFT_SONETVT = 0x33
|
||||
IFT_SRP = 0x97
|
||||
IFT_SS7SIGLINK = 0x9c
|
||||
IFT_STACKTOSTACK = 0x6f
|
||||
IFT_STARLAN = 0xb
|
||||
IFT_STF = 0xd7
|
||||
IFT_T1 = 0x12
|
||||
IFT_TDLC = 0x74
|
||||
IFT_TERMPAD = 0x5b
|
||||
IFT_TR008 = 0xb0
|
||||
IFT_TRANSPHDLC = 0x7b
|
||||
IFT_TUNNEL = 0x83
|
||||
IFT_ULTRA = 0x1d
|
||||
IFT_USB = 0xa0
|
||||
IFT_V11 = 0x40
|
||||
IFT_V35 = 0x2d
|
||||
IFT_V36 = 0x41
|
||||
IFT_V37 = 0x78
|
||||
IFT_VDSL = 0x61
|
||||
IFT_VIRTUALIPADDRESS = 0x70
|
||||
IFT_VOICEEM = 0x64
|
||||
IFT_VOICEENCAP = 0x67
|
||||
IFT_VOICEFXO = 0x65
|
||||
IFT_VOICEFXS = 0x66
|
||||
IFT_VOICEOVERATM = 0x98
|
||||
IFT_VOICEOVERFRAMERELAY = 0x99
|
||||
IFT_VOICEOVERIP = 0x68
|
||||
IFT_X213 = 0x5d
|
||||
IFT_X25 = 0x5
|
||||
IFT_X25DDN = 0x4
|
||||
IFT_X25HUNTGROUP = 0x7a
|
||||
IFT_X25MLP = 0x79
|
||||
IFT_X25PLE = 0x28
|
||||
IFT_XETHER = 0x1a
|
||||
|
||||
// missing constants on FreeBSD-11.1-RELEASE, copied from old values in ztypes_freebsd_arm.go
|
||||
IFF_SMART = 0x20
|
||||
IFT_FAITH = 0xf2
|
||||
IFT_IPXIP = 0xf9
|
||||
IPPROTO_MAXID = 0x34
|
||||
IPV6_FAITH = 0x1d
|
||||
IP_FAITH = 0x16
|
||||
MAP_NORESERVE = 0x40
|
||||
MAP_RENAME = 0x20
|
||||
NET_RT_MAXID = 0x6
|
||||
RTF_PRCLONING = 0x10000
|
||||
RTM_OLDADD = 0x9
|
||||
RTM_OLDDEL = 0xa
|
||||
SIOCADDRT = 0x8030720a
|
||||
SIOCALIFADDR = 0x8118691b
|
||||
SIOCDELRT = 0x8030720b
|
||||
SIOCDLIFADDR = 0x8118691d
|
||||
SIOCGLIFADDR = 0xc118691c
|
||||
SIOCGLIFPHYADDR = 0xc118694b
|
||||
SIOCSLIFPHYADDR = 0x8118694a
|
||||
)
|
|
@ -1,5 +1,3 @@
|
|||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
@ -14,6 +12,16 @@ import "unsafe"
|
|||
// systems by flock_linux_32bit.go to be SYS_FCNTL64.
|
||||
var fcntl64Syscall uintptr = SYS_FCNTL
|
||||
|
||||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
valptr, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(arg))
|
||||
var err error
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
}
|
||||
return int(valptr), err
|
||||
}
|
||||
|
||||
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
|
||||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
|
||||
_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
|
|
@ -1,19 +1,30 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gccgo
|
||||
// +build !aix
|
||||
|
||||
package unix
|
||||
|
||||
import "syscall"
|
||||
|
||||
// We can't use the gc-syntax .s files for gccgo. On the plus side
|
||||
// We can't use the gc-syntax .s files for gccgo. On the plus side
|
||||
// much of the functionality can be written directly in Go.
|
||||
|
||||
//extern gccgoRealSyscallNoError
|
||||
func realSyscallNoError(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r uintptr)
|
||||
|
||||
//extern gccgoRealSyscall
|
||||
func realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r, errno uintptr)
|
||||
|
||||
func SyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) {
|
||||
syscall.Entersyscall()
|
||||
r := realSyscallNoError(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
|
||||
syscall.Exitsyscall()
|
||||
return r, 0
|
||||
}
|
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
syscall.Entersyscall()
|
||||
r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
|
||||
|
@ -35,6 +46,11 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
|
|||
return r, 0, syscall.Errno(errno)
|
||||
}
|
||||
|
||||
func RawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) {
|
||||
r := realSyscallNoError(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
|
||||
return r, 0
|
||||
}
|
||||
|
||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
|
||||
return r, 0, syscall.Errno(errno)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gccgo
|
||||
// +build !aix
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
@ -31,11 +32,8 @@ gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintp
|
|||
return r;
|
||||
}
|
||||
|
||||
// Define the use function in C so that it is not inlined.
|
||||
|
||||
extern void use(void *) __asm__ (GOSYM_PREFIX GOPKGPATH ".use") __attribute__((noinline));
|
||||
|
||||
void
|
||||
use(void *p __attribute__ ((unused)))
|
||||
uintptr_t
|
||||
gccgoRealSyscallNoError(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
|
||||
{
|
||||
return syscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gccgo,linux,sparc64
|
||||
|
||||
package unix
|
||||
|
||||
import "syscall"
|
||||
|
||||
//extern sysconf
|
||||
func realSysconf(name int) int64
|
||||
|
||||
func sysconf(name int) (n int64, err syscall.Errno) {
|
||||
r := realSysconf(name)
|
||||
if r < 0 {
|
||||
return 0, syscall.GetErrno()
|
||||
}
|
||||
return r, 0
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package unix
|
||||
|
||||
import "runtime"
|
||||
|
||||
// IoctlSetWinsize performs an ioctl on fd with a *Winsize argument.
|
||||
//
|
||||
// To change fd's window size, the req argument should be TIOCSWINSZ.
|
||||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
|
||||
// TODO: if we get the chance, remove the req parameter and
|
||||
// hardcode TIOCSWINSZ.
|
||||
err := ioctlSetWinsize(fd, req, value)
|
||||
runtime.KeepAlive(value)
|
||||
return err
|
||||
}
|
||||
|
||||
// IoctlSetTermios performs an ioctl on fd with a *Termios.
|
||||
//
|
||||
// The req value will usually be TCSETA or TIOCSETA.
|
||||
func IoctlSetTermios(fd int, req uint, value *Termios) error {
|
||||
// TODO: if we get the chance, remove the req parameter.
|
||||
err := ioctlSetTermios(fd, req, value)
|
||||
runtime.KeepAlive(value)
|
||||
return err
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue