mirror of https://github.com/ouqiang/gocron
依赖包管理切换到gomod
parent
c997ce7cab
commit
372a5e2d8a
|
@ -0,0 +1,37 @@
|
|||
module github.com/ouqiang/gocron
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/Tang-RoseChild/mahonia v0.0.0-20131226213531-0eef680515cc
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 // indirect
|
||||
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
||||
github.com/go-macaron/binding v0.0.0-20170611065819-ac54ee249c27
|
||||
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
|
||||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 // indirect
|
||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90
|
||||
github.com/go-sql-driver/mysql v1.4.1
|
||||
github.com/go-xorm/builder v0.3.4 // indirect
|
||||
github.com/go-xorm/core v0.6.2
|
||||
github.com/go-xorm/xorm v0.7.1
|
||||
github.com/golang/protobuf v1.3.1
|
||||
github.com/jakecoffman/cron v0.0.0-20190106200828-7e2009c226a5
|
||||
github.com/klauspost/compress v1.5.0 // indirect
|
||||
github.com/klauspost/cpuid v1.2.1 // indirect
|
||||
github.com/lib/pq v1.1.1
|
||||
github.com/ouqiang/goutil v1.1.1
|
||||
github.com/rakyll/statik v0.1.6
|
||||
github.com/urfave/cli v1.20.0
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 // indirect
|
||||
google.golang.org/grpc v1.21.0
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||
gopkg.in/ini.v1 v1.42.0
|
||||
gopkg.in/macaron.v1 v1.3.2
|
||||
)
|
|
@ -0,0 +1,148 @@
|
|||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Tang-RoseChild/mahonia v0.0.0-20131226213531-0eef680515cc h1:7QGNag2vwx98HBiQ8A9M2PWO0ws/xNU6eZVlAbvIWZw=
|
||||
github.com/Tang-RoseChild/mahonia v0.0.0-20131226213531-0eef680515cc/go.mod h1:Dl60+a8Tcs+dl3UbHuY54vuwwXo6JJ22RwnDGZ+0U+o=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 h1:1B7wb36fHLSwZfHg6ngZhhtIEHQjiC5H4p7qQGBEffg=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68=
|
||||
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs=
|
||||
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
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/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f h1:WH0w/R4Yoey+04HhFxqZ6VX6I0d7RMyw5aXQ9UTvQPs=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E=
|
||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
|
||||
github.com/go-macaron/binding v0.0.0-20170611065819-ac54ee249c27 h1:ieTTL9+RAtpcMaskwWNN19Vdrsl4X+k2CWsQijWNsAU=
|
||||
github.com/go-macaron/binding v0.0.0-20170611065819-ac54ee249c27/go.mod h1:u+H6rwW+HQwUL+w5uaEJSpIlVZDye1o9MB4Su0JfRfM=
|
||||
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07 h1:YSIA98PevNf1NtCa/J6cz7gjzpz99WVAOa9Eg0klKps=
|
||||
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07/go.mod h1://cJFfDp/70L0oTNAMB+M8Jd0rpuIx/55iARuJ6StwE=
|
||||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI=
|
||||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw=
|
||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90 h1:3wYKrRg9IjUMfaf3H0Hh7M5Li9ge79Y7aw2yujHa2jQ=
|
||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90/go.mod h1:Ut/NmkIMGVYlEdJBzEZgWVWG5ZpYS9BLmUgXfAgi+qM=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-xorm/builder v0.3.2/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk=
|
||||
github.com/go-xorm/builder v0.3.4 h1:FxkeGB4Cggdw3tPwutLCpfjng2jugfkg6LDMrd/KsoY=
|
||||
github.com/go-xorm/builder v0.3.4/go.mod h1:KxkQkNN1DpPKTedxXyTQcmH+rXfvk4LZ9SOOBoZBAxw=
|
||||
github.com/go-xorm/core v0.6.0/go.mod h1:d8FJ9Br8OGyQl12MCclmYBuBqqxsyeedpXciV5Myih8=
|
||||
github.com/go-xorm/core v0.6.2 h1:EJLcSxf336POJr670wKB55Mah9f93xzvGYzNRgnT8/Y=
|
||||
github.com/go-xorm/core v0.6.2/go.mod h1:bwPIfLdm/FzWgVUH8WPVlr+uJhscvNGFcaZKXsI3n2c=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||
github.com/go-xorm/xorm v0.7.1 h1:Kj7mfuqctPdX60zuxP6EoEut0f3E6K66H6hcoxiHUMc=
|
||||
github.com/go-xorm/xorm v0.7.1/go.mod h1:EHS1htMQFptzMaIHKyzqpHGw6C9Rtug75nsq6DA9unI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY=
|
||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jakecoffman/cron v0.0.0-20190106200828-7e2009c226a5 h1:kCvm3G3u+eTRbjfLPyfsfznJtraYEfZer/UvQ6CaQhI=
|
||||
github.com/jakecoffman/cron v0.0.0-20190106200828-7e2009c226a5/go.mod h1:6DM2KNNK69jRu0lAHmYK9LYxmqpNjYHOaNp/ZxttD4U=
|
||||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/compress v1.5.0 h1:iDac0ZKbmSA4PRrRuXXjZL8C7UoJan8oBYxXkMzEQrI=
|
||||
github.com/klauspost/compress v1.5.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/ouqiang/goutil v1.1.1 h1:r5EKn1jw6vBnj0sLrk3XWeQGxG5ftjGQ3ftuiiO2DYE=
|
||||
github.com/ouqiang/goutil v1.1.1/go.mod h1:QrB1Ky4uGqcixxOx55MXweI3IA6nDZ0NtLMXbMfkur4=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
|
||||
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 h1:wuGevabY6r+ivPNagjUXGGxF+GqgMd+dBhjsxW4q9u4=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
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/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/macaron.v1 v1.3.2 h1:AvWIaPmwBUA87/OWzePkoxeaw6YJWDfBt1pDFPBnLf8=
|
||||
gopkg.in/macaron.v1 v1.3.2/go.mod h1:PrsiawTWAGZs6wFbT5hlr7SQ2Ns9h7cUVtcUu4lQOVo=
|
||||
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
@ -5,6 +5,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
@ -12,7 +14,6 @@ import (
|
|||
"github.com/ouqiang/gocron/internal/modules/app"
|
||||
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||
"github.com/ouqiang/gocron/internal/modules/setting"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
type Status int8
|
||||
|
@ -78,7 +79,7 @@ func CreateDb() *xorm.Engine {
|
|||
}
|
||||
engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns)
|
||||
engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns)
|
||||
engine.DB().SetConnMaxLifetime(dbMaxLiftTime)
|
||||
engine.SetConnMaxLifetime(dbMaxLiftTime)
|
||||
|
||||
if app.Setting.Db.Prefix != "" {
|
||||
// 设置表前缀
|
||||
|
@ -110,7 +111,7 @@ func getDbEngineDSN(setting *setting.Setting) string {
|
|||
dsn := ""
|
||||
switch engine {
|
||||
case "mysql":
|
||||
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",
|
||||
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&allowNativePasswords=true",
|
||||
setting.Db.User,
|
||||
setting.Db.Password,
|
||||
setting.Db.Host,
|
||||
|
|
|
@ -26,7 +26,7 @@ func RandAuthToken() string {
|
|||
// 生成长度为length的随机字符串
|
||||
func RandString(length int64) string {
|
||||
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
result := []byte{}
|
||||
var result []byte
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
sourceLength := len(sources)
|
||||
var i int64 = 0
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/lib/pq"
|
||||
|
@ -13,7 +15,6 @@ import (
|
|||
"github.com/ouqiang/gocron/internal/modules/setting"
|
||||
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||
"github.com/ouqiang/gocron/internal/service"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// 系统安装
|
||||
|
@ -107,7 +108,7 @@ func writeConfig(form InstallForm) error {
|
|||
"db.database", form.DbName,
|
||||
"db.prefix", form.DbTablePrefix,
|
||||
"db.charset", "utf8",
|
||||
"db.max.idle.conns", "30",
|
||||
"db.max.idle.conns", "5",
|
||||
"db.max.open.conns", "100",
|
||||
"allow_ips", "",
|
||||
"app.name", "定时任务管理系统", // 应用名称
|
||||
|
|
|
@ -1,844 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Converters for simple 8-bit character sets.
|
||||
|
||||
type eightBitInfo struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
|
||||
// the character used for characters that can't be converted
|
||||
SubstitutionChar byte
|
||||
|
||||
// a string containing all 256 characters, in order.
|
||||
Repertoire string
|
||||
|
||||
// used to synchronize unpacking Repertoire into the conversion tables
|
||||
once *sync.Once
|
||||
|
||||
// true if the first 128 characters are the same as US-ASCII
|
||||
asciiCompatible bool
|
||||
|
||||
byte2char [256]rune
|
||||
char2byte map[rune]byte
|
||||
}
|
||||
|
||||
const asciiRepertoire = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f"
|
||||
|
||||
func (info *eightBitInfo) register() {
|
||||
var cs Charset
|
||||
cs.Name = info.Name
|
||||
cs.Aliases = info.Aliases
|
||||
|
||||
info.once = new(sync.Once)
|
||||
|
||||
cs.NewDecoder = func() Decoder {
|
||||
info.once.Do(func() { info.unpack() })
|
||||
|
||||
return func(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
c = info.byte2char[p[0]]
|
||||
|
||||
if c == 0xfffd {
|
||||
status = INVALID_CHAR
|
||||
} else {
|
||||
status = SUCCESS
|
||||
}
|
||||
|
||||
size = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cs.NewEncoder = func() Encoder {
|
||||
info.once.Do(func() { info.unpack() })
|
||||
|
||||
return func(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if c < 128 && info.asciiCompatible {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
b, ok := info.char2byte[c]
|
||||
if !ok {
|
||||
b = info.SubstitutionChar
|
||||
status = INVALID_CHAR
|
||||
} else {
|
||||
status = SUCCESS
|
||||
}
|
||||
p[0] = b
|
||||
size = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
RegisterCharset(&cs)
|
||||
}
|
||||
|
||||
func (info *eightBitInfo) unpack() {
|
||||
info.asciiCompatible = info.Repertoire[:128] == asciiRepertoire
|
||||
|
||||
info.char2byte = make(map[rune]byte, 256)
|
||||
i := 0
|
||||
for _, c := range info.Repertoire {
|
||||
info.byte2char[i] = c
|
||||
if c != 0xfffd {
|
||||
info.char2byte[c] = byte(i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i != 256 {
|
||||
panic(fmt.Errorf("%s has only %d characters", info.Name, i))
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i := 0; i < len(eightBitCharsets); i++ {
|
||||
eightBitCharsets[i].register()
|
||||
}
|
||||
}
|
||||
|
||||
var eightBitCharsets = []eightBitInfo{
|
||||
{
|
||||
Name: "ISO-8859-2",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0104\u02d8\u0141\u00a4\u013d\u015a\u00a7\u00a8\u0160\u015e\u0164\u0179\u00ad\u017d\u017b\u00b0\u0105\u02db\u0142\u00b4\u013e\u015b\u02c7\u00b8\u0161\u015f\u0165\u017a\u02dd\u017e\u017c\u0154\u00c1\u00c2\u0102\u00c4\u0139\u0106\u00c7\u010c\u00c9\u0118\u00cb\u011a\u00cd\u00ce\u010e\u0110\u0143\u0147\u00d3\u00d4\u0150\u00d6\u00d7\u0158\u016e\u00da\u0170\u00dc\u00dd\u0162\u00df\u0155\u00e1\u00e2\u0103\u00e4\u013a\u0107\u00e7\u010d\u00e9\u0119\u00eb\u011b\u00ed\u00ee\u010f\u0111\u0144\u0148\u00f3\u00f4\u0151\u00f6\u00f7\u0159\u016f\u00fa\u0171\u00fc\u00fd\u0163\u02d9",
|
||||
Aliases: []string{"ISO_8859-2:1987", "iso-ir-101", "latin2", "l2", "csISOLatin2"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-3",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0126\u02d8\u00a3\u00a4\ufffd\u0124\u00a7\u00a8\u0130\u015e\u011e\u0134\u00ad\ufffd\u017b\u00b0\u0127\u00b2\u00b3\u00b4\u00b5\u0125\u00b7\u00b8\u0131\u015f\u011f\u0135\u00bd\ufffd\u017c\u00c0\u00c1\u00c2\ufffd\u00c4\u010a\u0108\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\ufffd\u00d1\u00d2\u00d3\u00d4\u0120\u00d6\u00d7\u011c\u00d9\u00da\u00db\u00dc\u016c\u015c\u00df\u00e0\u00e1\u00e2\ufffd\u00e4\u010b\u0109\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\ufffd\u00f1\u00f2\u00f3\u00f4\u0121\u00f6\u00f7\u011d\u00f9\u00fa\u00fb\u00fc\u016d\u015d\u02d9",
|
||||
Aliases: []string{"ISO_8859-3:1988", "iso-ir-109", "latin3", "l3", "csISOLatin3"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-4",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0104\u0138\u0156\u00a4\u0128\u013b\u00a7\u00a8\u0160\u0112\u0122\u0166\u00ad\u017d\u00af\u00b0\u0105\u02db\u0157\u00b4\u0129\u013c\u02c7\u00b8\u0161\u0113\u0123\u0167\u014a\u017e\u014b\u0100\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u012e\u010c\u00c9\u0118\u00cb\u0116\u00cd\u00ce\u012a\u0110\u0145\u014c\u0136\u00d4\u00d5\u00d6\u00d7\u00d8\u0172\u00da\u00db\u00dc\u0168\u016a\u00df\u0101\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u012f\u010d\u00e9\u0119\u00eb\u0117\u00ed\u00ee\u012b\u0111\u0146\u014d\u0137\u00f4\u00f5\u00f6\u00f7\u00f8\u0173\u00fa\u00fb\u00fc\u0169\u016b\u02d9",
|
||||
Aliases: []string{"ISO_8859-4:1988", "iso-ir-110", "latin4", "l4", "csISOLatin4"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-5",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u00ad\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u2116\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u00a7\u045e\u045f",
|
||||
Aliases: []string{"ISO_8859-5:1988", "iso-ir-144", "cyrillic", "csISOLatinCyrillic"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-6",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\ufffd\ufffd\ufffd\u00a4\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u060c\u00ad\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u061b\ufffd\ufffd\ufffd\u061f\ufffd\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\ufffd\ufffd\ufffd\ufffd\ufffd\u0640\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd",
|
||||
Aliases: []string{"ISO_8859-6:1987", "iso-ir-127", "ECMA-114", "ASMO-708", "arabic", "csISOLatinArabic"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-7",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u2018\u2019\u00a3\u20ac\u20af\u00a6\u00a7\u00a8\u00a9\u037a\u00ab\u00ac\u00ad\ufffd\u2015\u00b0\u00b1\u00b2\u00b3\u0384\u0385\u0386\u00b7\u0388\u0389\u038a\u00bb\u038c\u00bd\u038e\u038f\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\ufffd\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\ufffd",
|
||||
Aliases: []string{"ISO_8859-7:2003", "iso-ir-126", "ELOT_928", "ECMA-118", "greek", "greek8", "csISOLatinGreek"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-8",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\ufffd\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00d7\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00f7\u00bb\u00bc\u00bd\u00be\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u2017\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\ufffd\ufffd\u200e\u200f\ufffd",
|
||||
Aliases: []string{"ISO_8859-8:1999", "iso-ir-138", "hebrew", "csISOLatinHebrew"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-9",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u011e\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u0130\u015e\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u011f\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u0131\u015f\u00ff",
|
||||
Aliases: []string{"ISO_8859-9:1999", "iso-ir-148", "latin5", "l5", "csISOLatin5"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-10",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0104\u0112\u0122\u012a\u0128\u0136\u00a7\u013b\u0110\u0160\u0166\u017d\u00ad\u016a\u014a\u00b0\u0105\u0113\u0123\u012b\u0129\u0137\u00b7\u013c\u0111\u0161\u0167\u017e\u2015\u016b\u014b\u0100\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u012e\u010c\u00c9\u0118\u00cb\u0116\u00cd\u00ce\u00cf\u00d0\u0145\u014c\u00d3\u00d4\u00d5\u00d6\u0168\u00d8\u0172\u00da\u00db\u00dc\u00dd\u00de\u00df\u0101\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u012f\u010d\u00e9\u0119\u00eb\u0117\u00ed\u00ee\u00ef\u00f0\u0146\u014d\u00f3\u00f4\u00f5\u00f6\u0169\u00f8\u0173\u00fa\u00fb\u00fc\u00fd\u00fe\u0138",
|
||||
Aliases: []string{"iso_8859-10:1992", "l6", "iso-ir-157", "latin6", "csISOLatin6"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-11",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\ufffd\ufffd\ufffd\ufffd\u0e3f\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e5a\u0e5b\ufffd\ufffd\ufffd\ufffd",
|
||||
Aliases: []string{"iso_8859-11:2001", "Latin/Thai", "TIS-620"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-13",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u201d\u00a2\u00a3\u00a4\u201e\u00a6\u00a7\u00d8\u00a9\u0156\u00ab\u00ac\u00ad\u00ae\u00c6\u00b0\u00b1\u00b2\u00b3\u201c\u00b5\u00b6\u00b7\u00f8\u00b9\u0157\u00bb\u00bc\u00bd\u00be\u00e6\u0104\u012e\u0100\u0106\u00c4\u00c5\u0118\u0112\u010c\u00c9\u0179\u0116\u0122\u0136\u012a\u013b\u0160\u0143\u0145\u00d3\u014c\u00d5\u00d6\u00d7\u0172\u0141\u015a\u016a\u00dc\u017b\u017d\u00df\u0105\u012f\u0101\u0107\u00e4\u00e5\u0119\u0113\u010d\u00e9\u017a\u0117\u0123\u0137\u012b\u013c\u0161\u0144\u0146\u00f3\u014d\u00f5\u00f6\u00f7\u0173\u0142\u015b\u016b\u00fc\u017c\u017e\u2019",
|
||||
Aliases: []string{"latin7", "Baltic Rim"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-14",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u1e02\u1e03\u00a3\u010a\u010b\u1e0a\u00a7\u1e80\u00a9\u1e82\u1e0b\u1ef2\u00ad\u00ae\u0178\u1e1e\u1e1f\u0120\u0121\u1e40\u1e41\u00b6\u1e56\u1e81\u1e57\u1e83\u1e60\u1ef3\u1e84\u1e85\u1e61\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u0174\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u1e6a\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u0176\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u0175\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u1e6b\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u0177\u00ff",
|
||||
Aliases: []string{"iso-ir-199", "ISO_8859-14:1998", "latin8", "iso-celtic", "l8"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-15",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u00a1\u00a2\u00a3\u20ac\u00a5\u0160\u00a7\u0161\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u017d\u00b5\u00b6\u00b7\u017e\u00b9\u00ba\u00bb\u0152\u0153\u0178\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff",
|
||||
Aliases: []string{"Latin-9"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-16",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0104\u0105\u0141\u20ac\u201e\u0160\u00a7\u0161\u00a9\u0218\u00ab\u0179\u00ad\u017a\u017b\u00b0\u00b1\u010c\u0142\u017d\u201d\u00b6\u00b7\u017e\u010d\u0219\u00bb\u0152\u0153\u0178\u017c\u00c0\u00c1\u00c2\u0102\u00c4\u0106\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u0110\u0143\u00d2\u00d3\u00d4\u0150\u00d6\u015a\u0170\u00d9\u00da\u00db\u00dc\u0118\u021a\u00df\u00e0\u00e1\u00e2\u0103\u00e4\u0107\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u0111\u0144\u00f2\u00f3\u00f4\u0151\u00f6\u015b\u0171\u00f9\u00fa\u00fb\u00fc\u0119\u021b\u00ff",
|
||||
Aliases: []string{"iso-ir-226", "ISO_8859-16:2001", "latin10", "l10"},
|
||||
},
|
||||
{
|
||||
Name: "macos-0_2-10.2",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca\u00ff\u0178\u2044\u20ac\u2039\u203a\ufb01\ufb02\u2021\u00b7\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\u0131\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7",
|
||||
Aliases: []string{"macos-0_2-10.2", "macintosh", "mac", "csMacintosh", "windows-10000", "macroman"},
|
||||
},
|
||||
{
|
||||
Name: "macos-6_2-10.4",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u00c4\u00b9\u00b2\u00c9\u00b3\u00d6\u00dc\u0385\u00e0\u00e2\u00e4\u0384\u00a8\u00e7\u00e9\u00e8\u00ea\u00eb\u00a3\u2122\u00ee\u00ef\u2022\u00bd\u2030\u00f4\u00f6\u00a6\u20ac\u00f9\u00fb\u00fc\u2020\u0393\u0394\u0398\u039b\u039e\u03a0\u00df\u00ae\u00a9\u03a3\u03aa\u00a7\u2260\u00b0\u00b7\u0391\u00b1\u2264\u2265\u00a5\u0392\u0395\u0396\u0397\u0399\u039a\u039c\u03a6\u03ab\u03a8\u03a9\u03ac\u039d\u00ac\u039f\u03a1\u2248\u03a4\u00ab\u00bb\u2026\u00a0\u03a5\u03a7\u0386\u0388\u0153\u2013\u2015\u201c\u201d\u2018\u2019\u00f7\u0389\u038a\u038c\u038e\u03ad\u03ae\u03af\u03cc\u038f\u03cd\u03b1\u03b2\u03c8\u03b4\u03b5\u03c6\u03b3\u03b7\u03b9\u03be\u03ba\u03bb\u03bc\u03bd\u03bf\u03c0\u03ce\u03c1\u03c3\u03c4\u03b8\u03c9\u03c2\u03c7\u03c5\u03b6\u03ca\u03cb\u0390\u03b0\u00ad",
|
||||
Aliases: []string{"macos-6_2-10.4", "x-mac-greek", "windows-10006", "macgr"},
|
||||
},
|
||||
{
|
||||
Name: "macos-7_3-10.2",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u2020\u00b0\u0490\u00a3\u00a7\u2022\u00b6\u0406\u00ae\u00a9\u2122\u0402\u0452\u2260\u0403\u0453\u221e\u00b1\u2264\u2265\u0456\u00b5\u0491\u0408\u0404\u0454\u0407\u0457\u0409\u0459\u040a\u045a\u0458\u0405\u00ac\u221a\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u040b\u045b\u040c\u045c\u0455\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u201e\u040e\u045e\u040f\u045f\u2116\u0401\u0451\u044f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u20ac",
|
||||
Aliases: []string{"macos-7_3-10.2", "x-mac-cyrillic", "windows-10007", "mac-cyrillic", "maccy"},
|
||||
},
|
||||
{
|
||||
Name: "macos-29-10.2",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u00c4\u0100\u0101\u00c9\u0104\u00d6\u00dc\u00e1\u0105\u010c\u00e4\u010d\u0106\u0107\u00e9\u0179\u017a\u010e\u00ed\u010f\u0112\u0113\u0116\u00f3\u0117\u00f4\u00f6\u00f5\u00fa\u011a\u011b\u00fc\u2020\u00b0\u0118\u00a3\u00a7\u2022\u00b6\u00df\u00ae\u00a9\u2122\u0119\u00a8\u2260\u0123\u012e\u012f\u012a\u2264\u2265\u012b\u0136\u2202\u2211\u0142\u013b\u013c\u013d\u013e\u0139\u013a\u0145\u0146\u0143\u00ac\u221a\u0144\u0147\u2206\u00ab\u00bb\u2026\u00a0\u0148\u0150\u00d5\u0151\u014c\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca\u014d\u0154\u0155\u0158\u2039\u203a\u0159\u0156\u0157\u0160\u201a\u201e\u0161\u015a\u015b\u00c1\u0164\u0165\u00cd\u017d\u017e\u016a\u00d3\u00d4\u016b\u016e\u00da\u016f\u0170\u0171\u0172\u0173\u00dd\u00fd\u0137\u017b\u0141\u017c\u0122\u02c7",
|
||||
Aliases: []string{"macos-29-10.2", "x-mac-centraleurroman", "windows-10029", "x-mac-ce", "macce", "maccentraleurope"},
|
||||
},
|
||||
{
|
||||
Name: "macos-35-10.2",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u00c4\u00c5\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5\u00fa\u00f9\u00fb\u00fc\u2020\u00b0\u00a2\u00a3\u00a7\u2022\u00b6\u00df\u00ae\u00a9\u2122\u00b4\u00a8\u2260\u00c6\u00d8\u221e\u00b1\u2264\u2265\u00a5\u00b5\u2202\u2211\u220f\u03c0\u222b\u00aa\u00ba\u03a9\u00e6\u00f8\u00bf\u00a1\u00ac\u221a\u0192\u2248\u2206\u00ab\u00bb\u2026\u00a0\u00c0\u00c3\u00d5\u0152\u0153\u2013\u2014\u201c\u201d\u2018\u2019\u00f7\u25ca\u00ff\u0178\u011e\u011f\u0130\u0131\u015e\u015f\u2021\u00b7\u201a\u201e\u2030\u00c2\u00ca\u00c1\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00d3\u00d4\uf8ff\u00d2\u00da\u00db\u00d9\uf8a0\u02c6\u02dc\u00af\u02d8\u02d9\u02da\u00b8\u02dd\u02db\u02c7",
|
||||
Aliases: []string{"macos-35-10.2", "x-mac-turkish", "windows-10081", "mactr"},
|
||||
},
|
||||
{
|
||||
Name: "windows-1250",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\u201a\ufffd\u201e\u2026\u2020\u2021\ufffd\u2030\u0160\u2039\u015a\u0164\u017d\u0179\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffd\u2122\u0161\u203a\u015b\u0165\u017e\u017a\u00a0\u02c7\u02d8\u0141\u00a4\u0104\u00a6\u00a7\u00a8\u00a9\u015e\u00ab\u00ac\u00ad\u00ae\u017b\u00b0\u00b1\u02db\u0142\u00b4\u00b5\u00b6\u00b7\u00b8\u0105\u015f\u00bb\u013d\u02dd\u013e\u017c\u0154\u00c1\u00c2\u0102\u00c4\u0139\u0106\u00c7\u010c\u00c9\u0118\u00cb\u011a\u00cd\u00ce\u010e\u0110\u0143\u0147\u00d3\u00d4\u0150\u00d6\u00d7\u0158\u016e\u00da\u0170\u00dc\u00dd\u0162\u00df\u0155\u00e1\u00e2\u0103\u00e4\u013a\u0107\u00e7\u010d\u00e9\u0119\u00eb\u011b\u00ed\u00ee\u010f\u0111\u0144\u0148\u00f3\u00f4\u0151\u00f6\u00f7\u0159\u016f\u00fa\u0171\u00fc\u00fd\u0163\u02d9",
|
||||
},
|
||||
{
|
||||
Name: "windows-1251",
|
||||
Aliases: []string{"CP1251"},
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffd\u2122\u0459\u203a\u045a\u045c\u045b\u045f\u00a0\u040e\u045e\u0408\u00a4\u0490\u00a6\u00a7\u0401\u00a9\u0404\u00ab\u00ac\u00ad\u00ae\u0407\u00b0\u00b1\u0406\u0456\u0491\u00b5\u00b6\u00b7\u0451\u2116\u0454\u00bb\u0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f",
|
||||
},
|
||||
{
|
||||
Name: "windows-1252",
|
||||
Aliases: []string{"cp1252"},
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0160\u2039\u0152\ufffd\u017d\ufffd\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u0161\u203a\u0153\ufffd\u017e\u0178\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff",
|
||||
},
|
||||
{
|
||||
Name: "windows-1253",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\u201a\u0192\u201e\u2026\u2020\u2021\ufffd\u2030\ufffd\u2039\ufffd\ufffd\ufffd\ufffd\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffd\u2122\ufffd\u203a\ufffd\ufffd\ufffd\ufffd\u00a0\u0385\u0386\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\ufffd\u00ab\u00ac\u00ad\u00ae\u2015\u00b0\u00b1\u00b2\u00b3\u0384\u00b5\u00b6\u00b7\u0388\u0389\u038a\u00bb\u038c\u00bd\u038e\u038f\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\ufffd\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\ufffd",
|
||||
},
|
||||
{
|
||||
Name: "windows-1254",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0160\u2039\u0152\ufffd\ufffd\ufffd\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u0161\u203a\u0153\ufffd\ufffd\u0178\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u011e\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u0130\u015e\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u011f\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u0131\u015f\u00ff",
|
||||
},
|
||||
{
|
||||
Name: "windows-1255",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\ufffd\u2039\ufffd\ufffd\ufffd\ufffd\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\ufffd\u203a\ufffd\ufffd\ufffd\ufffd\u00a0\u00a1\u00a2\u00a3\u20aa\u00a5\u00a6\u00a7\u00a8\u00a9\u00d7\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00f7\u00bb\u00bc\u00bd\u00be\u00bf\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\ufffd\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05f0\u05f1\u05f2\u05f3\u05f4\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\ufffd\ufffd\u200e\u200f\ufffd",
|
||||
},
|
||||
{
|
||||
Name: "windows-1256",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\u00a0\u060c\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u06be\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u061b\u00bb\u00bc\u00bd\u00be\u061f\u06c1\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u00d7\u0637\u0638\u0639\u063a\u0640\u0641\u0642\u0643\u00e0\u0644\u00e2\u0645\u0646\u0647\u0648\u00e7\u00e8\u00e9\u00ea\u00eb\u0649\u064a\u00ee\u00ef\u064b\u064c\u064d\u064e\u00f4\u064f\u0650\u00f7\u0651\u00f9\u0652\u00fb\u00fc\u200e\u200f\u06d2",
|
||||
},
|
||||
{
|
||||
Name: "windows-1257",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\u201a\ufffd\u201e\u2026\u2020\u2021\ufffd\u2030\ufffd\u2039\ufffd\u00a8\u02c7\u00b8\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffd\u2122\ufffd\u203a\ufffd\u00af\u02db\ufffd\u00a0\ufffd\u00a2\u00a3\u00a4\ufffd\u00a6\u00a7\u00d8\u00a9\u0156\u00ab\u00ac\u00ad\u00ae\u00c6\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00f8\u00b9\u0157\u00bb\u00bc\u00bd\u00be\u00e6\u0104\u012e\u0100\u0106\u00c4\u00c5\u0118\u0112\u010c\u00c9\u0179\u0116\u0122\u0136\u012a\u013b\u0160\u0143\u0145\u00d3\u014c\u00d5\u00d6\u00d7\u0172\u0141\u015a\u016a\u00dc\u017b\u017d\u00df\u0105\u012f\u0101\u0107\u00e4\u00e5\u0119\u0113\u010d\u00e9\u017a\u0117\u0123\u0137\u012b\u013c\u0161\u0144\u0146\u00f3\u014d\u00f5\u00f6\u00f7\u0173\u0142\u015b\u016b\u00fc\u017c\u017e\u02d9",
|
||||
},
|
||||
{
|
||||
Name: "windows-1258",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\ufffd\u2039\u0152\ufffd\ufffd\ufffd\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\ufffd\u203a\u0153\ufffd\ufffd\u0178\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u0102\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u0300\u00cd\u00ce\u00cf\u0110\u00d1\u0309\u00d3\u00d4\u01a0\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u01af\u0303\u00df\u00e0\u00e1\u00e2\u0103\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u0301\u00ed\u00ee\u00ef\u0111\u00f1\u0323\u00f3\u00f4\u01a1\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u01b0\u20ab\u00ff",
|
||||
},
|
||||
{
|
||||
Name: "windows-874",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\ufffd\ufffd\ufffd\ufffd\u2026\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u00a0\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\ufffd\ufffd\ufffd\ufffd\u0e3f\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e5a\u0e5b\ufffd\ufffd\ufffd\ufffd",
|
||||
},
|
||||
{
|
||||
Name: "IBM037",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00a2.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df!$*);\u00ac-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5~stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae^\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be[]\u00af\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"cp037", "ebcdic-cp-us", "ebcdic-cp-ca", "ebcdic-cp-wt", "ebcdic-cp-nl", "csIBM037"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-273_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2{\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00c4.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec~\u00dc$*);^-/\u00c2[\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00f6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#\u00a7'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5\u00dfstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9@\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e4ABCDEFGHI\u00ad\u00f4\u00a6\u00f2\u00f3\u00f5\u00fcJKLMNOPQR\u00b9\u00fb}\u00f9\u00fa\u00ff\u00d6\u00f7STUVWXYZ\u00b2\u00d4\\\u00d2\u00d3\u00d50123456789\u00b3\u00db]\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-273_P100-1995", "ibm-273", "IBM273", "CP273", "csIBM273", "ebcdic-de", "273"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-277_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3}\u00e7\u00f1#.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u00a4\u00c5*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3$\u00c7\u00d1\u00f8,%_>?\u00a6\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:\u00c6\u00d8'=\"@abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba{\u00b8[]\u00b5\u00fcstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e6ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5\u00e5JKLMNOPQR\u00b9\u00fb~\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-277_P100-1995", "ibm-277", "IBM277", "cp277", "EBCDIC-CP-DK", "EBCDIC-CP-NO", "csIBM277", "ebcdic-dk", "277"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-278_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2{\u00e0\u00e1\u00e3}\u00e7\u00f1\u00a7.<(+!&`\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u00a4\u00c5*);^-/\u00c2#\u00c0\u00c1\u00c3$\u00c7\u00d1\u00f6,%_>?\u00f8\\\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00e9:\u00c4\u00d6'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6]\u00b5\u00fcstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9[\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e4ABCDEFGHI\u00ad\u00f4\u00a6\u00f2\u00f3\u00f5\u00e5JKLMNOPQR\u00b9\u00fb~\u00f9\u00fa\u00ff\u00c9\u00f7STUVWXYZ\u00b2\u00d4@\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-278_P100-1995", "ibm-278", "IBM278", "cp278", "ebcdic-cp-fi", "ebcdic-cp-se", "csIBM278", "ebcdic-sv", "278"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-280_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4{\u00e1\u00e3\u00e5\\\u00f1\u00b0.<(+!&]\u00ea\u00eb}\u00ed\u00ee\u00ef~\u00df\u00e9$*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00f2,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00f9:\u00a3\u00a7'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1[jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5\u00ecstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2#\u00a5\u00b7\u00a9@\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e0ABCDEFGHI\u00ad\u00f4\u00f6\u00a6\u00f3\u00f5\u00e8JKLMNOPQR\u00b9\u00fb\u00fc`\u00fa\u00ff\u00e7\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-280_P100-1995", "ibm-280", "IBM280", "CP280", "ebcdic-cp-it", "csIBM280", "280"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-284_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00a6[.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df]$*);\u00ac-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7#\u00f1,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:\u00d1@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5\u00a8stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be^!\u00af~\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-284_P100-1995", "ibm-284", "IBM284", "CP284", "ebcdic-cp-es", "csIBM284", "cpibm284", "284"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-285_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1$.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df!\u00a3*);\u00ac-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5\u00afstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2[\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be^]~\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-285_P100-1995", "ibm-285", "IBM285", "CP285", "ebcdic-cp-gb", "csIBM285", "cpibm285", "ebcdic-gb", "285"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-290_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \uff61\uff62\uff63\uff64\uff65\uff66\uff67\uff68\uff69\u00a3.<(+|&\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\ufffd\uff70\ufffd!\u00a5*);\u00ac-/abcdefgh\ufffd,%_>?[ijklmnop`:#@'=\"]\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7aq\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89r\ufffd\uff8a\uff8b\uff8c~\u203e\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95s\uff96\uff97\uff98\uff99^\u00a2\\tuvwxyz\uff9a\uff9b\uff9c\uff9d\uff9e\uff9f{ABCDEFGHI\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd}JKLMNOPQR\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd$\ufffdSTUVWXYZ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\ufffd\ufffd\ufffd\ufffd\ufffd\u009f",
|
||||
Aliases: []string{"ibm-290_P100-1995", "ibm-290", "IBM290", "cp290", "EBCDIC-JP-kana", "csIBM290"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-297_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4@\u00e1\u00e3\u00e5\\\u00f1\u00b0.<(+!&{\u00ea\u00eb}\u00ed\u00ee\u00ef\u00ec\u00df\u00a7$*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00f9,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00b5:\u00a3\u00e0'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1[jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4`\u00a8stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2#\u00a5\u00b7\u00a9]\u00b6\u00bc\u00bd\u00be\u00ac|\u00af~\u00b4\u00d7\u00e9ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5\u00e8JKLMNOPQR\u00b9\u00fb\u00fc\u00a6\u00fa\u00ff\u00e7\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-297_P100-1995", "ibm-297", "IBM297", "cp297", "ebcdic-cp-fr", "csIBM297", "cpibm297", "297"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-420_X120-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0651\ufe7d\u0640\u200b\u0621\u0622\ufe82\u0623\u00a2.<(+|&\ufe84\u0624\ufffd\ufffd\u0626\u0627\ufe8e\u0628\ufe91!$*);\u00ac-/\u0629\u062a\ufe97\u062b\ufe9b\u062c\ufe9f\u062d\u00a6,%_>?\ufea3\u062e\ufea7\u062f\u0630\u0631\u0632\u0633\ufeb3\u060c:#@'=\"\u0634abcdefghi\ufeb7\u0635\ufebb\u0636\ufebf\u0637\u0638jklmnopqr\u0639\ufeca\ufecb\ufecc\u063a\ufece\ufecf\u00f7stuvwxyz\ufed0\u0641\ufed3\u0642\ufed7\u0643\ufedb\u0644\ufef5\ufef6\ufef7\ufef8\ufffd\ufffd\ufefb\ufefc\ufedf\u0645\ufee3\u0646\ufee7\u0647\u061bABCDEFGHI\u00ad\ufeeb\ufffd\ufeec\ufffd\u0648\u061fJKLMNOPQR\u0649\ufef0\u064a\ufef2\ufef3\u0660\u00d7\ufffdSTUVWXYZ\u0661\u0662\ufffd\u0663\u0664\u06650123456789\ufffd\u0666\u0667\u0668\u0669\u009f",
|
||||
Aliases: []string{"ibm-420_X120-1999", "ibm-420", "IBM420", "cp420", "ebcdic-cp-ar1", "csIBM420", "420"},
|
||||
},
|
||||
{
|
||||
Name: "IBM424",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u00a2.<(+|&\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1!$*);\u00ac-/\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u00a6,%_>?\ufffd\u05ea\ufffd\ufffd\u00a0\ufffd\ufffd\ufffd\u2017`:#@'=\"\ufffdabcdefghi\u00ab\u00bb\ufffd\ufffd\ufffd\u00b1\u00b0jklmnopqr\ufffd\ufffd\ufffd\u00b8\ufffd\u00a4\u00b5~stuvwxyz\ufffd\ufffd\ufffd\ufffd\ufffd\u00ae^\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be[]\u00af\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\ufffd\ufffd\ufffd\ufffd\ufffd}JKLMNOPQR\u00b9\ufffd\ufffd\ufffd\ufffd\ufffd\\\u00f7STUVWXYZ\u00b2\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\u00b3\ufffd\ufffd\ufffd\ufffd\u009f",
|
||||
Aliases: []string{"cp424", "ebcdic-cp-he", "csIBM424"},
|
||||
},
|
||||
{
|
||||
Name: "IBM437",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00a2\u00a3\u00a5\u20a7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u2310\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u00b5\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"cp437", "437", "csPC8CodePage437"},
|
||||
},
|
||||
{
|
||||
Name: "IBM500",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1[.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df]$*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5~stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"CP500", "ebcdic-cp-be", "ebcdic-cp-ch", "csIBM500"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-720_P100-1997",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\ufffd\ufffd\u00e9\u00e2\ufffd\u00e0\ufffd\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\ufffd\ufffd\ufffd\ufffd\u0651\u0652\u00f4\u00a4\u0640\u00fb\u00f9\u0621\u0622\u0623\u0624\u00a3\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u0636\u0637\u0638\u0639\u063a\u0641\u00b5\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u2261\u064b\u064c\u064d\u064e\u064f\u0650\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-720_P100-1997", "ibm-720", "windows-720", "DOS-720"},
|
||||
},
|
||||
{
|
||||
Name: "IBM737",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u03c2\u03c4\u03c5\u03c6\u03c7\u03c8\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03c9\u03ac\u03ad\u03ae\u03ca\u03af\u03cc\u03cd\u03cb\u03ce\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u00b1\u2265\u2264\u03aa\u03ab\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"cp737", "cp737_DOSGreek"},
|
||||
},
|
||||
{
|
||||
Name: "IBM775",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0106\u00fc\u00e9\u0101\u00e4\u0123\u00e5\u0107\u0142\u0113\u0156\u0157\u012b\u0179\u00c4\u00c5\u00c9\u00e6\u00c6\u014d\u00f6\u0122\u00a2\u015a\u015b\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u00a4\u0100\u012a\u00f3\u017b\u017c\u017a\u201d\u00a6\u00a9\u00ae\u00ac\u00bd\u00bc\u0141\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u0104\u010c\u0118\u0116\u2563\u2551\u2557\u255d\u012e\u0160\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u0172\u016a\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u017d\u0105\u010d\u0119\u0117\u012f\u0161\u0173\u016b\u017e\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u00d3\u00df\u014c\u0143\u00f5\u00d5\u00b5\u0144\u0136\u0137\u013b\u013c\u0146\u0112\u0145\u2019\u00ad\u00b1\u201c\u00be\u00b6\u00a7\u00f7\u201e\u00b0\u2219\u00b7\u00b9\u00b3\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"cp775", "csPC775Baltic"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-803_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd$.<(+|\u05d0\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\u00a2*);\u00ac-/\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd,%_>?\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd:#@'=\"\ufffd\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdABCDEFGHI\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdJKLMNOPQR\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdSTUVWXYZ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\ufffd\ufffd\ufffd\ufffd\ufffd\u009f",
|
||||
Aliases: []string{"ibm-803_P100-1999", "ibm-803", "cp803"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-838_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07[\u00a2.<(+|&\u0e48\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e]!$*);\u00ac-/\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15^\u00a6,%_>?\u0e3f\u0e4e\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c`:#@'=\"\u0e4fabcdefghi\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e5ajklmnopqr\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e5b~stuvwxyz\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34{ABCDEFGHI\u0e49\u0e35\u0e36\u0e37\u0e38\u0e39}JKLMNOPQR\u0e3a\u0e40\u0e41\u0e42\u0e43\u0e44\\\u0e4aSTUVWXYZ\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a0123456789\u0e4b\u0e4c\u0e4d\u0e4b\u0e4c\u009f",
|
||||
Aliases: []string{"ibm-838_P100-1995", "ibm-838", "IBM838", "IBM-Thai", "csIBMThai", "cp838", "838", "ibm-9030"},
|
||||
},
|
||||
{
|
||||
Name: "IBM850",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u00c1\u00c2\u00c0\u00a9\u2563\u2551\u2557\u255d\u00a2\u00a5\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u00e3\u00c3\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u00a4\u00f0\u00d0\u00ca\u00cb\u00c8\u0131\u00cd\u00ce\u00cf\u2518\u250c\u2588\u2584\u00a6\u00cc\u2580\u00d3\u00df\u00d4\u00d2\u00f5\u00d5\u00b5\u00fe\u00de\u00da\u00db\u00d9\u00fd\u00dd\u00af\u00b4\u00ad\u00b1\u2017\u00be\u00b6\u00a7\u00f7\u00b8\u00b0\u00a8\u00b7\u00b9\u00b3\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"cp850", "850", "csPC850Multilingual"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-851_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u0386\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u0388\u00c4\u0389\u038a\ufffd\u038c\u00f4\u00f6\u038e\u00fb\u00f9\u038f\u00d6\u00dc\u03ac\u00a3\u03ad\u03ae\u03af\u03ca\u0390\u03cc\u03cd\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u00bd\u0398\u0399\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u039a\u039b\u039c\u039d\u2563\u2551\u2557\u255d\u039e\u039f\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u03a0\u03a1\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u2518\u250c\u2588\u2584\u03b4\u03b5\u2580\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u03c2\u03c4\u00b4\u00ad\u00b1\u03c5\u03c6\u03c7\u00a7\u03c8\u00b8\u00b0\u00a8\u03c9\u03cb\u03b0\u03ce\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-851_P100-1995", "ibm-851", "IBM851", "cp851", "851", "csPC851"},
|
||||
},
|
||||
{
|
||||
Name: "IBM852",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u00c7\u00fc\u00e9\u00e2\u00e4\u016f\u0107\u00e7\u0142\u00eb\u0150\u0151\u00ee\u0179\u00c4\u0106\u00c9\u0139\u013a\u00f4\u00f6\u013d\u013e\u015a\u015b\u00d6\u00dc\u0164\u0165\u0141\u00d7\u010d\u00e1\u00ed\u00f3\u00fa\u0104\u0105\u017d\u017e\u0118\u0119\u00ac\u017a\u010c\u015f\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u00c1\u00c2\u011a\u015e\u2563\u2551\u2557\u255d\u017b\u017c\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u0102\u0103\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u00a4\u0111\u0110\u010e\u00cb\u010f\u0147\u00cd\u00ce\u011b\u2518\u250c\u2588\u2584\u0162\u016e\u2580\u00d3\u00df\u00d4\u0143\u0144\u0148\u0160\u0161\u0154\u00da\u0155\u0170\u00fd\u00dd\u0163\u00b4\u00ad\u02dd\u02db\u02c7\u02d8\u00a7\u00f7\u00b8\u00b0\u00a8\u02d9\u0171\u0158\u0159\u25a0\u00a0",
|
||||
Aliases: []string{"cp852", "852", "csPCp852"},
|
||||
},
|
||||
{
|
||||
Name: "IBM855",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0452\u0402\u0453\u0403\u0451\u0401\u0454\u0404\u0455\u0405\u0456\u0406\u0457\u0407\u0458\u0408\u0459\u0409\u045a\u040a\u045b\u040b\u045c\u040c\u045e\u040e\u045f\u040f\u044e\u042e\u044a\u042a\u0430\u0410\u0431\u0411\u0446\u0426\u0434\u0414\u0435\u0415\u0444\u0424\u0433\u0413\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u0445\u0425\u0438\u0418\u2563\u2551\u2557\u255d\u0439\u0419\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u043a\u041a\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u00a4\u043b\u041b\u043c\u041c\u043d\u041d\u043e\u041e\u043f\u2518\u250c\u2588\u2584\u041f\u044f\u2580\u042f\u0440\u0420\u0441\u0421\u0442\u0422\u0443\u0423\u0436\u0416\u0432\u0412\u044c\u042c\u2116\u00ad\u044b\u042b\u0437\u0417\u0448\u0428\u044d\u042d\u0449\u0429\u0447\u0427\u00a7\u25a0\u00a0",
|
||||
Aliases: []string{"cp855", "855", "csIBM855"},
|
||||
},
|
||||
{
|
||||
Name: "IBM856",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\ufffd\u00a3\ufffd\u00d7\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u00ae\u00ac\u00bd\u00bc\ufffd\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\ufffd\ufffd\ufffd\u00a9\u2563\u2551\u2557\u255d\u00a2\u00a5\u2510\u2514\u2534\u252c\u251c\u2500\u253c\ufffd\ufffd\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u00a4\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u2518\u250c\u2588\u2584\u00a6\ufffd\u2580\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u00b5\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u00af\u00b4\u00ad\u00b1\u2017\u00be\u00b6\u00a7\u00f7\u00b8\u00b0\u00a8\u00b7\u00b9\u00b3\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"cp856", "cp856_Hebrew_PC"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-857_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u0131\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u0130\u00d6\u00dc\u00f8\u00a3\u00d8\u015e\u015f\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u011e\u011f\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u00c1\u00c2\u00c0\u00a9\u2563\u2551\u2557\u255d\u00a2\u00a5\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u00e3\u00c3\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u00a4\u00ba\u00aa\u00ca\u00cb\u00c8\ufffd\u00cd\u00ce\u00cf\u2518\u250c\u2588\u2584\u00a6\u00cc\u2580\u00d3\u00df\u00d4\u00d2\u00f5\u00d5\u00b5\ufffd\u00d7\u00da\u00db\u00d9\u00ec\u00ff\u00af\u00b4\u00ad\u00b1\ufffd\u00be\u00b6\u00a7\u00f7\u00b8\u00b0\u00a8\u00b7\u00b9\u00b3\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-857_P100-1995", "ibm-857", "IBM857", "cp857", "857", "csIBM857", "windows-857"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-858_P100-1997",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u00c1\u00c2\u00c0\u00a9\u2563\u2551\u2557\u255d\u00a2\u00a5\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u00e3\u00c3\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u00a4\u00f0\u00d0\u00ca\u00cb\u00c8\u20ac\u00cd\u00ce\u00cf\u2518\u250c\u2588\u2584\u00a6\u00cc\u2580\u00d3\u00df\u00d4\u00d2\u00f5\u00d5\u00b5\u00fe\u00de\u00da\u00db\u00d9\u00fd\u00dd\u00af\u00b4\u00ad\u00b1\u2017\u00be\u00b6\u00a7\u00f7\u00b8\u00b0\u00a8\u00b7\u00b9\u00b3\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-858_P100-1997", "ibm-858", "IBM00858", "CCSID00858", "CP00858", "PC-Multilingual-850+euro", "cp858", "windows-858"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-860_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00c7\u00fc\u00e9\u00e2\u00e3\u00e0\u00c1\u00e7\u00ea\u00ca\u00e8\u00cd\u00d4\u00ec\u00c3\u00c2\u00c9\u00c0\u00c8\u00f4\u00f5\u00f2\u00da\u00f9\u00cc\u00d5\u00dc\u00a2\u00a3\u00d9\u20a7\u00d3\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00d2\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-860_P100-1995", "ibm-860", "IBM860", "cp860", "860", "csIBM860"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-861_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00d0\u00f0\u00de\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00fe\u00fb\u00dd\u00fd\u00d6\u00dc\u00f8\u00a3\u00d8\u20a7\u0192\u00e1\u00ed\u00f3\u00fa\u00c1\u00cd\u00d3\u00da\u00bf\u2310\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-861_P100-1995", "ibm-861", "IBM861", "cp861", "861", "cp-is", "csIBM861", "windows-861"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-862_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u00a2\u00a3\u00a5\u20a7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u2310\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-862_P100-1995", "ibm-862", "IBM862", "cp862", "862", "csPC862LatinHebrew", "DOS-862", "windows-862"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-863_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00c7\u00fc\u00e9\u00e2\u00c2\u00e0\u00b6\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u2017\u00c0\u00a7\u00c9\u00c8\u00ca\u00f4\u00cb\u00cf\u00fb\u00f9\u00a4\u00d4\u00dc\u00a2\u00a3\u00d9\u00db\u0192\u00a6\u00b4\u00f3\u00fa\u00a8\u00b8\u00b3\u00af\u00ce\u2310\u00ac\u00bd\u00bc\u00be\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-863_P100-1995", "ibm-863", "IBM863", "cp863", "863", "csIBM863"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-864_X110-1999",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00b0\u00b7\u2219\u221a\u2592\u2500\u2502\u253c\u2524\u252c\u251c\u2534\u2510\u250c\u2514\u2518\u03b2\u221e\u03c6\u00b1\u00bd\u00bc\u2248\u00ab\u00bb\ufef7\ufef8\ufffd\ufffd\ufefb\ufefc\u200b\u00a0\u00ad\ufe82\u00a3\u00a4\ufe84\ufffd\ufffd\ufe8e\ufe8f\ufe95\ufe99\u060c\ufe9d\ufea1\ufea5\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\ufed1\u061b\ufeb1\ufeb5\ufeb9\u061f\u00a2\ufe80\ufe81\ufe83\ufe85\ufeca\ufe8b\ufe8d\ufe91\ufe93\ufe97\ufe9b\ufe9f\ufea3\ufea7\ufea9\ufeab\ufead\ufeaf\ufeb3\ufeb7\ufebb\ufebf\ufec3\ufec7\ufecb\ufecf\u00a6\u00ac\u00f7\u00d7\ufec9\u0640\ufed3\ufed7\ufedb\ufedf\ufee3\ufee7\ufeeb\ufeed\ufeef\ufef3\ufebd\ufecc\ufece\ufecd\ufee1\ufe7d\ufe7c\ufee5\ufee9\ufeec\ufef0\ufef2\ufed0\ufed5\ufef5\ufef6\ufedd\ufed9\ufef1\u25a0\ufffd",
|
||||
Aliases: []string{"ibm-864_X110-1999", "ibm-864", "IBM864", "cp864", "csIBM864"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-865_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u20a7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u2310\u00ac\u00bd\u00bc\u00a1\u00ab\u00a4\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-865_P100-1995", "ibm-865", "IBM865", "cp865", "865", "csIBM865"},
|
||||
},
|
||||
{
|
||||
Name: "IBM866",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0401\u0451\u0404\u0454\u0407\u0457\u040e\u045e\u00b0\u2219\u00b7\u221a\u2116\u00a4\u25a0\u00a0",
|
||||
Aliases: []string{"cp866", "866", "csIBM866"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-867_P100-1998",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u00a2\u00a3\u00a5\ufffd\u20aa\u200e\u200f\u202a\u202b\u202d\u202e\u202c\ufffd\ufffd\u2310\u00ac\u00bd\u00bc\u20ac\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-867_P100-1998", "ibm-867"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-868_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u060c\u061b\u061f\ufe81\ufe8d\ufe8e\uf8fb\ufe8f\ufe91\ufb56\ufb58\ufe93\ufe95\ufe97\ufb66\ufb68\ufe99\ufe9b\ufe9d\ufe9f\ufb7a\ufb7c\ufea1\ufea3\ufea5\ufea7\ufea9\ufb88\ufeab\ufead\ufb8c\ufeaf\ufb8a\ufeb1\ufeb3\ufeb5\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\ufeb7\ufeb9\ufebb\ufebd\u2563\u2551\u2557\u255d\ufebf\ufec3\u2510\u2514\u2534\u252c\u251c\u2500\u253c\ufec7\ufec9\u255a\u2554\u2569\u2566\u2560\u2550\u256c\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed3\ufed5\u2518\u250c\u2588\u2584\ufed7\ufb8e\u2580\ufedb\ufb92\ufb94\ufedd\ufedf\ufee0\ufee1\ufee3\ufb9e\ufee5\ufee7\ufe85\ufeed\ufba6\ufba8\ufba9\u00ad\ufbaa\ufe80\ufe89\ufe8a\ufe8b\ufbfc\ufbfd\ufbfe\ufbb0\ufbae\ufe7c\ufe7d\ufffd\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-868_P100-1995", "ibm-868", "IBM868", "CP868", "868", "csIBM868", "cp-ar"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-869_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0386\ufffd\u0387\u00ac\u00a6\u2018\u2019\u0388\u2015\u0389\u038a\u03aa\u038c\ufffd\ufffd\u038e\u03ab\u00a9\u038f\u00b2\u00b3\u03ac\u00a3\u03ad\u03ae\u03af\u03ca\u0390\u03cc\u03cd\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u00bd\u0398\u0399\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u039a\u039b\u039c\u039d\u2563\u2551\u2557\u255d\u039e\u039f\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u03a0\u03a1\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u2518\u250c\u2588\u2584\u03b4\u03b5\u2580\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u03c2\u03c4\u00b4\u00ad\u00b1\u03c5\u03c6\u03c7\u00a7\u03c8\u0385\u00b0\u00a8\u03c9\u03cb\u03b0\u03ce\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-869_P100-1995", "ibm-869", "IBM869", "cp869", "869", "cp-gr", "csIBM869", "windows-869"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-870_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u0163\u00e1\u0103\u010d\u00e7\u0107[.<(+!&\u00e9\u0119\u00eb\u016f\u00ed\u00ee\u013e\u013a\u00df]$*);^-/\u00c2\u00c4\u02dd\u00c1\u0102\u010c\u00c7\u0106|,%_>?\u02c7\u00c9\u0118\u00cb\u016e\u00cd\u00ce\u013d\u0139`:#@'=\"\u02d8abcdefghi\u015b\u0148\u0111\u00fd\u0159\u015f\u00b0jklmnopqr\u0142\u0144\u0161\u00b8\u02db\u00a4\u0105~stuvwxyz\u015a\u0147\u0110\u00dd\u0158\u015e\u02d9\u0104\u017c\u0162\u017b\u00a7\u017e\u017a\u017d\u0179\u0141\u0143\u0160\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u0155\u00f3\u0151}JKLMNOPQR\u011a\u0171\u00fc\u0165\u00fa\u011b\\\u00f7STUVWXYZ\u010f\u00d4\u00d6\u0154\u00d3\u01500123456789\u010e\u0170\u00dc\u0164\u00da\u009f",
|
||||
Aliases: []string{"ibm-870_P100-1995", "ibm-870", "IBM870", "CP870", "ebcdic-cp-roece", "ebcdic-cp-yu", "csIBM870"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-871_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00de.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u00c6$*);\u00d6-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00f0:#\u00d0'=\"\u00d8abcdefghi\u00ab\u00bb`\u00fd{\u00b1\u00b0jklmnopqr\u00aa\u00ba}\u00b8]\u00a4\u00b5\u00f6stuvwxyz\u00a1\u00bf@\u00dd[\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\\\u00d7\u00feABCDEFGHI\u00ad\u00f4~\u00f2\u00f3\u00f5\u00e6JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\u00b4\u00f7STUVWXYZ\u00b2\u00d4^\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-871_P100-1995", "ibm-871", "IBM871", "ebcdic-cp-is", "csIBM871", "CP871", "ebcdic-is", "871"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-874_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0e48\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e49\u0e4a\u0e4b\u0e4c\u0e3f\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e5a\u0e5b\u00a2\u00ac\u00a6\u00a0",
|
||||
Aliases: []string{"ibm-874_P100-1995", "ibm-874", "ibm-9066", "cp874", "tis620.2533", "eucTH"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-875_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399[.<(+!&\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3]$*);^-/\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab|,%_>?\u00a8\u0386\u0388\u0389\u00a0\u038a\u038c\u038e\u038f`:#@'=\"\u0385abcdefghi\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u00b0jklmnopqr\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u00b4~stuvwxyz\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u00a3\u03ac\u03ad\u03ae\u03ca\u03af\u03cc\u03cd\u03cb\u03ce\u03c2\u03c4\u03c5\u03c6\u03c7\u03c8{ABCDEFGHI\u00ad\u03c9\u0390\u03b0\u2018\u2015}JKLMNOPQR\u00b1\u00bd\ufffd\u0387\u2019\u00a6\\\ufffdSTUVWXYZ\u00b2\u00a7\ufffd\ufffd\u00ab\u00ac0123456789\u00b3\u00a9\ufffd\ufffd\u00bb\u009f",
|
||||
Aliases: []string{"ibm-875_P100-1995", "ibm-875", "IBM875", "cp875", "875"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-901_P100-1999",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u201d\u00a2\u00a3\u20ac\u201e\u00a6\u00a7\u00d8\u00a9\u0156\u00ab\u00ac\u00ad\u00ae\u00c6\u00b0\u00b1\u00b2\u00b3\u201c\u00b5\u00b6\u00b7\u00f8\u00b9\u0157\u00bb\u00bc\u00bd\u00be\u00e6\u0104\u012e\u0100\u0106\u00c4\u00c5\u0118\u0112\u010c\u00c9\u0179\u0116\u0122\u0136\u012a\u013b\u0160\u0143\u0145\u00d3\u014c\u00d5\u00d6\u00d7\u0172\u0141\u015a\u016a\u00dc\u017b\u017d\u00df\u0105\u012f\u0101\u0107\u00e4\u00e5\u0119\u0113\u010d\u00e9\u017a\u0117\u0123\u0137\u012b\u013c\u0161\u0144\u0146\u00f3\u014d\u00f5\u00f6\u00f7\u0173\u0142\u015b\u016b\u00fc\u017c\u017e\u2019",
|
||||
Aliases: []string{"ibm-901_P100-1999", "ibm-901"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-902_P100-1999",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u00a1\u00a2\u00a3\u20ac\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u0160\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u017d\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u0161\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u017e\u00ff",
|
||||
Aliases: []string{"ibm-902_P100-1999", "ibm-902"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-916_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\ufffd\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00d7\u00ab\u00ac\u00ad\u00ae\u203e\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u2022\u00b8\u00b9\u00f7\u00bb\u00bc\u00bd\u00be\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u2017\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\ufffd\ufffd\ufffd\ufffd\ufffd",
|
||||
Aliases: []string{"ibm-916_P100-1995", "ibm-916", "cp916", "916"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-918_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u060c\u061b\u061f\ufe81\ufe8d\ufe8e\uf8fb\ufe8f[.<(+!&\ufe91\ufb56\ufb58\ufe93\ufe95\ufe97\ufb66\ufb68\ufe99]$*);^-/\ufe9b\ufe9d\ufe9f\ufb7a\ufb7c\ufea1\ufea3\ufea5`,%_>?\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9:#@'=\"\ufea7abcdefghi\ufea9\ufb88\ufeab\ufead\ufb8c\ufeaf\ufb8ajklmnopqr\ufeb1\ufeb3\ufeb5\ufeb7\ufeb9\ufebb\ufebd~stuvwxyz\ufebf\ufec3\ufec7\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed3\ufed5\ufed7\ufb8e\ufedb|\ufb92\ufb94\ufedd\ufedf{ABCDEFGHI\u00ad\ufee0\ufee1\ufee3\ufb9e\ufee5}JKLMNOPQR\ufee7\ufe85\ufeed\ufba6\ufba8\ufba9\\\ufbaaSTUVWXYZ\ufe80\ufe89\ufe8a\ufe8b\ufbfc\ufbfd0123456789\ufbfe\ufbb0\ufbae\ufe7c\ufe7d\u009f",
|
||||
Aliases: []string{"ibm-918_P100-1995", "ibm-918", "IBM918", "CP918", "ebcdic-cp-ar2", "csIBM918"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-922_P100-1999",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u0160\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u017d\u00df\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u0161\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u017e\u00ff",
|
||||
Aliases: []string{"ibm-922_P100-1999", "ibm-922", "IBM922", "cp922", "922"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1006_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u060c\u061b\u00ad\u061f\ufe81\ufe8d\ufe8e\uf8fb\ufe8f\ufe91\ufb56\ufb58\ufe93\ufe95\ufe97\ufb66\ufb68\ufe99\ufe9b\ufe9d\ufe9f\ufb7a\ufb7c\ufea1\ufea3\ufea5\ufea7\ufea9\ufb88\ufeab\ufead\ufb8c\ufeaf\ufb8a\ufeb1\ufeb3\ufeb5\ufeb7\ufeb9\ufebb\ufebd\ufebf\ufec3\ufec7\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed3\ufed5\ufed7\ufb8e\ufedb\ufb92\ufb94\ufedd\ufedf\ufee0\ufee1\ufee3\ufb9e\ufee5\ufee7\ufe85\ufeed\ufba6\ufba8\ufba9\ufbaa\ufe80\ufe89\ufe8a\ufe8b\ufbfc\ufbfd\ufbfe\ufbb0\ufbae\ufe7c\ufe7d",
|
||||
Aliases: []string{"ibm-1006_P100-1995", "ibm-1006", "IBM1006", "cp1006", "1006"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1025_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0452\u0453\u0451\u0454\u0455\u0456\u0457\u0458[.<(+!&\u0459\u045a\u045b\u045c\u045e\u045f\u042a\u2116\u0402]$*);^-/\u0403\u0401\u0404\u0405\u0406\u0407\u0408\u0409|,%_>?\u040a\u040b\u040c\u00ad\u040e\u040f\u044e\u0430\u0431`:#@'=\"\u0446abcdefghi\u0434\u0435\u0444\u0433\u0445\u0438\u0439jklmnopqr\u043a\u043b\u043c\u043d\u043e\u043f\u044f~stuvwxyz\u0440\u0441\u0442\u0443\u0436\u0432\u044c\u044b\u0437\u0448\u044d\u0449\u0447\u044a\u042e\u0410\u0411\u0426\u0414\u0415\u0424\u0413{ABCDEFGHI\u0425\u0418\u0419\u041a\u041b\u041c}JKLMNOPQR\u041d\u041e\u041f\u042f\u0420\u0421\\\u00a7STUVWXYZ\u0422\u0423\u0416\u0412\u042c\u042b0123456789\u0417\u0428\u042d\u0429\u0427\u009f",
|
||||
Aliases: []string{"ibm-1025_P100-1995", "ibm-1025", "cp1025", "1025"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1026_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5{\u00f1\u00c7.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u011e\u0130*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5[\u00d1\u015f,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u0131:\u00d6\u015e'=\u00dc\u00d8abcdefghi\u00ab\u00bb}`\u00a6\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5\u00f6stuvwxyz\u00a1\u00bf]$@\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e7ABCDEFGHI\u00ad\u00f4~\u00f2\u00f3\u00f5\u011fJKLMNOPQR\u00b9\u00fb\\\u00f9\u00fa\u00ff\u00fc\u00f7STUVWXYZ\u00b2\u00d4#\u00d2\u00d3\u00d50123456789\u00b3\u00db\"\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1026_P100-1995", "ibm-1026", "IBM1026", "CP1026", "csIBM1026", "1026"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1047_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00a2.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df!$*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u00a4\u00b5~stuvwxyz\u00a1\u00bf\u00d0[\u00de\u00ae\u00ac\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00dd\u00a8\u00af]\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1047_P100-1995", "ibm-1047", "IBM1047", "cp1047", "1047"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1097_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u060c\u064b\ufe81\ufe82\uf8fa\ufe8d\ufe8e\uf8fb\u00a4.<(+|&\ufe80\ufe83\ufe84\uf8f9\ufe85\ufe8b\ufe8f\ufe91\ufb56!$*);\u00ac-/\ufb58\ufe95\ufe97\ufe99\ufe9b\ufe9d\ufe9f\ufb7a\u061b,%_>?\ufb7c\ufea1\ufea3\ufea5\ufea7\ufea9\ufeab\ufead\ufeaf`:#@'=\"\ufb8aabcdefghi\u00ab\u00bb\ufeb1\ufeb3\ufeb5\ufeb7\ufeb9jklmnopqr\ufebb\ufebd\ufebf\ufec1\ufec3\ufec5\ufec7~stuvwxyz\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed3\ufed5\ufed7\ufb8e\ufedb\ufb92\ufb94[]\ufedd\ufedf\ufee1\u00d7{ABCDEFGHI\u00ad\ufee3\ufee5\ufee7\ufeed\ufee9}JKLMNOPQR\ufeeb\ufeec\ufba4\ufbfc\ufbfd\ufbfe\\\u061fSTUVWXYZ\u0640\u06f0\u06f1\u06f2\u06f3\u06f40123456789\u06f5\u06f6\u06f7\u06f8\u06f9\u009f",
|
||||
Aliases: []string{"ibm-1097_P100-1995", "ibm-1097", "cp1097", "1097"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1098_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\ufffd\ufffd\u060c\u061b\u061f\u064b\ufe81\ufe82\uf8fa\ufe8d\ufe8e\uf8fb\ufe80\ufe83\ufe84\uf8f9\ufe85\ufe8b\ufe8f\ufe91\ufb56\ufb58\ufe95\ufe97\ufe99\ufe9b\ufe9d\ufe9f\ufb7a\ufb7c\u00d7\ufea1\ufea3\ufea5\ufea7\ufea9\ufeab\ufead\ufeaf\ufb8a\ufeb1\ufeb3\ufeb5\ufeb7\ufeb9\ufebb\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\ufebd\ufebf\ufec1\ufec3\u2563\u2551\u2557\u255d\u00a4\ufec5\u2510\u2514\u2534\u252c\u251c\u2500\u253c\ufec7\ufec9\u255a\u2554\u2569\u2566\u2560\u2550\u256c\ufffd\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed3\u2518\u250c\u2588\u2584\ufed5\ufed7\u2580\ufb8e\ufedb\ufb92\ufb94\ufedd\ufedf\ufee1\ufee3\ufee5\ufee7\ufeed\ufee9\ufeeb\ufeec\ufba4\ufbfc\u00ad\ufbfd\ufbfe\u0640\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-1098_P100-1995", "ibm-1098", "IBM1098", "cp1098", "1098"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1112_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0161\u00e4\u0105\u012f\u016b\u00e5\u0113\u017e\u00a2.<(+|&\u00e9\u0119\u0117\u010d\u0173\u201e\u201c\u0123\u00df!$*);\u00ac-/\u0160\u00c4\u0104\u012e\u016a\u00c5\u0112\u017d\u00a6,%_>?\u00f8\u00c9\u0118\u0116\u010c\u0172\u012a\u013b\u0122`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u0101\u017c\u0144\u00b1\u00b0jklmnopqr\u0156\u0157\u00e6\u0137\u00c6\u00a4\u00b5~stuvwxyz\u201d\u017a\u0100\u017b\u0143\u00ae^\u00a3\u012b\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be[]\u0179\u0136\u013c\u00d7{ABCDEFGHI\u00ad\u014d\u00f6\u0146\u00f3\u00f5}JKLMNOPQR\u00b9\u0107\u00fc\u0142\u015b\u2019\\\u00f7STUVWXYZ\u00b2\u014c\u00d6\u0145\u00d3\u00d50123456789\u00b3\u0106\u00dc\u0141\u015a\u009f",
|
||||
Aliases: []string{"ibm-1112_P100-1995", "ibm-1112", "cp1112", "1112"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1122_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2{\u00e0\u00e1\u00e3}\u00e7\u00f1\u00a7.<(+!&`\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u00a4\u00c5*);^-/\u00c2#\u00c0\u00c1\u00c3$\u00c7\u00d1\u00f6,%_>?\u00f8\\\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00e9:\u00c4\u00d6'=\"\u00d8abcdefghi\u00ab\u00bb\u0161\u00fd\u017e\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6]\u00b5\u00fcstuvwxyz\u00a1\u00bf\u0160\u00dd\u017d\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9[\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e4ABCDEFGHI\u00ad\u00f4\u00a6\u00f2\u00f3\u00f5\u00e5JKLMNOPQR\u00b9\u00fb~\u00f9\u00fa\u00ff\u00c9\u00f7STUVWXYZ\u00b2\u00d4@\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1122_P100-1999", "ibm-1122", "cp1122", "1122"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1123_P100-1995",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0452\u0491\u0451\u0454\u0455\u0456\u0457\u0458[.<(+!&\u0459\u045a\u045b\u045c\u045e\u045f\u042a\u2116\u0402]$*);^-/\u0490\u0401\u0404\u0405\u0406\u0407\u0408\u0409|,%_>?\u040a\u040b\u040c\u00ad\u040e\u040f\u044e\u0430\u0431`:#@'=\"\u0446abcdefghi\u0434\u0435\u0444\u0433\u0445\u0438\u0439jklmnopqr\u043a\u043b\u043c\u043d\u043e\u043f\u044f~stuvwxyz\u0440\u0441\u0442\u0443\u0436\u0432\u044c\u044b\u0437\u0448\u044d\u0449\u0447\u044a\u042e\u0410\u0411\u0426\u0414\u0415\u0424\u0413{ABCDEFGHI\u0425\u0418\u0419\u041a\u041b\u041c}JKLMNOPQR\u041d\u041e\u041f\u042f\u0420\u0421\\\u00a7STUVWXYZ\u0422\u0423\u0416\u0412\u042c\u042b0123456789\u0417\u0428\u042d\u0429\u0427\u009f",
|
||||
Aliases: []string{"ibm-1123_P100-1995", "ibm-1123", "cp1123", "1123"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1124_P100-1996",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0401\u0402\u0490\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u00ad\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u2116\u0451\u0452\u0491\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u00a7\u045e\u045f",
|
||||
Aliases: []string{"ibm-1124_P100-1996", "ibm-1124", "cp1124", "1124"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1125_P100-1997",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0401\u0451\u0490\u0491\u0404\u0454\u0406\u0456\u0407\u0457\u00f7\u00b1\u2116\u00a4\u25a0\u00a0",
|
||||
Aliases: []string{"ibm-1125_P100-1997", "ibm-1125", "cp1125"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1129_P100-1997",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u0153\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u0178\u00b5\u00b6\u00b7\u0152\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u0102\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u0300\u00cd\u00ce\u00cf\u0110\u00d1\u0309\u00d3\u00d4\u01a0\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u01af\u0303\u00df\u00e0\u00e1\u00e2\u0103\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u0301\u00ed\u00ee\u00ef\u0111\u00f1\u0323\u00f3\u00f4\u01a1\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u01b0\u20ab\u00ff",
|
||||
Aliases: []string{"ibm-1129_P100-1997", "ibm-1129"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1130_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u0103\u00e5\u00e7\u00f1[.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u0303\u00df]$*);^-/\u00c2\u00c4\u00c0\u00c1\u0102\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u20ab`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u0111\u0309\u0300\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u0152\u00c6\u00a4\u00b5~stuvwxyz\u00a1\u00bf\u0110\u0323\u0301\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u0153\u0178\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u01b0\u00f3\u01a1}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u01af\u00d3\u01a00123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1130_P100-1997", "ibm-1130"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1131_P100-1997",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1c\x1b\x7f\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x1a\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0401\u0451\u0404\u0454\u0407\u0457\u040e\u045e\u0406\u0456\u00b7\u00a4\u0490\u0491\u2219\u00a0",
|
||||
Aliases: []string{"ibm-1131_P100-1997", "ibm-1131", "cp1131"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1132_P100-1998",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0e81\u0e82\u0e84\u0e87\u0e88\u0eaa\u0e8a[\u00a2.<(+|&\ufffd\u0e8d\u0e94\u0e95\u0e96\u0e97\u0e99\u0e9a]!$*);\u00ac-/\u0e9b\u0e9c\u0e9d\u0e9e\u0e9f\u0ea1\u0ea2^\u00a6,%_>?\u20ad\ufffd\u0ea3\u0ea5\u0ea7\u0eab\u0ead\u0eae\ufffd`:#@'=\"\ufffdabcdefghi\ufffd\ufffd\u0eaf\u0eb0\u0eb2\u0eb3\ufffdjklmnopqr\u0eb4\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\ufffd~stuvwxyz\u0ebc\u0eb1\u0ebb\u0ebd\ufffd\ufffd\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\ufffd\u0ec0\u0ec1\u0ec2\u0ec3\u0ec4{ABCDEFGHI\ufffd\u0ec8\u0ec9\u0eca\u0ecb\u0ecc}JKLMNOPQR\u0ecd\u0ec6\ufffd\u0edc\u0edd\ufffd\\\ufffdSTUVWXYZ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\ufffd\ufffd\ufffd\ufffd\ufffd\u009f",
|
||||
Aliases: []string{"ibm-1132_P100-1998", "ibm-1132"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1133_P100-1997",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\ufffd\u0e81\u0e82\u0e84\u0e87\u0e88\u0eaa\u0e8a\u0e8d\u0e94\u0e95\u0e96\u0e97\u0e99\u0e9a\u0e9b\u0e9c\u0e9d\u0e9e\u0e9f\u0ea1\u0ea2\u0ea3\u0ea5\u0ea7\u0eab\u0ead\u0eae\ufffd\ufffd\ufffd\u0eaf\u0eb0\u0eb2\u0eb3\u0eb4\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\u0ebc\u0eb1\u0ebb\u0ebd\ufffd\ufffd\ufffd\u0ec0\u0ec1\u0ec2\u0ec3\u0ec4\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0ec6\ufffd\u0edc\u0eddk\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\ufffd\ufffd\u00a2\u00ac\u00a6\u00a0",
|
||||
Aliases: []string{"ibm-1133_P100-1997", "ibm-1133"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1137_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0901\u0902\u0903\u0905\u0906\u0907\u0908\u0909\u090a.<(+|&\u090b\u090c\u090d\u090e\u090f\u0910\u0911\u0912\u0913!$*);^-/\u0914\u0915\u0916\u0917\u0918\u0919\u091a\u091b\u091c,%_>?\u091d\u091e\u091f\u0920\u0921\u0922\u0923\u0924\u0925`:#@'=\"\u0926abcdefghi\u0927\u0928\u092a\u092b\u092c\u092d\u092ejklmnopqr\u092f\u0930\u0932\u0933\u0935\u0936\u200c~stuvwxyz\u0937\u0938\u0939[\u093c\u093d\u093e\u093f\u0940\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u0949\u094a]\u094b\u094c{ABCDEFGHI\u094d\u0950\u0951\u0952\ufffd\ufffd}JKLMNOPQR\u0960\u0961\u0962\u0963\u0964\u0965\\\u200dSTUVWXYZ\u0966\u0967\u0968\u0969\u096a\u096b0123456789\u096c\u096d\u096e\u096f\u0970\u009f",
|
||||
Aliases: []string{"ibm-1137_P100-1999", "ibm-1137"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1140_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00a2.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df!$*);\u00ac-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5~stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae^\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be[]\u00af\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1140_P100-1997", "ibm-1140", "IBM01140", "CCSID01140", "CP01140", "cp1140", "ebcdic-us-37+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1141_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2{\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00c4.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec~\u00dc$*);^-/\u00c2[\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00f6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#\u00a7'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5\u00dfstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9@\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e4ABCDEFGHI\u00ad\u00f4\u00a6\u00f2\u00f3\u00f5\u00fcJKLMNOPQR\u00b9\u00fb}\u00f9\u00fa\u00ff\u00d6\u00f7STUVWXYZ\u00b2\u00d4\\\u00d2\u00d3\u00d50123456789\u00b3\u00db]\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1141_P100-1997", "ibm-1141", "IBM01141", "CCSID01141", "CP01141", "cp1141", "ebcdic-de-273+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1142_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3}\u00e7\u00f1#.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u20ac\u00c5*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3$\u00c7\u00d1\u00f8,%_>?\u00a6\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:\u00c6\u00d8'=\"@abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba{\u00b8[]\u00b5\u00fcstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e6ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5\u00e5JKLMNOPQR\u00b9\u00fb~\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1142_P100-1997", "ibm-1142", "IBM01142", "CCSID01142", "CP01142", "cp1142", "ebcdic-dk-277+euro", "ebcdic-no-277+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1143_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2{\u00e0\u00e1\u00e3}\u00e7\u00f1\u00a7.<(+!&`\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u20ac\u00c5*);^-/\u00c2#\u00c0\u00c1\u00c3$\u00c7\u00d1\u00f6,%_>?\u00f8\\\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00e9:\u00c4\u00d6'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6]\u00b5\u00fcstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9[\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e4ABCDEFGHI\u00ad\u00f4\u00a6\u00f2\u00f3\u00f5\u00e5JKLMNOPQR\u00b9\u00fb~\u00f9\u00fa\u00ff\u00c9\u00f7STUVWXYZ\u00b2\u00d4@\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1143_P100-1997", "ibm-1143", "IBM01143", "CCSID01143", "CP01143", "cp1143", "ebcdic-fi-278+euro", "ebcdic-se-278+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1144_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4{\u00e1\u00e3\u00e5\\\u00f1\u00b0.<(+!&]\u00ea\u00eb}\u00ed\u00ee\u00ef~\u00df\u00e9$*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00f2,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00f9:\u00a3\u00a7'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1[jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5\u00ecstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2#\u00a5\u00b7\u00a9@\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e0ABCDEFGHI\u00ad\u00f4\u00f6\u00a6\u00f3\u00f5\u00e8JKLMNOPQR\u00b9\u00fb\u00fc`\u00fa\u00ff\u00e7\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1144_P100-1997", "ibm-1144", "IBM01144", "CCSID01144", "CP01144", "cp1144", "ebcdic-it-280+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1145_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00a6[.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df]$*);\u00ac-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7#\u00f1,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:\u00d1@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5\u00a8stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be^!\u00af~\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1145_P100-1997", "ibm-1145", "IBM01145", "CCSID01145", "CP01145", "cp1145", "ebcdic-es-284+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1146_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1$.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df!\u00a3*);\u00ac-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5\u00afstuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2[\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be^]~\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1146_P100-1997", "ibm-1146", "IBM01146", "CCSID01146", "CP01146", "cp1146", "ebcdic-gb-285+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1147_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4@\u00e1\u00e3\u00e5\\\u00f1\u00b0.<(+!&{\u00ea\u00eb}\u00ed\u00ee\u00ef\u00ec\u00df\u00a7$*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00f9,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00b5:\u00a3\u00e0'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1[jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac`\u00a8stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2#\u00a5\u00b7\u00a9]\u00b6\u00bc\u00bd\u00be\u00ac|\u00af~\u00b4\u00d7\u00e9ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5\u00e8JKLMNOPQR\u00b9\u00fb\u00fc\u00a6\u00fa\u00ff\u00e7\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1147_P100-1997", "ibm-1147", "IBM01147", "CCSID01147", "CP01147", "cp1147", "ebcdic-fr-297+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1148_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1[.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df]$*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5~stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1148_P100-1997", "ibm-1148", "IBM01148", "CCSID01148", "CP01148", "cp1148", "ebcdic-international-500+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1149_P100-1997",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00de.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u00c6$*);\u00d6-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00f0:#\u00d0'=\"\u00d8abcdefghi\u00ab\u00bb`\u00fd{\u00b1\u00b0jklmnopqr\u00aa\u00ba}\u00b8]\u20ac\u00b5\u00f6stuvwxyz\u00a1\u00bf@\u00dd[\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\\\u00d7\u00feABCDEFGHI\u00ad\u00f4~\u00f2\u00f3\u00f5\u00e6JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\u00b4\u00f7STUVWXYZ\u00b2\u00d4^\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1149_P100-1997", "ibm-1149", "IBM01149", "CCSID01149", "CP01149", "cp1149", "ebcdic-is-871+euro"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1153_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u0163\u00e1\u0103\u010d\u00e7\u0107[.<(+!&\u00e9\u0119\u00eb\u016f\u00ed\u00ee\u013e\u013a\u00df]$*);^-/\u00c2\u00c4\u02dd\u00c1\u0102\u010c\u00c7\u0106|,%_>?\u02c7\u00c9\u0118\u00cb\u016e\u00cd\u00ce\u013d\u0139`:#@'=\"\u02d8abcdefghi\u015b\u0148\u0111\u00fd\u0159\u015f\u00b0jklmnopqr\u0142\u0144\u0161\u00b8\u02db\u20ac\u0105~stuvwxyz\u015a\u0147\u0110\u00dd\u0158\u015e\u02d9\u0104\u017c\u0162\u017b\u00a7\u017e\u017a\u017d\u0179\u0141\u0143\u0160\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u0155\u00f3\u0151}JKLMNOPQR\u011a\u0171\u00fc\u0165\u00fa\u011b\\\u00f7STUVWXYZ\u010f\u00d4\u00d6\u0154\u00d3\u01500123456789\u010e\u0170\u00dc\u0164\u00da\u009f",
|
||||
Aliases: []string{"ibm-1153_P100-1999", "ibm-1153"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1154_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0452\u0453\u0451\u0454\u0455\u0456\u0457\u0458[.<(+!&\u0459\u045a\u045b\u045c\u045e\u045f\u042a\u2116\u0402]$*);^-/\u0403\u0401\u0404\u0405\u0406\u0407\u0408\u0409|,%_>?\u040a\u040b\u040c\u00ad\u040e\u040f\u044e\u0430\u0431`:#@'=\"\u0446abcdefghi\u0434\u0435\u0444\u0433\u0445\u0438\u0439jklmnopqr\u043a\u043b\u043c\u043d\u043e\u043f\u044f~stuvwxyz\u0440\u0441\u0442\u0443\u0436\u0432\u044c\u044b\u0437\u0448\u044d\u0449\u0447\u044a\u042e\u0410\u0411\u0426\u0414\u0415\u0424\u0413{ABCDEFGHI\u0425\u0418\u0419\u041a\u041b\u041c}JKLMNOPQR\u041d\u041e\u041f\u042f\u0420\u0421\\\u20acSTUVWXYZ\u0422\u0423\u0416\u0412\u042c\u042b0123456789\u0417\u0428\u042d\u0429\u0427\u009f",
|
||||
Aliases: []string{"ibm-1154_P100-1999", "ibm-1154"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1155_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5{\u00f1\u00c7.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u011e\u0130*);^-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5[\u00d1\u015f,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u0131:\u00d6\u015e'=\u00dc\u00d8abcdefghi\u00ab\u00bb}`\u00a6\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5\u00f6stuvwxyz\u00a1\u00bf]$@\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e7ABCDEFGHI\u00ad\u00f4~\u00f2\u00f3\u00f5\u011fJKLMNOPQR\u00b9\u00fb\\\u00f9\u00fa\u00ff\u00fc\u00f7STUVWXYZ\u00b2\u00d4#\u00d2\u00d3\u00d50123456789\u00b3\u00db\"\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1155_P100-1999", "ibm-1155"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1156_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0161\u00e4\u0105\u012f\u016b\u00e5\u0113\u017e\u00a2.<(+|&\u00e9\u0119\u0117\u010d\u0173\u201e\u201c\u0123\u00df!$*);\u00ac-/\u0160\u00c4\u0104\u012e\u016a\u00c5\u0112\u017d\u00a6,%_>?\u00f8\u00c9\u0118\u0116\u010c\u0172\u012a\u013b\u0122`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u0101\u017c\u0144\u00b1\u00b0jklmnopqr\u0156\u0157\u00e6\u0137\u00c6\u20ac\u00b5~stuvwxyz\u201d\u017a\u0100\u017b\u0143\u00ae^\u00a3\u012b\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be[]\u0179\u0136\u013c\u00d7{ABCDEFGHI\u00ad\u014d\u00f6\u0146\u00f3\u00f5}JKLMNOPQR\u00b9\u0107\u00fc\u0142\u015b\u2019\\\u00f7STUVWXYZ\u00b2\u014c\u00d6\u0145\u00d3\u00d50123456789\u00b3\u0106\u00dc\u0141\u015a\u009f",
|
||||
Aliases: []string{"ibm-1156_P100-1999", "ibm-1156"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1157_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2{\u00e0\u00e1\u00e3}\u00e7\u00f1\u00a7.<(+!&`\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df\u20ac\u00c5*);^-/\u00c2#\u00c0\u00c1\u00c3$\u00c7\u00d1\u00f6,%_>?\u00f8\\\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc\u00e9:\u00c4\u00d6'=\"\u00d8abcdefghi\u00ab\u00bb\u0161\u00fd\u017e\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6]\u00b5\u00fcstuvwxyz\u00a1\u00bf\u0160\u00dd\u017d\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9[\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u00a8\u00b4\u00d7\u00e4ABCDEFGHI\u00ad\u00f4\u00a6\u00f2\u00f3\u00f5\u00e5JKLMNOPQR\u00b9\u00fb~\u00f9\u00fa\u00ff\u00c9\u00f7STUVWXYZ\u00b2\u00d4@\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1157_P100-1999", "ibm-1157"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1158_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0452\u0491\u0451\u0454\u0455\u0456\u0457\u0458[.<(+!&\u0459\u045a\u045b\u045c\u045e\u045f\u042a\u2116\u0402]$*);^-/\u0490\u0401\u0404\u0405\u0406\u0407\u0408\u0409|,%_>?\u040a\u040b\u040c\u00ad\u040e\u040f\u044e\u0430\u0431`:#@'=\"\u0446abcdefghi\u0434\u0435\u0444\u0433\u0445\u0438\u0439jklmnopqr\u043a\u043b\u043c\u043d\u043e\u043f\u044f~stuvwxyz\u0440\u0441\u0442\u0443\u0436\u0432\u044c\u044b\u0437\u0448\u044d\u0449\u0447\u044a\u042e\u0410\u0411\u0426\u0414\u0415\u0424\u0413{ABCDEFGHI\u0425\u0418\u0419\u041a\u041b\u041c}JKLMNOPQR\u041d\u041e\u041f\u042f\u0420\u0421\\\u20acSTUVWXYZ\u0422\u0423\u0416\u0412\u042c\u042b0123456789\u0417\u0428\u042d\u0429\u0427\u009f",
|
||||
Aliases: []string{"ibm-1158_P100-1999", "ibm-1158"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1160_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07[\u00a2.<(+|&\u0e48\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e]!$*);\u00ac-/\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15^\u00a6,%_>?\u0e3f\u0e4e\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c`:#@'=\"\u0e4fabcdefghi\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e5ajklmnopqr\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e5b~stuvwxyz\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34{ABCDEFGHI\u0e49\u0e35\u0e36\u0e37\u0e38\u0e39}JKLMNOPQR\u0e3a\u0e40\u0e41\u0e42\u0e43\u0e44\\\u0e4aSTUVWXYZ\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a0123456789\u0e4b\u0e4c\u0e4d\u0e4b\u20ac\u009f",
|
||||
Aliases: []string{"ibm-1160_P100-1999", "ibm-1160"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1162_P100-1999",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u20ac\u0081\u0082\u0083\u0084\u2026\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\ufffd\ufffd\ufffd\ufffd\u0e3f\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e5a\u0e5b\ufffd\ufffd\ufffd\ufffd",
|
||||
Aliases: []string{"ibm-1162_P100-1999", "ibm-1162"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1164_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u0103\u00e5\u00e7\u00f1[.<(+!&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u0303\u00df]$*);^-/\u00c2\u00c4\u00c0\u00c1\u0102\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u20ab`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u0111\u0309\u0300\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u0152\u00c6\u20ac\u00b5~stuvwxyz\u00a1\u00bf\u0110\u0323\u0301\u00ae\u00a2\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be\u00ac|\u00af\u0153\u0178\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u01b0\u00f3\u01a1}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u01af\u00d3\u01a00123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ibm-1164_P100-1999", "ibm-1164"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-4517_P100-2005",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\ufe7c\ufe7d\u0640\u200b\ufe80\ufe81\ufe82\ufe83\u00b0.<(+!&\ufe84\ufe85\ufffd\ufffd\ufe8b\ufe8d\ufe8e\ufe8f\ufe91\u00a7$*);^-/\ufe93\ufe95\ufe97\ufe99\ufe9b\ufe9d\ufe9f\ufea1\u00fa,%_>?\ufea3\ufea5\ufea7\ufea9\ufeab\ufead\ufeaf\ufeb1\ufeb3\u00a3:\u00b5\u00e1'=\"\ufeb5abcdefghi\ufeb7\ufeb9\ufebb\ufebd\ufebf\ufec3\ufec7jklmnopqr\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\u00a8stuvwxyz\ufed0\ufed1\ufed3\ufed5\ufed7\ufed9\ufedb\ufedd\ufef5\ufef6\ufef7\ufef8\ufffd\ufffd\ufefb\ufefc\ufedf\ufee1\ufee3\ufee5\ufee7\ufee9\u00e9ABCDEFGHI\u00ad\ufeeb\ufffd\ufeec\ufffd\ufeed\u00e8JKLMNOPQR\ufeef\ufef0\ufef1\ufef2\ufef3\ufffd\u00e7\u2007STUVWXYZ\u00f7\u060c\ufffd\u00d7\u061f\u061b0123456789\ufffd\ufffd\ufffd\ufffd\ufffd\u009f",
|
||||
Aliases: []string{"ibm-4517_P100-2005", "ibm-4517"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-4899_P100-1998",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd$.<(+|\u05d0\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\u00a2*);\u00ac-/\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd,%_>?\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd:#@'=\"\ufffd\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\ufffd\ufffd\u20ac\ufffd\u20aa\ufffd\ufffd\ufffd\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdABCDEFGHI\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdJKLMNOPQR\ufffd\u202d\u202e\u202c\ufffd\ufffd\ufffd\ufffdSTUVWXYZ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\ufffd\u202a\u202b\u200e\u200f\u009f",
|
||||
Aliases: []string{"ibm-4899_P100-1998", "ibm-4899"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-4909_P100-1999",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0\u2018\u2019\u00a3\u20ac\ufffd\u00a6\u00a7\u00a8\u00a9\ufffd\u00ab\u00ac\u00ad\ufffd\u2015\u00b0\u00b1\u00b2\u00b3\u00b4\u0385\u0386\u0387\u0388\u0389\u038a\u00bb\u038c\u00bd\u038e\u038f\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\ufffd\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\ufffd",
|
||||
Aliases: []string{"ibm-4909_P100-1999", "ibm-4909"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-4971_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399[.<(+!&\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3]$*);^-/\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab|,%_>?\u00a8\u0386\u0388\u0389\u00a0\u038a\u038c\u038e\u038f`:#@'=\"\u0385abcdefghi\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u00b0jklmnopqr\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u00b4~stuvwxyz\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u00a3\u03ac\u03ad\u03ae\u03ca\u03af\u03cc\u03cd\u03cb\u03ce\u03c2\u03c4\u03c5\u03c6\u03c7\u03c8{ABCDEFGHI\u00ad\u03c9\u0390\u03b0\u2018\u2015}JKLMNOPQR\u00b1\u00bd\ufffd\u0387\u2019\u00a6\\\ufffdSTUVWXYZ\u00b2\u00a7\ufffd\ufffd\u00ab\u00ac0123456789\u00b3\u00a9\u20ac\ufffd\u00bb\u009f",
|
||||
Aliases: []string{"ibm-4971_P100-1999", "ibm-4971"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-5123_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \ufffd\uff61\uff62\uff63\uff64\uff65\uff66\uff67\uff68\u00a2.<(+|&\uff69\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\uff70\uff71!$*);\u00ac-/\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\ufffd,%_>?\uff7a\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82`:#@'=\"\ufffdabcdefghi\uff83\uff84\uff85\uff86\uff87\uff88\ufffdjklmnopqr\uff89\uff8a\uff8b\uff8c\uff8d\uff8e\u203e~stuvwxyz\uff8f\uff90\uff91[\uff92\uff93^\u00a3\u00a5\uff94\uff95\uff96\uff97\uff98\uff99\uff9a\uff9b\uff9c\uff9d]\uff9e\uff9f{ABCDEFGHI\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd}JKLMNOPQR\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\\\u20acSTUVWXYZ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\ufffd\ufffd\ufffd\ufffd\ufffd\u009f",
|
||||
Aliases: []string{"ibm-5123_P100-1999", "ibm-5123"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-8482_P100-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \uff61\uff62\uff63\uff64\uff65\uff66\uff67\uff68\uff69\u00a3.<(+|&\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\ufffd\uff70\ufffd!\u00a5*);\u00ac-/abcdefgh\ufffd,%_>?[ijklmnop`:#@'=\"]\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7aq\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89r\ufffd\uff8a\uff8b\uff8c~\u203e\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95s\uff96\uff97\uff98\uff99^\u00a2\\tuvwxyz\uff9a\uff9b\uff9c\uff9d\uff9e\uff9f{ABCDEFGHI\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd}JKLMNOPQR\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd$\u20acSTUVWXYZ\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\ufffd\ufffd\ufffd\ufffd\ufffd\u009f",
|
||||
Aliases: []string{"ibm-8482_P100-1999", "ibm-8482"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-9067_X100-2005",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399[.<(+!&\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3]$*);^-/\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab|,%_>?\u00a8\u0386\u0388\u0389\u00a0\u038a\u038c\u038e\u038f`:#@'=\"\u0385abcdefghi\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u00b0jklmnopqr\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u00b4~stuvwxyz\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u00a3\u03ac\u03ad\u03ae\u03ca\u03af\u03cc\u03cd\u03cb\u03ce\u03c2\u03c4\u03c5\u03c6\u03c7\u03c8{ABCDEFGHI\u00ad\u03c9\u0390\u03b0\u2018\u2015}JKLMNOPQR\u00b1\u00bd\ufffd\u0387\u2019\u00a6\\\u20afSTUVWXYZ\u00b2\u00a7\u037a\ufffd\u00ab\u00ac0123456789\u00b3\u00a9\u20ac\ufffd\u00bb\u009f",
|
||||
Aliases: []string{"ibm-9067_X100-2005", "ibm-9067"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-12712_P100-1998",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u00a2.<(+|&\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1!$*);\u00ac-/\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u00a6,%_>?\ufffd\u05ea\ufffd\ufffd\u00a0\ufffd\ufffd\ufffd\u2017`:#@'=\"\ufffdabcdefghi\u00ab\u00bb\ufffd\ufffd\ufffd\u00b1\u00b0jklmnopqr\ufffd\ufffd\u20ac\u00b8\u20aa\u00a4\u00b5~stuvwxyz\ufffd\ufffd\ufffd\ufffd\ufffd\u00ae^\u00a3\u00a5\u2022\u00a9\u00a7\u00b6\u00bc\u00bd\u00be[]\u203e\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\ufffd\ufffd\ufffd\ufffd\ufffd}JKLMNOPQR\u00b9\u202d\u202e\u202c\ufffd\ufffd\\\u00f7STUVWXYZ\u00b2\ufffd\ufffd\ufffd\ufffd\ufffd0123456789\u00b3\u202a\u202b\u200e\u200f\u009f",
|
||||
Aliases: []string{"ibm-12712_P100-1998", "ibm-12712", "ebcdic-he"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-16804_X110-1999",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\u0085\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u0651\ufe7d\u0640\u200b\u0621\u0622\ufe82\u0623\u00a2.<(+|&\ufe84\u0624\ufffd\ufffd\u0626\u0627\ufe8e\u0628\ufe91!$*);\u00ac-/\u0629\u062a\ufe97\u062b\ufe9b\u062c\ufe9f\u062d\u00a6,%_>?\ufea3\u062e\ufea7\u062f\u0630\u0631\u0632\u0633\ufeb3\u060c:#@'=\"\u0634abcdefghi\ufeb7\u0635\ufebb\u0636\ufebf\u0637\u0638jklmnopqr\u0639\ufeca\ufecb\ufecc\u063a\ufece\ufecf\u00f7stuvwxyz\ufed0\u0641\ufed3\u0642\ufed7\u0643\ufedb\u0644\ufef5\ufef6\ufef7\ufef8\ufffd\ufffd\ufefb\ufefc\ufedf\u0645\ufee3\u0646\ufee7\u0647\u061bABCDEFGHI\u00ad\ufeeb\ufffd\ufeec\ufffd\u0648\u061fJKLMNOPQR\u0649\ufef0\u064a\ufef2\ufef3\u0660\u00d7\u2007STUVWXYZ\u0661\u0662\ufffd\u0663\u0664\u06650123456789\u20ac\u0666\u0667\u0668\u0669\u009f",
|
||||
Aliases: []string{"ibm-16804_X110-1999", "ibm-16804", "ebcdic-ar"},
|
||||
},
|
||||
{
|
||||
Name: "KOI8-R",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u2500\u2502\u250c\u2510\u2514\u2518\u251c\u2524\u252c\u2534\u253c\u2580\u2584\u2588\u258c\u2590\u2591\u2592\u2593\u2320\u25a0\u2219\u221a\u2248\u2264\u2265\u00a0\u2321\u00b0\u00b2\u00b7\u00f7\u2550\u2551\u2552\u0451\u2553\u2554\u2555\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f\u2560\u2561\u0401\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569\u256a\u256b\u256c\u00a9\u044e\u0430\u0431\u0446\u0434\u0435\u0444\u0433\u0445\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u044f\u0440\u0441\u0442\u0443\u0436\u0432\u044c\u044b\u0437\u0448\u044d\u0449\u0447\u044a\u042e\u0410\u0411\u0426\u0414\u0415\u0424\u0413\u0425\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u042f\u0420\u0421\u0422\u0423\u0416\u0412\u042c\u042b\u0417\u0428\u042d\u0429\u0427\u042a",
|
||||
Aliases: []string{"csKOI8R"},
|
||||
},
|
||||
{
|
||||
Name: "KOI8-U",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u2500\u2502\u250c\u2510\u2514\u2518\u251c\u2524\u252c\u2534\u253c\u2580\u2584\u2588\u258c\u2590\u2591\u2592\u2593\u2320\u25a0\u2219\u221a\u2248\u2264\u2265\u00a0\u2321\u00b0\u00b2\u00b7\u00f7\u2550\u2551\u2552\u0451\u0454\u2554\u0456\u0457\u2557\u2558\u2559\u255a\u255b\u0491\u255d\u255e\u255f\u2560\u2561\u0401\u0404\u2563\u0406\u0407\u2566\u2567\u2568\u2569\u256a\u0490\u256c\u00a9\u044e\u0430\u0431\u0446\u0434\u0435\u0444\u0433\u0445\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u044f\u0440\u0441\u0442\u0443\u0436\u0432\u044c\u044b\u0437\u0448\u044d\u0449\u0447\u044a\u042e\u0410\u0411\u0426\u0414\u0415\u0424\u0413\u0425\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u042f\u0420\u0421\u0422\u0423\u0416\u0412\u042c\u042b\u0417\u0428\u042d\u0429\u0427\u042a",
|
||||
},
|
||||
{
|
||||
Name: "ibm-1051_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\ufffd\u00c0\u00c2\u00c8\u00ca\u00cb\u00ce\u00cf\u00b4`\u02c6\u00a8\u02dc\u00d9\u00db\u00a3\u203e\u00dd\u00fd\u02da\u00c7\u00e7\u00d1\u00f1\u00a1\u00bf\u00a4\u00a3\u00a5\u00a7\u0192\u00a2\u00e2\u00ea\u00f4\u00fb\u00e1\u00e9\u00f3\u00fa\u00e0\u00e8\u00f2\u00f9\u00e4\u00eb\u00f6\u00fc\u00c5\u00ee\u00d8\u00c6\u00e5\u00ed\u00f8\u00e6\u00c4\u00ec\u00d6\u00dc\u00c9\u00ef\u00df\u00d4\u00c1\u00c3\u00e3\u00d0\u00f0\u00cd\u00cc\u00d3\u00d2\u00d5\u00f5\u0160\u0161\u00da\u0178\u00ff\u00de\u00fe\u00b7\u03bc\u00b6\u00be-\u00bc\u00bd\u00aa\u00ba\u00ab\u25a0\u00bb\u00b1\ufffd",
|
||||
Aliases: []string{"ibm-1051_P100-1995", "ibm-1051", "hp-roman8", "roman8", "r8", "csHPRoman8"},
|
||||
},
|
||||
{
|
||||
Name: "ibm-1276_P100-1995",
|
||||
SubstitutionChar: '?',
|
||||
Repertoire: "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&\u2019()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\u2018abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\ufffd\u00a1\u00a2\u00a3\u2044\u00a5\u0192\u00a7\u00a4'\u201c\u00ab\u2039\u203a\ufb01\ufb02\ufffd\u2013\u2020\u2021\u00b7\ufffd\u00b6\u2022\u201a\u201e\u201d\u00bb\u2026\u2030\ufffd\u00bf\ufffd`\u00b4\u02c6\u02dc\u00af\u02d8\u02d9\u00a8\ufffd\u02da\u00b8\ufffd\u02dd\u02db\u02c7\u2014\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u00c6\ufffd\u00aa\ufffd\ufffd\ufffd\ufffd\u0141\u00d8\u0152\u00ba\ufffd\ufffd\ufffd\ufffd\ufffd\u00e6\ufffd\ufffd\ufffd\u0131\ufffd\ufffd\u0142\u00f8\u0153\u00df\ufffd\ufffd\ufffd\ufffd",
|
||||
Aliases: []string{"ibm-1276_P100-1995", "ibm-1276", "Adobe-Standard-Encoding", "csAdobeStandardEncoding"},
|
||||
},
|
||||
{
|
||||
Name: "ebcdic-xml-us",
|
||||
SubstitutionChar: 0x6f,
|
||||
Repertoire: "\x00\x01\x02\x03\u009c\t\u0086\x7f\u0097\u008d\u008e\v\f\r\x0e\x0f\x10\x11\x12\x13\u009d\n\b\u0087\x18\x19\u0092\u008f\x1c\x1d\x1e\x1f\u0080\u0081\u0082\u0083\u0084\n\x17\x1b\u0088\u0089\u008a\u008b\u008c\x05\x06\a\u0090\u0091\x16\u0093\u0094\u0095\u0096\x04\u0098\u0099\u009a\u009b\x14\x15\u009e\x1a \u00a0\u00e2\u00e4\u00e0\u00e1\u00e3\u00e5\u00e7\u00f1\u00a2.<(+|&\u00e9\u00ea\u00eb\u00e8\u00ed\u00ee\u00ef\u00ec\u00df!$*);\u00ac-/\u00c2\u00c4\u00c0\u00c1\u00c3\u00c5\u00c7\u00d1\u00a6,%_>?\u00f8\u00c9\u00ca\u00cb\u00c8\u00cd\u00ce\u00cf\u00cc`:#@'=\"\u00d8abcdefghi\u00ab\u00bb\u00f0\u00fd\u00fe\u00b1\u00b0jklmnopqr\u00aa\u00ba\u00e6\u00b8\u00c6\u20ac\u00b5~stuvwxyz\u00a1\u00bf\u00d0\u00dd\u00de\u00ae^\u00a3\u00a5\u00b7\u00a9\u00a7\u00b6\u00bc\u00bd\u00be[]\u00af\u00a8\u00b4\u00d7{ABCDEFGHI\u00ad\u00f4\u00f6\u00f2\u00f3\u00f5}JKLMNOPQR\u00b9\u00fb\u00fc\u00f9\u00fa\u00ff\\\u00f7STUVWXYZ\u00b2\u00d4\u00d6\u00d2\u00d3\u00d50123456789\u00b3\u00db\u00dc\u00d9\u00da\u009f",
|
||||
Aliases: []string{"ebcdic-xml-us"},
|
||||
},
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// Converters for ASCII and ISO-8859-1
|
||||
|
||||
func init() {
|
||||
for i := 0; i < len(asciiCharsets); i++ {
|
||||
RegisterCharset(&asciiCharsets[i])
|
||||
}
|
||||
}
|
||||
|
||||
var asciiCharsets = []Charset{
|
||||
{
|
||||
Name: "US-ASCII",
|
||||
NewDecoder: func() Decoder { return decodeASCIIRune },
|
||||
NewEncoder: func() Encoder { return encodeASCIIRune },
|
||||
Aliases: []string{"ASCII", "US", "ISO646-US", "IBM367", "cp367", "ANSI_X3.4-1968", "iso-ir-6", "ANSI_X3.4-1986", "ISO_646.irv:1991", "csASCII"},
|
||||
},
|
||||
{
|
||||
Name: "ISO-8859-1",
|
||||
NewDecoder: func() Decoder { return decodeLatin1Rune },
|
||||
NewEncoder: func() Encoder { return encodeLatin1Rune },
|
||||
Aliases: []string{"latin1", "ISO Latin 1", "IBM819", "cp819", "ISO_8859-1:1987", "iso-ir-100", "l1", "csISOLatin1"},
|
||||
},
|
||||
}
|
||||
|
||||
func decodeASCIIRune(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
if b > 127 {
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
return rune(b), 1, SUCCESS
|
||||
}
|
||||
|
||||
func encodeASCIIRune(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if c < 128 {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func decodeLatin1Rune(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
return rune(p[0]), 1, SUCCESS
|
||||
}
|
||||
|
||||
func encodeLatin1Rune(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if c < 256 {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,89 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// Converters for Big 5 encoding.
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "Big5",
|
||||
Aliases: []string{"csBig5"},
|
||||
NewDecoder: func() Decoder {
|
||||
return decodeBig5Rune
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
big5Once.Do(reverseBig5Table)
|
||||
return encodeBig5Rune
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func decodeBig5Rune(p []byte) (r rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
if b < 128 {
|
||||
return rune(b), 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
c := int(p[0])<<8 + int(p[1])
|
||||
c = int(big5ToUnicode[c])
|
||||
if c > 0 {
|
||||
return rune(c), 2, SUCCESS
|
||||
}
|
||||
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func encodeBig5Rune(p []byte, r rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if r < 128 {
|
||||
p[0] = byte(r)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if r < 0x10000 {
|
||||
c := unicodeToBig5[r]
|
||||
if c > 0 {
|
||||
p[0] = byte(c >> 8)
|
||||
p[1] = byte(c)
|
||||
return 2, SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
var big5Once sync.Once
|
||||
|
||||
var unicodeToBig5 []uint16
|
||||
|
||||
func reverseBig5Table() {
|
||||
unicodeToBig5 = make([]uint16, 65536)
|
||||
|
||||
for big5, unicode := range big5ToUnicode {
|
||||
if unicode > 0 {
|
||||
unicodeToBig5[unicode] = uint16(big5)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
// This package is a character-set conversion library for Go.
|
||||
//
|
||||
// (DEPRECATED: use code.google.com/p/go.text/encoding, perhaps along with
|
||||
// code.google.com/p/go.net/html/charset)
|
||||
package mahonia
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Status is the type for the status return value from a Decoder or Encoder.
|
||||
type Status int
|
||||
|
||||
const (
|
||||
// SUCCESS means that the character was converted with no problems.
|
||||
SUCCESS = Status(iota)
|
||||
|
||||
// INVALID_CHAR means that the source contained invalid bytes, or that the character
|
||||
// could not be represented in the destination encoding.
|
||||
// The Encoder or Decoder should have output a substitute character.
|
||||
INVALID_CHAR
|
||||
|
||||
// NO_ROOM means there were not enough input bytes to form a complete character,
|
||||
// or there was not enough room in the output buffer to write a complete character.
|
||||
// No bytes were written, and no internal state was changed in the Encoder or Decoder.
|
||||
NO_ROOM
|
||||
|
||||
// STATE_ONLY means that bytes were read or written indicating a state transition,
|
||||
// but no actual character was processed. (Examples: byte order marks, ISO-2022 escape sequences)
|
||||
STATE_ONLY
|
||||
)
|
||||
|
||||
// A Decoder is a function that decodes a character set, one character at a time.
|
||||
// It works much like utf8.DecodeRune, but has an aditional status return value.
|
||||
type Decoder func(p []byte) (c rune, size int, status Status)
|
||||
|
||||
// An Encoder is a function that encodes a character set, one character at a time.
|
||||
// It works much like utf8.EncodeRune, but has an additional status return value.
|
||||
type Encoder func(p []byte, c rune) (size int, status Status)
|
||||
|
||||
// A Charset represents a character set that can be converted, and contains functions
|
||||
// to create Converters to encode and decode strings in that character set.
|
||||
type Charset struct {
|
||||
// Name is the character set's canonical name.
|
||||
Name string
|
||||
|
||||
// Aliases returns a list of alternate names.
|
||||
Aliases []string
|
||||
|
||||
// NewDecoder returns a Decoder to convert from the charset to Unicode.
|
||||
NewDecoder func() Decoder
|
||||
|
||||
// NewEncoder returns an Encoder to convert from Unicode to the charset.
|
||||
NewEncoder func() Encoder
|
||||
}
|
||||
|
||||
// The charsets are stored in charsets under their canonical names.
|
||||
var charsets = make(map[string]*Charset)
|
||||
|
||||
// aliases maps their aliases to their canonical names.
|
||||
var aliases = make(map[string]string)
|
||||
|
||||
// simplifyName converts a name to lower case and removes non-alphanumeric characters.
|
||||
// This is how the names are used as keys to the maps.
|
||||
func simplifyName(name string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, c := range name {
|
||||
switch {
|
||||
case unicode.IsDigit(c):
|
||||
buf.WriteRune(c)
|
||||
case unicode.IsLetter(c):
|
||||
buf.WriteRune(unicode.ToLower(c))
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// RegisterCharset adds a charset to the charsetMap.
|
||||
func RegisterCharset(cs *Charset) {
|
||||
name := cs.Name
|
||||
charsets[name] = cs
|
||||
aliases[simplifyName(name)] = name
|
||||
for _, alias := range cs.Aliases {
|
||||
aliases[simplifyName(alias)] = name
|
||||
}
|
||||
}
|
||||
|
||||
// GetCharset fetches a charset by name.
|
||||
// If the name is not found, it returns nil.
|
||||
func GetCharset(name string) *Charset {
|
||||
return charsets[aliases[simplifyName(name)]]
|
||||
}
|
||||
|
||||
// NewDecoder returns a Decoder to decode the named charset.
|
||||
// If the name is not found, it returns nil.
|
||||
func NewDecoder(name string) Decoder {
|
||||
cs := GetCharset(name)
|
||||
if cs == nil {
|
||||
return nil
|
||||
}
|
||||
return cs.NewDecoder()
|
||||
}
|
||||
|
||||
// NewEncoder returns an Encoder to encode the named charset.
|
||||
func NewEncoder(name string) Encoder {
|
||||
cs := GetCharset(name)
|
||||
if cs == nil {
|
||||
return nil
|
||||
}
|
||||
return cs.NewEncoder()
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ConvertString converts a string from UTF-8 to e's encoding.
|
||||
func (e Encoder) ConvertString(s string) string {
|
||||
dest := make([]byte, len(s)+10)
|
||||
destPos := 0
|
||||
|
||||
for _, rune := range s {
|
||||
retry:
|
||||
size, status := e(dest[destPos:], rune)
|
||||
|
||||
if status == NO_ROOM {
|
||||
newDest := make([]byte, len(dest)*2)
|
||||
copy(newDest, dest)
|
||||
dest = newDest
|
||||
goto retry
|
||||
}
|
||||
|
||||
if status == STATE_ONLY {
|
||||
destPos += size
|
||||
goto retry
|
||||
}
|
||||
|
||||
destPos += size
|
||||
}
|
||||
|
||||
return string(dest[:destPos])
|
||||
}
|
||||
|
||||
// ConvertString converts a string from d's encoding to UTF-8.
|
||||
func (d Decoder) ConvertString(s string) string {
|
||||
bytes := []byte(s)
|
||||
runes := make([]rune, len(s))
|
||||
destPos := 0
|
||||
|
||||
for len(bytes) > 0 {
|
||||
c, size, status := d(bytes)
|
||||
|
||||
if status == STATE_ONLY {
|
||||
bytes = bytes[size:]
|
||||
continue
|
||||
}
|
||||
|
||||
if status == NO_ROOM {
|
||||
c = 0xfffd
|
||||
size = len(bytes)
|
||||
status = INVALID_CHAR
|
||||
}
|
||||
|
||||
bytes = bytes[size:]
|
||||
runes[destPos] = c
|
||||
destPos++
|
||||
}
|
||||
|
||||
return string(runes[:destPos])
|
||||
}
|
||||
|
||||
// ConvertStringOK converts a string from UTF-8 to e's encoding. It also
|
||||
// returns a boolean indicating whether every character was converted
|
||||
// successfully.
|
||||
func (e Encoder) ConvertStringOK(s string) (result string, ok bool) {
|
||||
dest := make([]byte, len(s)+10)
|
||||
destPos := 0
|
||||
ok = true
|
||||
|
||||
for i, r := range s {
|
||||
// The following test is copied from utf8.ValidString.
|
||||
if r == utf8.RuneError && ok {
|
||||
_, size := utf8.DecodeRuneInString(s[i:])
|
||||
if size == 1 {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
retry:
|
||||
size, status := e(dest[destPos:], r)
|
||||
|
||||
switch status {
|
||||
case NO_ROOM:
|
||||
newDest := make([]byte, len(dest)*2)
|
||||
copy(newDest, dest)
|
||||
dest = newDest
|
||||
goto retry
|
||||
|
||||
case STATE_ONLY:
|
||||
destPos += size
|
||||
goto retry
|
||||
|
||||
case INVALID_CHAR:
|
||||
ok = false
|
||||
}
|
||||
|
||||
destPos += size
|
||||
}
|
||||
|
||||
return string(dest[:destPos]), ok
|
||||
}
|
||||
|
||||
// ConvertStringOK converts a string from d's encoding to UTF-8.
|
||||
// It also returns a boolean indicating whether every character was converted
|
||||
// successfully.
|
||||
func (d Decoder) ConvertStringOK(s string) (result string, ok bool) {
|
||||
bytes := []byte(s)
|
||||
runes := make([]rune, len(s))
|
||||
destPos := 0
|
||||
ok = true
|
||||
|
||||
for len(bytes) > 0 {
|
||||
c, size, status := d(bytes)
|
||||
|
||||
switch status {
|
||||
case STATE_ONLY:
|
||||
bytes = bytes[size:]
|
||||
continue
|
||||
|
||||
case NO_ROOM:
|
||||
c = 0xfffd
|
||||
size = len(bytes)
|
||||
ok = false
|
||||
|
||||
case INVALID_CHAR:
|
||||
ok = false
|
||||
}
|
||||
|
||||
bytes = bytes[size:]
|
||||
runes[destPos] = c
|
||||
destPos++
|
||||
}
|
||||
|
||||
return string(runes[:destPos]), ok
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Converters for Microsoft's version of the EUC-JP encoding
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "cp51932",
|
||||
Aliases: []string{"windows-51932"},
|
||||
NewDecoder: func() Decoder {
|
||||
return decodeCP51932
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
msJISTable.Reverse()
|
||||
return encodeCP51932
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func decodeCP51932(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
switch {
|
||||
case b < 0x80:
|
||||
return rune(b), 1, SUCCESS
|
||||
|
||||
case b == 0x8e:
|
||||
if len(p) < 2 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
b2 := p[1]
|
||||
if b2 < 0xa1 || b2 > 0xdf {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
return rune(b2) + (0xff61 - 0xa1), 2, SUCCESS
|
||||
|
||||
case 0xa1 <= b && b <= 0xfe:
|
||||
return msJISTable.DecodeHigh(p)
|
||||
}
|
||||
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func encodeCP51932(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c < 0x80 {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c > 0xffff {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
if 0xff61 <= c && c <= 0xff9f {
|
||||
p[0] = 0x8e
|
||||
p[1] = byte(c - (0xff61 - 0xa1))
|
||||
return 2, SUCCESS
|
||||
}
|
||||
|
||||
return msJISTable.EncodeHigh(p, c)
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// decoding HTML entities
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// EntityDecoder returns a Decoder that decodes HTML character entities.
|
||||
// If there is no valid character entity at the current position, it returns INVALID_CHAR.
|
||||
// So it needs to be combined with another Decoder via FallbackDecoder.
|
||||
func EntityDecoder() Decoder {
|
||||
var leftover rune // leftover rune from two-rune entity
|
||||
return func(p []byte) (r rune, size int, status Status) {
|
||||
if leftover != 0 {
|
||||
r = leftover
|
||||
leftover = 0
|
||||
return r, 0, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) == 0 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
if p[0] != '&' {
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
if len(p) < 3 {
|
||||
return 0, 1, NO_ROOM
|
||||
}
|
||||
|
||||
r, size, status = 0xfffd, 1, INVALID_CHAR
|
||||
n := 1 // number of bytes read so far
|
||||
|
||||
if p[n] == '#' {
|
||||
n++
|
||||
c := p[n]
|
||||
hex := false
|
||||
if c == 'x' || c == 'X' {
|
||||
hex = true
|
||||
n++
|
||||
}
|
||||
|
||||
var x rune
|
||||
for n < len(p) {
|
||||
c = p[n]
|
||||
n++
|
||||
if hex {
|
||||
if '0' <= c && c <= '9' {
|
||||
x = 16*x + rune(c) - '0'
|
||||
continue
|
||||
} else if 'a' <= c && c <= 'f' {
|
||||
x = 16*x + rune(c) - 'a' + 10
|
||||
continue
|
||||
} else if 'A' <= c && c <= 'F' {
|
||||
x = 16*x + rune(c) - 'A' + 10
|
||||
continue
|
||||
}
|
||||
} else if '0' <= c && c <= '9' {
|
||||
x = 10*x + rune(c) - '0'
|
||||
continue
|
||||
}
|
||||
if c != ';' {
|
||||
n--
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if n == len(p) && p[n-1] != ';' {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
size = n
|
||||
if p[n-1] == ';' {
|
||||
n--
|
||||
}
|
||||
if hex {
|
||||
n--
|
||||
}
|
||||
n--
|
||||
// Now n is the number of actual digits read.
|
||||
if n == 0 {
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
if 0x80 <= x && x <= 0x9F {
|
||||
// Replace characters from Windows-1252 with UTF-8 equivalents.
|
||||
x = replacementTable[x-0x80]
|
||||
} else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF {
|
||||
// Replace invalid characters with the replacement character.
|
||||
return 0xfffd, size, INVALID_CHAR
|
||||
}
|
||||
|
||||
r = x
|
||||
status = SUCCESS
|
||||
return
|
||||
}
|
||||
|
||||
// Look for a named entity in EntityList.
|
||||
|
||||
possible := entityList
|
||||
for len(possible) > 0 {
|
||||
if len(p) <= n {
|
||||
leftover = 0
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
c := p[n]
|
||||
|
||||
// Narrow down the selection in possible to those items that have c in the
|
||||
// appropriate byte.
|
||||
first := sort.Search(len(possible), func(i int) bool {
|
||||
e := possible[i].name
|
||||
if len(e) < n {
|
||||
return false
|
||||
}
|
||||
return e[n-1] >= c
|
||||
})
|
||||
possible = possible[first:]
|
||||
last := sort.Search(len(possible), func(i int) bool {
|
||||
return possible[i].name[n-1] > c
|
||||
})
|
||||
possible = possible[:last]
|
||||
|
||||
n++
|
||||
if len(possible) > 0 && len(possible[0].name) == n-1 {
|
||||
r, leftover = possible[0].r1, possible[0].r2
|
||||
size = n
|
||||
status = SUCCESS
|
||||
// but don't return yet, since we need the longest match
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// This table is copied from /src/pkg/html/escape.go in the Go source
|
||||
//
|
||||
// These replacements permit compatibility with old numeric entities that
|
||||
// assumed Windows-1252 encoding.
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
|
||||
var replacementTable = [...]rune{
|
||||
'\u20AC', // First entry is what 0x80 should be replaced with.
|
||||
'\u0081',
|
||||
'\u201A',
|
||||
'\u0192',
|
||||
'\u201E',
|
||||
'\u2026',
|
||||
'\u2020',
|
||||
'\u2021',
|
||||
'\u02C6',
|
||||
'\u2030',
|
||||
'\u0160',
|
||||
'\u2039',
|
||||
'\u0152',
|
||||
'\u008D',
|
||||
'\u017D',
|
||||
'\u008F',
|
||||
'\u0090',
|
||||
'\u2018',
|
||||
'\u2019',
|
||||
'\u201C',
|
||||
'\u201D',
|
||||
'\u2022',
|
||||
'\u2013',
|
||||
'\u2014',
|
||||
'\u02DC',
|
||||
'\u2122',
|
||||
'\u0161',
|
||||
'\u203A',
|
||||
'\u0153',
|
||||
'\u009D',
|
||||
'\u017E',
|
||||
'\u0178', // Last entry is 0x9F.
|
||||
// 0x00->'\uFFFD' is handled programmatically.
|
||||
// 0x0D->'\u000D' is a no-op.
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,102 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Converters for the EUC-JP encoding
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "EUC-JP",
|
||||
Aliases: []string{"extended_unix_code_packed_format_for_japanese", "cseucpkdfmtjapanese"},
|
||||
NewDecoder: func() Decoder {
|
||||
return decodeEucJP
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
jis0208Table.Reverse()
|
||||
jis0212Table.Reverse()
|
||||
return encodeEucJP
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func decodeEucJP(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
switch {
|
||||
case b < 0x80:
|
||||
return rune(b), 1, SUCCESS
|
||||
|
||||
case b == 0x8e:
|
||||
if len(p) < 2 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
b2 := p[1]
|
||||
if b2 < 0xa1 || b2 > 0xdf {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
return rune(b2) + (0xff61 - 0xa1), 2, SUCCESS
|
||||
|
||||
case b == 0x8f:
|
||||
if len(p) < 3 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
c, size, status = jis0212Table.DecodeHigh(p[1:3])
|
||||
if status == SUCCESS {
|
||||
size = 3
|
||||
}
|
||||
return
|
||||
|
||||
case 0xa1 <= b && b <= 0xfe:
|
||||
return jis0208Table.DecodeHigh(p)
|
||||
}
|
||||
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func encodeEucJP(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c < 0x80 {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c > 0xffff {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
if 0xff61 <= c && c <= 0xff9f {
|
||||
p[0] = 0x8e
|
||||
p[1] = byte(c - (0xff61 - 0xa1))
|
||||
return 2, SUCCESS
|
||||
}
|
||||
|
||||
size, status = jis0208Table.EncodeHigh(p, c)
|
||||
if status == SUCCESS {
|
||||
return size, status
|
||||
}
|
||||
|
||||
size, status = jis0212Table.EncodeHigh(p[1:], c)
|
||||
switch status {
|
||||
case SUCCESS:
|
||||
p[0] = 0x8f
|
||||
return size + 1, SUCCESS
|
||||
|
||||
case INVALID_CHAR:
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
return size, status
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,89 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// Converters for the EUC-KR encoding.
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "EUC-KR",
|
||||
Aliases: []string{
|
||||
"ibm-1363",
|
||||
"KS_C_5601-1987",
|
||||
"KS_C_5601-1989",
|
||||
"KSC_5601",
|
||||
"Korean",
|
||||
"iso-ir-149",
|
||||
"cp1363",
|
||||
"5601",
|
||||
"ksc",
|
||||
"windows-949",
|
||||
"ibm-970",
|
||||
"cp970",
|
||||
"970",
|
||||
"cp949",
|
||||
},
|
||||
NewDecoder: func() Decoder {
|
||||
return decodeEucKr
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
eucKrOnce.Do(reverseEucKrTable)
|
||||
return encodeEucKr
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func decodeEucKr(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
if b < 0x80 {
|
||||
return rune(b), 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
euc := int(b)<<8 + int(p[1])
|
||||
c = rune(eucKrToUnicode[euc])
|
||||
|
||||
if c == 0 {
|
||||
return utf8.RuneError, 2, INVALID_CHAR
|
||||
}
|
||||
return c, 2, SUCCESS
|
||||
}
|
||||
|
||||
func encodeEucKr(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c < 0x80 {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c > 0xffff {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
euc := unicodeToEucKr[c]
|
||||
if euc == 0 {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
p[0] = byte(euc >> 8)
|
||||
p[1] = byte(euc)
|
||||
return 2, SUCCESS
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// FallbackDecoder combines a series of Decoders into one.
|
||||
// If the first Decoder returns a status of INVALID_CHAR, the others are tried as well.
|
||||
//
|
||||
// Note: if the text to be decoded ends with a sequence of bytes that is not a valid character in the first charset,
|
||||
// but it could be the beginning of a valid character, the FallbackDecoder will give a status of NO_ROOM instead of
|
||||
// falling back to the other Decoders.
|
||||
func FallbackDecoder(decoders ...Decoder) Decoder {
|
||||
return func(p []byte) (c rune, size int, status Status) {
|
||||
for _, d := range decoders {
|
||||
c, size, status = d(p)
|
||||
if status != INVALID_CHAR {
|
||||
return
|
||||
}
|
||||
}
|
||||
return 0, 1, INVALID_CHAR
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,156 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Converters for GB18030 encoding.
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "GB18030",
|
||||
NewDecoder: func() Decoder {
|
||||
gb18030Once.Do(buildGB18030Tables)
|
||||
return decodeGB18030Rune
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
gb18030Once.Do(buildGB18030Tables)
|
||||
return encodeGB18030Rune
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func decodeGB18030Rune(p []byte) (r rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
if b < 128 {
|
||||
return rune(b), 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if p[0] < 0x81 || p[0] > 0xfe {
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
if p[1] >= 0x40 {
|
||||
// 2-byte character
|
||||
c := uint16(p[0])<<8 + uint16(p[1])
|
||||
r = rune(gbkToUnicode[c])
|
||||
if r == 0 {
|
||||
r = gbkToUnicodeExtra[c]
|
||||
}
|
||||
|
||||
if r != 0 {
|
||||
return r, 2, SUCCESS
|
||||
}
|
||||
} else if p[1] >= 0x30 {
|
||||
// 4-byte character
|
||||
if len(p) < 4 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
if p[2] < 0x81 || p[2] > 0xfe || p[3] < 0x30 || p[3] > 0x39 {
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
code := uint32(p[0])<<24 + uint32(p[1])<<16 + uint32(p[2])<<8 + uint32(p[3])
|
||||
lin := gb18030Linear(code)
|
||||
|
||||
if lin <= maxGB18030Linear {
|
||||
r = rune(gb18030LinearToUnicode[lin])
|
||||
if r != 0 {
|
||||
return r, 4, SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
for _, rng := range gb18030Ranges {
|
||||
if lin >= rng.firstGB && lin <= rng.lastGB {
|
||||
return rng.firstRune + rune(lin) - rune(rng.firstGB), 4, SUCCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func encodeGB18030Rune(p []byte, r rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if r < 128 {
|
||||
p[0] = byte(r)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
var c uint16
|
||||
if r < 0x10000 {
|
||||
c = unicodeToGBK[r]
|
||||
} else {
|
||||
c = unicodeToGBKExtra[r]
|
||||
}
|
||||
|
||||
if c != 0 {
|
||||
p[0] = byte(c >> 8)
|
||||
p[1] = byte(c)
|
||||
return 2, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 4 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if r < 0x10000 {
|
||||
f := unicodeToGB18030[r]
|
||||
if f != 0 {
|
||||
p[0] = byte(f >> 24)
|
||||
p[1] = byte(f >> 16)
|
||||
p[2] = byte(f >> 8)
|
||||
p[3] = byte(f)
|
||||
return 4, SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
for _, rng := range gb18030Ranges {
|
||||
if r >= rng.firstRune && r <= rng.lastRune {
|
||||
lin := rng.firstGB + uint32(r) - uint32(rng.firstRune)
|
||||
p[0] = byte(lin/(10*126*10)) + 0x81
|
||||
p[1] = byte(lin/(126*10)%10) + 0x30
|
||||
p[2] = byte(lin/10%126) + 0x81
|
||||
p[3] = byte(lin%10) + 0x30
|
||||
return 4, SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
p[0] = 0x1a
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
var gb18030Once sync.Once
|
||||
|
||||
// Mapping from gb18039Linear values to Unicode.
|
||||
var gb18030LinearToUnicode []uint16
|
||||
|
||||
var unicodeToGB18030 []uint32
|
||||
|
||||
func buildGB18030Tables() {
|
||||
gb18030LinearToUnicode = make([]uint16, maxGB18030Linear+1)
|
||||
unicodeToGB18030 = make([]uint32, 65536)
|
||||
for _, data := range gb18030Data {
|
||||
gb18030LinearToUnicode[gb18030Linear(data.gb18030)] = data.unicode
|
||||
unicodeToGB18030[data.unicode] = data.gb18030
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,78 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// Converters for GBK encoding.
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "GBK",
|
||||
Aliases: []string{"GB2312"}, // GBK is a superset of GB2312.
|
||||
NewDecoder: func() Decoder {
|
||||
return decodeGBKRune
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
return encodeGBKRune
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func decodeGBKRune(p []byte) (r rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
if b < 128 {
|
||||
return rune(b), 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
c := uint16(p[0])<<8 + uint16(p[1])
|
||||
r = rune(gbkToUnicode[c])
|
||||
if r == 0 {
|
||||
r = gbkToUnicodeExtra[c]
|
||||
}
|
||||
|
||||
if r != 0 {
|
||||
return r, 2, SUCCESS
|
||||
}
|
||||
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func encodeGBKRune(p []byte, r rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if r < 128 {
|
||||
p[0] = byte(r)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
var c uint16
|
||||
if r < 0x10000 {
|
||||
c = unicodeToGBK[r]
|
||||
} else {
|
||||
c = unicodeToGBKExtra[r]
|
||||
}
|
||||
|
||||
if c != 0 {
|
||||
p[0] = byte(c >> 8)
|
||||
p[1] = byte(c)
|
||||
return 2, SUCCESS
|
||||
}
|
||||
|
||||
p[0] = 0x1a
|
||||
return 1, INVALID_CHAR
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// converters for ISO-2022-JP encoding
|
||||
|
||||
const esc = 27
|
||||
|
||||
func init() {
|
||||
type jpEncoding int
|
||||
const (
|
||||
ascii jpEncoding = iota
|
||||
jisX0201Roman
|
||||
jisX0208
|
||||
)
|
||||
|
||||
RegisterCharset(&Charset{
|
||||
Name: "ISO-2022-JP",
|
||||
NewDecoder: func() Decoder {
|
||||
encoding := ascii
|
||||
return func(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
if b == esc {
|
||||
if len(p) < 3 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
switch p[1] {
|
||||
case '(':
|
||||
switch p[2] {
|
||||
case 'B':
|
||||
encoding = ascii
|
||||
return 0, 3, STATE_ONLY
|
||||
|
||||
case 'J':
|
||||
encoding = jisX0201Roman
|
||||
return 0, 3, STATE_ONLY
|
||||
}
|
||||
|
||||
case '$':
|
||||
switch p[2] {
|
||||
case '@', 'B':
|
||||
encoding = jisX0208
|
||||
return 0, 3, STATE_ONLY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch encoding {
|
||||
case ascii:
|
||||
if b > 127 {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
return rune(b), 1, SUCCESS
|
||||
|
||||
case jisX0201Roman:
|
||||
if b > 127 {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
switch b {
|
||||
case '\\':
|
||||
return 0xA5, 1, SUCCESS
|
||||
case '~':
|
||||
return 0x203E, 1, SUCCESS
|
||||
}
|
||||
return rune(b), 1, SUCCESS
|
||||
|
||||
case jisX0208:
|
||||
return jis0208Table.DecodeLow(p)
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
jis0208Table.Reverse()
|
||||
encoding := ascii
|
||||
return func(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c < 128 {
|
||||
if encoding != ascii {
|
||||
if len(p) < 4 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
p[0], p[1], p[2] = esc, '(', 'B'
|
||||
p[3] = byte(c)
|
||||
encoding = ascii
|
||||
return 4, SUCCESS
|
||||
}
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if c > 65535 {
|
||||
return 0, INVALID_CHAR
|
||||
}
|
||||
jis := jis0208Table.FromUnicode[c]
|
||||
if jis == [2]byte{0, 0} && c != rune(jis0208Table.Data[0][0]) {
|
||||
return 0, INVALID_CHAR
|
||||
}
|
||||
|
||||
if encoding != jisX0208 {
|
||||
if len(p) < 3 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
p[0], p[1], p[2] = esc, '$', 'B'
|
||||
encoding = jisX0208
|
||||
return 3, STATE_ONLY
|
||||
}
|
||||
|
||||
p[0] = jis[0] + 0x21
|
||||
p[1] = jis[1] + 0x21
|
||||
return 2, SUCCESS
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
var jis0201ToUnicode = [256]uint16{
|
||||
0x20: 0x0020, // SPACE
|
||||
0x21: 0x0021, // EXCLAMATION MARK
|
||||
0x22: 0x0022, // QUOTATION MARK
|
||||
0x23: 0x0023, // NUMBER SIGN
|
||||
0x24: 0x0024, // DOLLAR SIGN
|
||||
0x25: 0x0025, // PERCENT SIGN
|
||||
0x26: 0x0026, // AMPERSAND
|
||||
0x27: 0x0027, // APOSTROPHE
|
||||
0x28: 0x0028, // LEFT PARENTHESIS
|
||||
0x29: 0x0029, // RIGHT PARENTHESIS
|
||||
0x2A: 0x002A, // ASTERISK
|
||||
0x2B: 0x002B, // PLUS SIGN
|
||||
0x2C: 0x002C, // COMMA
|
||||
0x2D: 0x002D, // HYPHEN-MINUS
|
||||
0x2E: 0x002E, // FULL STOP
|
||||
0x2F: 0x002F, // SOLIDUS
|
||||
0x30: 0x0030, // DIGIT ZERO
|
||||
0x31: 0x0031, // DIGIT ONE
|
||||
0x32: 0x0032, // DIGIT TWO
|
||||
0x33: 0x0033, // DIGIT THREE
|
||||
0x34: 0x0034, // DIGIT FOUR
|
||||
0x35: 0x0035, // DIGIT FIVE
|
||||
0x36: 0x0036, // DIGIT SIX
|
||||
0x37: 0x0037, // DIGIT SEVEN
|
||||
0x38: 0x0038, // DIGIT EIGHT
|
||||
0x39: 0x0039, // DIGIT NINE
|
||||
0x3A: 0x003A, // COLON
|
||||
0x3B: 0x003B, // SEMICOLON
|
||||
0x3C: 0x003C, // LESS-THAN SIGN
|
||||
0x3D: 0x003D, // EQUALS SIGN
|
||||
0x3E: 0x003E, // GREATER-THAN SIGN
|
||||
0x3F: 0x003F, // QUESTION MARK
|
||||
0x40: 0x0040, // COMMERCIAL AT
|
||||
0x41: 0x0041, // LATIN CAPITAL LETTER A
|
||||
0x42: 0x0042, // LATIN CAPITAL LETTER B
|
||||
0x43: 0x0043, // LATIN CAPITAL LETTER C
|
||||
0x44: 0x0044, // LATIN CAPITAL LETTER D
|
||||
0x45: 0x0045, // LATIN CAPITAL LETTER E
|
||||
0x46: 0x0046, // LATIN CAPITAL LETTER F
|
||||
0x47: 0x0047, // LATIN CAPITAL LETTER G
|
||||
0x48: 0x0048, // LATIN CAPITAL LETTER H
|
||||
0x49: 0x0049, // LATIN CAPITAL LETTER I
|
||||
0x4A: 0x004A, // LATIN CAPITAL LETTER J
|
||||
0x4B: 0x004B, // LATIN CAPITAL LETTER K
|
||||
0x4C: 0x004C, // LATIN CAPITAL LETTER L
|
||||
0x4D: 0x004D, // LATIN CAPITAL LETTER M
|
||||
0x4E: 0x004E, // LATIN CAPITAL LETTER N
|
||||
0x4F: 0x004F, // LATIN CAPITAL LETTER O
|
||||
0x50: 0x0050, // LATIN CAPITAL LETTER P
|
||||
0x51: 0x0051, // LATIN CAPITAL LETTER Q
|
||||
0x52: 0x0052, // LATIN CAPITAL LETTER R
|
||||
0x53: 0x0053, // LATIN CAPITAL LETTER S
|
||||
0x54: 0x0054, // LATIN CAPITAL LETTER T
|
||||
0x55: 0x0055, // LATIN CAPITAL LETTER U
|
||||
0x56: 0x0056, // LATIN CAPITAL LETTER V
|
||||
0x57: 0x0057, // LATIN CAPITAL LETTER W
|
||||
0x58: 0x0058, // LATIN CAPITAL LETTER X
|
||||
0x59: 0x0059, // LATIN CAPITAL LETTER Y
|
||||
0x5A: 0x005A, // LATIN CAPITAL LETTER Z
|
||||
0x5B: 0x005B, // LEFT SQUARE BRACKET
|
||||
0x5C: 0x00A5, // YEN SIGN
|
||||
0x5D: 0x005D, // RIGHT SQUARE BRACKET
|
||||
0x5E: 0x005E, // CIRCUMFLEX ACCENT
|
||||
0x5F: 0x005F, // LOW LINE
|
||||
0x60: 0x0060, // GRAVE ACCENT
|
||||
0x61: 0x0061, // LATIN SMALL LETTER A
|
||||
0x62: 0x0062, // LATIN SMALL LETTER B
|
||||
0x63: 0x0063, // LATIN SMALL LETTER C
|
||||
0x64: 0x0064, // LATIN SMALL LETTER D
|
||||
0x65: 0x0065, // LATIN SMALL LETTER E
|
||||
0x66: 0x0066, // LATIN SMALL LETTER F
|
||||
0x67: 0x0067, // LATIN SMALL LETTER G
|
||||
0x68: 0x0068, // LATIN SMALL LETTER H
|
||||
0x69: 0x0069, // LATIN SMALL LETTER I
|
||||
0x6A: 0x006A, // LATIN SMALL LETTER J
|
||||
0x6B: 0x006B, // LATIN SMALL LETTER K
|
||||
0x6C: 0x006C, // LATIN SMALL LETTER L
|
||||
0x6D: 0x006D, // LATIN SMALL LETTER M
|
||||
0x6E: 0x006E, // LATIN SMALL LETTER N
|
||||
0x6F: 0x006F, // LATIN SMALL LETTER O
|
||||
0x70: 0x0070, // LATIN SMALL LETTER P
|
||||
0x71: 0x0071, // LATIN SMALL LETTER Q
|
||||
0x72: 0x0072, // LATIN SMALL LETTER R
|
||||
0x73: 0x0073, // LATIN SMALL LETTER S
|
||||
0x74: 0x0074, // LATIN SMALL LETTER T
|
||||
0x75: 0x0075, // LATIN SMALL LETTER U
|
||||
0x76: 0x0076, // LATIN SMALL LETTER V
|
||||
0x77: 0x0077, // LATIN SMALL LETTER W
|
||||
0x78: 0x0078, // LATIN SMALL LETTER X
|
||||
0x79: 0x0079, // LATIN SMALL LETTER Y
|
||||
0x7A: 0x007A, // LATIN SMALL LETTER Z
|
||||
0x7B: 0x007B, // LEFT CURLY BRACKET
|
||||
0x7C: 0x007C, // VERTICAL LINE
|
||||
0x7D: 0x007D, // RIGHT CURLY BRACKET
|
||||
0x7E: 0x203E, // OVERLINE
|
||||
0xA1: 0xFF61, // HALFWIDTH IDEOGRAPHIC FULL STOP
|
||||
0xA2: 0xFF62, // HALFWIDTH LEFT CORNER BRACKET
|
||||
0xA3: 0xFF63, // HALFWIDTH RIGHT CORNER BRACKET
|
||||
0xA4: 0xFF64, // HALFWIDTH IDEOGRAPHIC COMMA
|
||||
0xA5: 0xFF65, // HALFWIDTH KATAKANA MIDDLE DOT
|
||||
0xA6: 0xFF66, // HALFWIDTH KATAKANA LETTER WO
|
||||
0xA7: 0xFF67, // HALFWIDTH KATAKANA LETTER SMALL A
|
||||
0xA8: 0xFF68, // HALFWIDTH KATAKANA LETTER SMALL I
|
||||
0xA9: 0xFF69, // HALFWIDTH KATAKANA LETTER SMALL U
|
||||
0xAA: 0xFF6A, // HALFWIDTH KATAKANA LETTER SMALL E
|
||||
0xAB: 0xFF6B, // HALFWIDTH KATAKANA LETTER SMALL O
|
||||
0xAC: 0xFF6C, // HALFWIDTH KATAKANA LETTER SMALL YA
|
||||
0xAD: 0xFF6D, // HALFWIDTH KATAKANA LETTER SMALL YU
|
||||
0xAE: 0xFF6E, // HALFWIDTH KATAKANA LETTER SMALL YO
|
||||
0xAF: 0xFF6F, // HALFWIDTH KATAKANA LETTER SMALL TU
|
||||
0xB0: 0xFF70, // HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
|
||||
0xB1: 0xFF71, // HALFWIDTH KATAKANA LETTER A
|
||||
0xB2: 0xFF72, // HALFWIDTH KATAKANA LETTER I
|
||||
0xB3: 0xFF73, // HALFWIDTH KATAKANA LETTER U
|
||||
0xB4: 0xFF74, // HALFWIDTH KATAKANA LETTER E
|
||||
0xB5: 0xFF75, // HALFWIDTH KATAKANA LETTER O
|
||||
0xB6: 0xFF76, // HALFWIDTH KATAKANA LETTER KA
|
||||
0xB7: 0xFF77, // HALFWIDTH KATAKANA LETTER KI
|
||||
0xB8: 0xFF78, // HALFWIDTH KATAKANA LETTER KU
|
||||
0xB9: 0xFF79, // HALFWIDTH KATAKANA LETTER KE
|
||||
0xBA: 0xFF7A, // HALFWIDTH KATAKANA LETTER KO
|
||||
0xBB: 0xFF7B, // HALFWIDTH KATAKANA LETTER SA
|
||||
0xBC: 0xFF7C, // HALFWIDTH KATAKANA LETTER SI
|
||||
0xBD: 0xFF7D, // HALFWIDTH KATAKANA LETTER SU
|
||||
0xBE: 0xFF7E, // HALFWIDTH KATAKANA LETTER SE
|
||||
0xBF: 0xFF7F, // HALFWIDTH KATAKANA LETTER SO
|
||||
0xC0: 0xFF80, // HALFWIDTH KATAKANA LETTER TA
|
||||
0xC1: 0xFF81, // HALFWIDTH KATAKANA LETTER TI
|
||||
0xC2: 0xFF82, // HALFWIDTH KATAKANA LETTER TU
|
||||
0xC3: 0xFF83, // HALFWIDTH KATAKANA LETTER TE
|
||||
0xC4: 0xFF84, // HALFWIDTH KATAKANA LETTER TO
|
||||
0xC5: 0xFF85, // HALFWIDTH KATAKANA LETTER NA
|
||||
0xC6: 0xFF86, // HALFWIDTH KATAKANA LETTER NI
|
||||
0xC7: 0xFF87, // HALFWIDTH KATAKANA LETTER NU
|
||||
0xC8: 0xFF88, // HALFWIDTH KATAKANA LETTER NE
|
||||
0xC9: 0xFF89, // HALFWIDTH KATAKANA LETTER NO
|
||||
0xCA: 0xFF8A, // HALFWIDTH KATAKANA LETTER HA
|
||||
0xCB: 0xFF8B, // HALFWIDTH KATAKANA LETTER HI
|
||||
0xCC: 0xFF8C, // HALFWIDTH KATAKANA LETTER HU
|
||||
0xCD: 0xFF8D, // HALFWIDTH KATAKANA LETTER HE
|
||||
0xCE: 0xFF8E, // HALFWIDTH KATAKANA LETTER HO
|
||||
0xCF: 0xFF8F, // HALFWIDTH KATAKANA LETTER MA
|
||||
0xD0: 0xFF90, // HALFWIDTH KATAKANA LETTER MI
|
||||
0xD1: 0xFF91, // HALFWIDTH KATAKANA LETTER MU
|
||||
0xD2: 0xFF92, // HALFWIDTH KATAKANA LETTER ME
|
||||
0xD3: 0xFF93, // HALFWIDTH KATAKANA LETTER MO
|
||||
0xD4: 0xFF94, // HALFWIDTH KATAKANA LETTER YA
|
||||
0xD5: 0xFF95, // HALFWIDTH KATAKANA LETTER YU
|
||||
0xD6: 0xFF96, // HALFWIDTH KATAKANA LETTER YO
|
||||
0xD7: 0xFF97, // HALFWIDTH KATAKANA LETTER RA
|
||||
0xD8: 0xFF98, // HALFWIDTH KATAKANA LETTER RI
|
||||
0xD9: 0xFF99, // HALFWIDTH KATAKANA LETTER RU
|
||||
0xDA: 0xFF9A, // HALFWIDTH KATAKANA LETTER RE
|
||||
0xDB: 0xFF9B, // HALFWIDTH KATAKANA LETTER RO
|
||||
0xDC: 0xFF9C, // HALFWIDTH KATAKANA LETTER WA
|
||||
0xDD: 0xFF9D, // HALFWIDTH KATAKANA LETTER N
|
||||
0xDE: 0xFF9E, // HALFWIDTH KATAKANA VOICED SOUND MARK
|
||||
0xDF: 0xFF9F, // HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,88 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// A kutenTable holds the data for a double-byte character set, arranged by ku
|
||||
// (区, zone) and ten (点, position). These can be converted to various actual
|
||||
// encoding schemes.
|
||||
type kutenTable struct {
|
||||
// Data[ku][ten] is the unicode value for the character at that zone and
|
||||
// position.
|
||||
Data [94][94]uint16
|
||||
|
||||
// FromUnicode holds the ku and ten for each Unicode code point.
|
||||
// It is not available until Reverse() has been called.
|
||||
FromUnicode [][2]byte
|
||||
|
||||
// once is used to synchronize the generation of FromUnicode.
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// Reverse generates FromUnicode.
|
||||
func (t *kutenTable) Reverse() {
|
||||
t.once.Do(func() {
|
||||
t.FromUnicode = make([][2]byte, 65536)
|
||||
for ku := range t.Data {
|
||||
for ten, unicode := range t.Data[ku] {
|
||||
t.FromUnicode[unicode] = [2]byte{byte(ku), byte(ten)}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// DecodeLow decodes a character from an encoding that does not have the high
|
||||
// bit set.
|
||||
func (t *kutenTable) DecodeLow(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) < 2 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
ku := p[0] - 0x21
|
||||
ten := p[1] - 0x21
|
||||
if ku > 93 || ten > 93 {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
u := t.Data[ku][ten]
|
||||
if u == 0 {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
return rune(u), 2, SUCCESS
|
||||
}
|
||||
|
||||
// DecodeHigh decodes a character from an encoding that has the high bit set.
|
||||
func (t *kutenTable) DecodeHigh(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) < 2 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
ku := p[0] - 0xa1
|
||||
ten := p[1] - 0xa1
|
||||
if ku > 93 || ten > 93 {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
u := t.Data[ku][ten]
|
||||
if u == 0 {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
return rune(u), 2, SUCCESS
|
||||
}
|
||||
|
||||
// EncodeHigh encodes a character in an encoding that has the high bit set.
|
||||
func (t *kutenTable) EncodeHigh(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) < 2 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
if c > 0xffff {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
kuten := t.FromUnicode[c]
|
||||
if kuten == [2]byte{0, 0} && c != rune(t.Data[0][0]) {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
p[0] = kuten[0] + 0xa1
|
||||
p[1] = kuten[1] + 0xa1
|
||||
return 2, SUCCESS
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// Generic converters for multibyte character sets.
|
||||
|
||||
// An mbcsTrie contains the data to convert from the character set to Unicode.
|
||||
// If a character would be encoded as "\x01\x02\x03", its unicode value would be found at t.children[1].children[2].children[3].rune
|
||||
// children either is nil or has 256 elements.
|
||||
type mbcsTrie struct {
|
||||
// For leaf nodes, the Unicode character that is represented.
|
||||
char rune
|
||||
|
||||
// For non-leaf nodes, the trie to decode the remainder of the character.
|
||||
children []mbcsTrie
|
||||
}
|
||||
|
||||
// A MBCSTable holds the data to convert to and from Unicode.
|
||||
type MBCSTable struct {
|
||||
toUnicode mbcsTrie
|
||||
fromUnicode map[rune]string
|
||||
}
|
||||
|
||||
// AddCharacter adds a character to the table. rune is its Unicode code point,
|
||||
// and bytes contains the bytes used to encode it in the character set.
|
||||
func (table *MBCSTable) AddCharacter(c rune, bytes string) {
|
||||
if table.fromUnicode == nil {
|
||||
table.fromUnicode = make(map[rune]string)
|
||||
}
|
||||
|
||||
table.fromUnicode[c] = bytes
|
||||
|
||||
trie := &table.toUnicode
|
||||
for i := 0; i < len(bytes); i++ {
|
||||
if trie.children == nil {
|
||||
trie.children = make([]mbcsTrie, 256)
|
||||
}
|
||||
|
||||
b := bytes[i]
|
||||
trie = &trie.children[b]
|
||||
}
|
||||
|
||||
trie.char = c
|
||||
}
|
||||
|
||||
func (table *MBCSTable) Decoder() Decoder {
|
||||
return func(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if p[0] == 0 {
|
||||
return 0, 1, SUCCESS
|
||||
}
|
||||
|
||||
trie := &table.toUnicode
|
||||
for trie.char == 0 {
|
||||
if trie.children == nil {
|
||||
return 0xfffd, 1, INVALID_CHAR
|
||||
}
|
||||
if len(p) < size+1 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
trie = &trie.children[p[size]]
|
||||
size++
|
||||
}
|
||||
|
||||
c = trie.char
|
||||
status = SUCCESS
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (table *MBCSTable) Encoder() Encoder {
|
||||
return func(p []byte, c rune) (size int, status Status) {
|
||||
bytes := table.fromUnicode[c]
|
||||
if bytes == "" {
|
||||
if len(p) > 0 {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
} else {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
}
|
||||
|
||||
if len(p) < len(bytes) {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
return copy(p, bytes), SUCCESS
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,151 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// This file is based on bufio.Reader in the Go standard library,
|
||||
// which has the following copyright notice:
|
||||
|
||||
// Copyright 2009 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.
|
||||
|
||||
import (
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBufSize = 4096
|
||||
)
|
||||
|
||||
// Reader implements character-set decoding for an io.Reader object.
|
||||
type Reader struct {
|
||||
buf []byte
|
||||
rd io.Reader
|
||||
decode Decoder
|
||||
r, w int
|
||||
err error
|
||||
}
|
||||
|
||||
// NewReader creates a new Reader that uses the receiver to decode text.
|
||||
func (d Decoder) NewReader(rd io.Reader) *Reader {
|
||||
b := new(Reader)
|
||||
b.buf = make([]byte, defaultBufSize)
|
||||
b.rd = rd
|
||||
b.decode = d
|
||||
return b
|
||||
}
|
||||
|
||||
// fill reads a new chunk into the buffer.
|
||||
func (b *Reader) fill() {
|
||||
// Slide existing data to beginning.
|
||||
if b.r > 0 {
|
||||
copy(b.buf, b.buf[b.r:b.w])
|
||||
b.w -= b.r
|
||||
b.r = 0
|
||||
}
|
||||
|
||||
// Read new data.
|
||||
n, e := b.rd.Read(b.buf[b.w:])
|
||||
b.w += n
|
||||
if e != nil {
|
||||
b.err = e
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads data into p.
|
||||
// It returns the number of bytes read into p.
|
||||
// It calls Read at most once on the underlying Reader,
|
||||
// hence n may be less than len(p).
|
||||
// At EOF, the count will be zero and err will be os.EOF.
|
||||
func (b *Reader) Read(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
filled := false
|
||||
if n == 0 {
|
||||
return 0, b.err
|
||||
}
|
||||
if b.w == b.r {
|
||||
if b.err != nil {
|
||||
return 0, b.err
|
||||
}
|
||||
if n > len(b.buf) {
|
||||
// Large read, empty buffer.
|
||||
// Allocate a larger buffer for efficiency.
|
||||
b.buf = make([]byte, n)
|
||||
}
|
||||
b.fill()
|
||||
filled = true
|
||||
if b.w == b.r {
|
||||
return 0, b.err
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < n {
|
||||
rune, size, status := b.decode(b.buf[b.r:b.w])
|
||||
|
||||
if status == STATE_ONLY {
|
||||
b.r += size
|
||||
continue
|
||||
}
|
||||
|
||||
if status == NO_ROOM {
|
||||
if b.err != nil {
|
||||
rune = 0xfffd
|
||||
size = b.w - b.r
|
||||
if size == 0 {
|
||||
break
|
||||
}
|
||||
status = INVALID_CHAR
|
||||
} else if filled {
|
||||
break
|
||||
} else {
|
||||
b.fill()
|
||||
filled = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if i+utf8.RuneLen(rune) > n {
|
||||
break
|
||||
}
|
||||
|
||||
b.r += size
|
||||
if rune < 128 {
|
||||
p[i] = byte(rune)
|
||||
i++
|
||||
} else {
|
||||
i += utf8.EncodeRune(p[i:], rune)
|
||||
}
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// ReadRune reads a single Unicode character and returns the
|
||||
// rune and its size in bytes.
|
||||
func (b *Reader) ReadRune() (c rune, size int, err error) {
|
||||
read:
|
||||
c, size, status := b.decode(b.buf[b.r:b.w])
|
||||
|
||||
if status == NO_ROOM && b.err == nil {
|
||||
b.fill()
|
||||
goto read
|
||||
}
|
||||
|
||||
if status == STATE_ONLY {
|
||||
b.r += size
|
||||
goto read
|
||||
}
|
||||
|
||||
if b.r == b.w {
|
||||
return 0, 0, b.err
|
||||
}
|
||||
|
||||
if status == NO_ROOM {
|
||||
c = 0xfffd
|
||||
size = b.w - b.r
|
||||
status = INVALID_CHAR
|
||||
}
|
||||
|
||||
b.r += size
|
||||
return c, size, nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,88 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// Converters for the Shift-JIS encoding.
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "Shift_JIS",
|
||||
Aliases: []string{"MS_Kanji", "csShiftJIS", "SJIS", "ibm-943", "windows-31j", "cp932", "windows-932"},
|
||||
NewDecoder: func() Decoder {
|
||||
return decodeSJIS
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
shiftJISOnce.Do(reverseShiftJISTable)
|
||||
return encodeSJIS
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func decodeSJIS(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
b := p[0]
|
||||
if b < 0x80 {
|
||||
return rune(b), 1, SUCCESS
|
||||
}
|
||||
|
||||
if 0xa1 <= b && b <= 0xdf {
|
||||
return rune(b) + (0xff61 - 0xa1), 1, SUCCESS
|
||||
}
|
||||
|
||||
if b == 0x80 || b == 0xa0 {
|
||||
return utf8.RuneError, 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
jis := int(b)<<8 + int(p[1])
|
||||
c = rune(shiftJISToUnicode[jis])
|
||||
|
||||
if c == 0 {
|
||||
return utf8.RuneError, 2, INVALID_CHAR
|
||||
}
|
||||
return c, 2, SUCCESS
|
||||
}
|
||||
|
||||
func encodeSJIS(p []byte, c rune) (size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c < 0x80 {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if 0xff61 <= c && c <= 0xff9f {
|
||||
// half-width katakana
|
||||
p[0] = byte(c - (0xff61 - 0xa1))
|
||||
return 1, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 2 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
if c > 0xffff {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
jis := unicodeToShiftJIS[c]
|
||||
if jis == 0 {
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
p[0] = byte(jis >> 8)
|
||||
p[1] = byte(jis)
|
||||
return 2, SUCCESS
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
// Converters for TCVN3 encoding.
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
onceTCVN3 sync.Once
|
||||
dataTCVN3 = struct {
|
||||
UnicodeToWord map[rune][2]byte
|
||||
WordToUnicode [256]struct {
|
||||
r rune
|
||||
m *[256]rune
|
||||
}
|
||||
}{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
p := new(Charset)
|
||||
p.Name = "TCVN3"
|
||||
p.NewDecoder = func() Decoder {
|
||||
onceTCVN3.Do(buildTCVN3Tables)
|
||||
return decodeTCVN3
|
||||
}
|
||||
p.NewEncoder = func() Encoder {
|
||||
onceTCVN3.Do(buildTCVN3Tables)
|
||||
return encodeTCVN3
|
||||
}
|
||||
RegisterCharset(p)
|
||||
}
|
||||
|
||||
func decodeTCVN3(p []byte) (rune, int, Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
item := &dataTCVN3.WordToUnicode[p[0]]
|
||||
if item.m != nil && len(p) > 1 {
|
||||
if r := item.m[p[1]]; r != 0 {
|
||||
return r, 2, SUCCESS
|
||||
}
|
||||
}
|
||||
if item.r != 0 {
|
||||
return item.r, 1, SUCCESS
|
||||
}
|
||||
if p[0] < 0x80 {
|
||||
return rune(p[0]), 1, SUCCESS
|
||||
}
|
||||
return '?', 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func encodeTCVN3(p []byte, c rune) (int, Status) {
|
||||
if len(p) == 0 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
if c < rune(0x80) {
|
||||
p[0] = byte(c)
|
||||
return 1, SUCCESS
|
||||
}
|
||||
if v, ok := dataTCVN3.UnicodeToWord[c]; ok {
|
||||
if v[1] != 0 {
|
||||
if len(p) < 2 {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
p[0] = v[0]
|
||||
p[1] = v[1]
|
||||
return 2, SUCCESS
|
||||
} else {
|
||||
p[0] = v[0]
|
||||
return 1, SUCCESS
|
||||
}
|
||||
}
|
||||
p[0] = '?'
|
||||
return 1, INVALID_CHAR
|
||||
}
|
||||
|
||||
func buildTCVN3Tables() {
|
||||
dataTCVN3.UnicodeToWord = map[rune][2]byte{
|
||||
// one byte
|
||||
0x00C2: {0xA2, 0x00},
|
||||
0x00CA: {0xA3, 0x00},
|
||||
0x00D4: {0xA4, 0x00},
|
||||
0x00E0: {0xB5, 0x00},
|
||||
0x00E1: {0xB8, 0x00},
|
||||
0x00E2: {0xA9, 0x00},
|
||||
0x00E3: {0xB7, 0x00},
|
||||
0x00E8: {0xCC, 0x00},
|
||||
0x00E9: {0xD0, 0x00},
|
||||
0x00EA: {0xAA, 0x00},
|
||||
0x00EC: {0xD7, 0x00},
|
||||
0x00ED: {0xDD, 0x00},
|
||||
0x00F2: {0xDF, 0x00},
|
||||
0x00F3: {0xE3, 0x00},
|
||||
0x00F4: {0xAB, 0x00},
|
||||
0x00F5: {0xE2, 0x00},
|
||||
0x00F9: {0xEF, 0x00},
|
||||
0x00FA: {0xF3, 0x00},
|
||||
0x00FD: {0xFD, 0x00},
|
||||
0x0102: {0xA1, 0x00},
|
||||
0x0103: {0xA8, 0x00},
|
||||
0x0110: {0xA7, 0x00},
|
||||
0x0111: {0xAE, 0x00},
|
||||
0x0129: {0xDC, 0x00},
|
||||
0x0169: {0xF2, 0x00},
|
||||
0x01A0: {0xA5, 0x00},
|
||||
0x01A1: {0xAC, 0x00},
|
||||
0x01AF: {0xA6, 0x00},
|
||||
0x01B0: {0xAD, 0x00},
|
||||
0x1EA1: {0xB9, 0x00},
|
||||
0x1EA3: {0xB6, 0x00},
|
||||
0x1EA5: {0xCA, 0x00},
|
||||
0x1EA7: {0xC7, 0x00},
|
||||
0x1EA9: {0xC8, 0x00},
|
||||
0x1EAB: {0xC9, 0x00},
|
||||
0x1EAD: {0xCB, 0x00},
|
||||
0x1EAF: {0xBE, 0x00},
|
||||
0x1EB1: {0xBB, 0x00},
|
||||
0x1EB3: {0xBC, 0x00},
|
||||
0x1EB5: {0xBD, 0x00},
|
||||
0x1EB7: {0xC6, 0x00},
|
||||
0x1EB9: {0xD1, 0x00},
|
||||
0x1EBB: {0xCE, 0x00},
|
||||
0x1EBD: {0xCF, 0x00},
|
||||
0x1EBF: {0xD5, 0x00},
|
||||
0x1EC1: {0xD2, 0x00},
|
||||
0x1EC3: {0xD3, 0x00},
|
||||
0x1EC5: {0xD4, 0x00},
|
||||
0x1EC7: {0xD6, 0x00},
|
||||
0x1EC9: {0xD8, 0x00},
|
||||
0x1ECB: {0xDE, 0x00},
|
||||
0x1ECD: {0xE4, 0x00},
|
||||
0x1ECF: {0xE1, 0x00},
|
||||
0x1ED1: {0xE8, 0x00},
|
||||
0x1ED3: {0xE5, 0x00},
|
||||
0x1ED5: {0xE6, 0x00},
|
||||
0x1ED7: {0xE7, 0x00},
|
||||
0x1ED9: {0xE9, 0x00},
|
||||
0x1EDB: {0xED, 0x00},
|
||||
0x1EDD: {0xEA, 0x00},
|
||||
0x1EDF: {0xEB, 0x00},
|
||||
0x1EE1: {0xEC, 0x00},
|
||||
0x1EE3: {0xEE, 0x00},
|
||||
0x1EE5: {0xF4, 0x00},
|
||||
0x1EE7: {0xF1, 0x00},
|
||||
0x1EE9: {0xF8, 0x00},
|
||||
0x1EEB: {0xF5, 0x00},
|
||||
0x1EED: {0xF6, 0x00},
|
||||
0x1EEF: {0xF7, 0x00},
|
||||
0x1EF1: {0xF9, 0x00},
|
||||
0x1EF3: {0xFA, 0x00},
|
||||
0x1EF5: {0xFE, 0x00},
|
||||
0x1EF7: {0xFB, 0x00},
|
||||
0x1EF9: {0xFC, 0x00},
|
||||
// two bytes
|
||||
0x00C0: {0x41, 0xB5},
|
||||
0x00C1: {0x41, 0xB8},
|
||||
0x00C3: {0x41, 0xB7},
|
||||
0x00C8: {0x45, 0xCC},
|
||||
0x00C9: {0x45, 0xD0},
|
||||
0x00CC: {0x49, 0xD7},
|
||||
0x00CD: {0x49, 0xDD},
|
||||
0x00D2: {0x4F, 0xDF},
|
||||
0x00D3: {0x4F, 0xE3},
|
||||
0x00D5: {0x4F, 0xE2},
|
||||
0x00D9: {0x55, 0xEF},
|
||||
0x00DA: {0x55, 0xF3},
|
||||
0x00DD: {0x59, 0xFD},
|
||||
0x0128: {0x49, 0xDC},
|
||||
0x0168: {0x55, 0xF2},
|
||||
0x1EA0: {0x41, 0xB9},
|
||||
0x1EA2: {0x41, 0xB6},
|
||||
0x1EA4: {0xA2, 0xCA},
|
||||
0x1EA6: {0xA2, 0xC7},
|
||||
0x1EA8: {0xA2, 0xC8},
|
||||
0x1EAA: {0xA2, 0xC9},
|
||||
0x1EAC: {0xA2, 0xCB},
|
||||
0x1EAE: {0xA1, 0xBE},
|
||||
0x1EB0: {0xA1, 0xBB},
|
||||
0x1EB2: {0xA1, 0xBC},
|
||||
0x1EB4: {0xA1, 0xBD},
|
||||
0x1EB6: {0xA1, 0xC6},
|
||||
0x1EB8: {0x45, 0xD1},
|
||||
0x1EBA: {0x45, 0xCE},
|
||||
0x1EBC: {0x45, 0xCF},
|
||||
0x1EBE: {0xA3, 0xD5},
|
||||
0x1EC0: {0xA3, 0xD2},
|
||||
0x1EC2: {0xA3, 0xD3},
|
||||
0x1EC4: {0xA3, 0xD4},
|
||||
0x1EC6: {0xA3, 0xD6},
|
||||
0x1EC8: {0x49, 0xD8},
|
||||
0x1ECA: {0x49, 0xDE},
|
||||
0x1ECC: {0x4F, 0xE4},
|
||||
0x1ECE: {0x4F, 0xE1},
|
||||
0x1ED0: {0xA4, 0xE8},
|
||||
0x1ED2: {0xA4, 0xE5},
|
||||
0x1ED4: {0xA4, 0xE6},
|
||||
0x1ED6: {0xA4, 0xE7},
|
||||
0x1ED8: {0xA4, 0xE9},
|
||||
0x1EDA: {0xA5, 0xED},
|
||||
0x1EDC: {0xA5, 0xEA},
|
||||
0x1EDE: {0xA5, 0xEB},
|
||||
0x1EE0: {0xA5, 0xEC},
|
||||
0x1EE2: {0xA5, 0xEE},
|
||||
0x1EE4: {0x55, 0xF4},
|
||||
0x1EE6: {0x55, 0xF1},
|
||||
0x1EE8: {0xA6, 0xF8},
|
||||
0x1EEA: {0xA6, 0xF5},
|
||||
0x1EEC: {0xA6, 0xF6},
|
||||
0x1EEE: {0xA6, 0xF7},
|
||||
0x1EF0: {0xA6, 0xF9},
|
||||
0x1EF2: {0x59, 0xFA},
|
||||
0x1EF4: {0x59, 0xFE},
|
||||
0x1EF6: {0x59, 0xFB},
|
||||
0x1EF8: {0x59, 0xFC},
|
||||
}
|
||||
for r, b := range dataTCVN3.UnicodeToWord {
|
||||
item := &dataTCVN3.WordToUnicode[b[0]]
|
||||
if b[1] == 0 {
|
||||
item.r = r
|
||||
} else {
|
||||
if item.m == nil {
|
||||
item.m = new([256]rune)
|
||||
}
|
||||
item.m[b[1]] = r
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// Translate enables a Decoder to implement go-charset's Translator interface.
|
||||
func (d Decoder) Translate(data []byte, eof bool) (n int, cdata []byte, err error) {
|
||||
cdata = make([]byte, len(data)+1)
|
||||
destPos := 0
|
||||
|
||||
for n < len(data) {
|
||||
rune, size, status := d(data[n:])
|
||||
|
||||
switch status {
|
||||
case STATE_ONLY:
|
||||
n += size
|
||||
continue
|
||||
|
||||
case NO_ROOM:
|
||||
if !eof {
|
||||
return n, cdata[:destPos], nil
|
||||
}
|
||||
rune = 0xfffd
|
||||
n = len(data)
|
||||
|
||||
default:
|
||||
n += size
|
||||
}
|
||||
|
||||
if rune < 128 {
|
||||
if destPos >= len(cdata) {
|
||||
cdata = doubleLength(cdata)
|
||||
}
|
||||
cdata[destPos] = byte(rune)
|
||||
destPos++
|
||||
} else {
|
||||
if destPos+utf8.RuneLen(rune) > len(cdata) {
|
||||
cdata = doubleLength(cdata)
|
||||
}
|
||||
destPos += utf8.EncodeRune(cdata[destPos:], rune)
|
||||
}
|
||||
}
|
||||
|
||||
return n, cdata[:destPos], nil
|
||||
}
|
||||
|
||||
func doubleLength(b []byte) []byte {
|
||||
b2 := make([]byte, 2*len(b))
|
||||
copy(b2, b)
|
||||
return b2
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := 0; i < len(utf16Charsets); i++ {
|
||||
RegisterCharset(&utf16Charsets[i])
|
||||
}
|
||||
}
|
||||
|
||||
var utf16Charsets = []Charset{
|
||||
{
|
||||
Name: "UTF-16",
|
||||
NewDecoder: func() Decoder {
|
||||
var decodeRune Decoder
|
||||
return func(p []byte) (c rune, size int, status Status) {
|
||||
if decodeRune == nil {
|
||||
// haven't read the BOM yet
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case p[0] == 0xfe && p[1] == 0xff:
|
||||
decodeRune = decodeUTF16beRune
|
||||
return 0, 2, STATE_ONLY
|
||||
case p[0] == 0xff && p[1] == 0xfe:
|
||||
decodeRune = decodeUTF16leRune
|
||||
return 0, 2, STATE_ONLY
|
||||
default:
|
||||
decodeRune = decodeUTF16beRune
|
||||
}
|
||||
}
|
||||
|
||||
return decodeRune(p)
|
||||
}
|
||||
},
|
||||
NewEncoder: func() Encoder {
|
||||
wroteBOM := false
|
||||
return func(p []byte, c rune) (size int, status Status) {
|
||||
if !wroteBOM {
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
p[0] = 0xfe
|
||||
p[1] = 0xff
|
||||
wroteBOM = true
|
||||
return 2, STATE_ONLY
|
||||
}
|
||||
|
||||
return encodeUTF16beRune(p, c)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "UTF-16BE",
|
||||
NewDecoder: func() Decoder { return decodeUTF16beRune },
|
||||
NewEncoder: func() Encoder { return encodeUTF16beRune },
|
||||
},
|
||||
{
|
||||
Name: "UTF-16LE",
|
||||
NewDecoder: func() Decoder { return decodeUTF16leRune },
|
||||
NewEncoder: func() Encoder { return encodeUTF16leRune },
|
||||
},
|
||||
}
|
||||
|
||||
func decodeUTF16beRune(p []byte) (r rune, size int, status Status) {
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
c := rune(p[0])<<8 + rune(p[1])
|
||||
|
||||
if utf16.IsSurrogate(c) {
|
||||
if len(p) < 4 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
c2 := rune(p[2])<<8 + rune(p[3])
|
||||
c = utf16.DecodeRune(c, c2)
|
||||
|
||||
if c == 0xfffd {
|
||||
return c, 2, INVALID_CHAR
|
||||
} else {
|
||||
return c, 4, SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
return c, 2, SUCCESS
|
||||
}
|
||||
|
||||
func encodeUTF16beRune(p []byte, c rune) (size int, status Status) {
|
||||
if c < 0x10000 {
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
p[0] = byte(c >> 8)
|
||||
p[1] = byte(c)
|
||||
return 2, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 4 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
s1, s2 := utf16.EncodeRune(c)
|
||||
p[0] = byte(s1 >> 8)
|
||||
p[1] = byte(s1)
|
||||
p[2] = byte(s2 >> 8)
|
||||
p[3] = byte(s2)
|
||||
return 4, SUCCESS
|
||||
}
|
||||
|
||||
func decodeUTF16leRune(p []byte) (r rune, size int, status Status) {
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
c := rune(p[1])<<8 + rune(p[0])
|
||||
|
||||
if utf16.IsSurrogate(c) {
|
||||
if len(p) < 4 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
c2 := rune(p[3])<<8 + rune(p[2])
|
||||
c = utf16.DecodeRune(c, c2)
|
||||
|
||||
if c == 0xfffd {
|
||||
return c, 2, INVALID_CHAR
|
||||
} else {
|
||||
return c, 4, SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
return c, 2, SUCCESS
|
||||
}
|
||||
|
||||
func encodeUTF16leRune(p []byte, c rune) (size int, status Status) {
|
||||
if c < 0x10000 {
|
||||
if len(p) < 2 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
p[1] = byte(c >> 8)
|
||||
p[0] = byte(c)
|
||||
return 2, SUCCESS
|
||||
}
|
||||
|
||||
if len(p) < 4 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
s1, s2 := utf16.EncodeRune(c)
|
||||
p[1] = byte(s1 >> 8)
|
||||
p[0] = byte(s1)
|
||||
p[3] = byte(s2 >> 8)
|
||||
p[2] = byte(s2)
|
||||
return 4, SUCCESS
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
func init() {
|
||||
RegisterCharset(&Charset{
|
||||
Name: "UTF-8",
|
||||
NewDecoder: func() Decoder { return decodeUTF8Rune },
|
||||
NewEncoder: func() Encoder { return encodeUTF8Rune },
|
||||
})
|
||||
}
|
||||
|
||||
func decodeUTF8Rune(p []byte) (c rune, size int, status Status) {
|
||||
if len(p) == 0 {
|
||||
status = NO_ROOM
|
||||
return
|
||||
}
|
||||
|
||||
if p[0] < 128 {
|
||||
return rune(p[0]), 1, SUCCESS
|
||||
}
|
||||
|
||||
c, size = utf8.DecodeRune(p)
|
||||
|
||||
if c == 0xfffd {
|
||||
if utf8.FullRune(p) {
|
||||
status = INVALID_CHAR
|
||||
return
|
||||
}
|
||||
|
||||
return 0, 0, NO_ROOM
|
||||
}
|
||||
|
||||
status = SUCCESS
|
||||
return
|
||||
}
|
||||
|
||||
func encodeUTF8Rune(p []byte, c rune) (size int, status Status) {
|
||||
size = utf8.RuneLen(c)
|
||||
if size > len(p) {
|
||||
return 0, NO_ROOM
|
||||
}
|
||||
|
||||
return utf8.EncodeRune(p, c), SUCCESS
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
package mahonia
|
||||
|
||||
import (
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Writer implements character-set encoding for an io.Writer object.
|
||||
type Writer struct {
|
||||
wr io.Writer
|
||||
encode Encoder
|
||||
inbuf []byte
|
||||
outbuf []byte
|
||||
}
|
||||
|
||||
// NewWriter creates a new Writer that uses the receiver to encode text.
|
||||
func (e Encoder) NewWriter(wr io.Writer) *Writer {
|
||||
w := new(Writer)
|
||||
w.wr = wr
|
||||
w.encode = e
|
||||
return w
|
||||
}
|
||||
|
||||
// Write encodes and writes the data from p.
|
||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
|
||||
if len(w.inbuf) > 0 {
|
||||
w.inbuf = append(w.inbuf, p...)
|
||||
p = w.inbuf
|
||||
}
|
||||
|
||||
if len(w.outbuf) < len(p) {
|
||||
w.outbuf = make([]byte, len(p)+10)
|
||||
}
|
||||
|
||||
outpos := 0
|
||||
|
||||
for len(p) > 0 {
|
||||
rune, size := utf8.DecodeRune(p)
|
||||
if rune == 0xfffd && !utf8.FullRune(p) {
|
||||
break
|
||||
}
|
||||
|
||||
p = p[size:]
|
||||
|
||||
retry:
|
||||
size, status := w.encode(w.outbuf[outpos:], rune)
|
||||
|
||||
if status == NO_ROOM {
|
||||
newDest := make([]byte, len(w.outbuf)*2)
|
||||
copy(newDest, w.outbuf)
|
||||
w.outbuf = newDest
|
||||
goto retry
|
||||
}
|
||||
|
||||
if status == STATE_ONLY {
|
||||
outpos += size
|
||||
goto retry
|
||||
}
|
||||
|
||||
outpos += size
|
||||
}
|
||||
|
||||
w.inbuf = w.inbuf[:0]
|
||||
if len(p) > 0 {
|
||||
w.inbuf = append(w.inbuf, p...)
|
||||
}
|
||||
|
||||
n1, err := w.wr.Write(w.outbuf[0:outpos])
|
||||
|
||||
if err != nil && n1 < n {
|
||||
n = n1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) WriteRune(c rune) (size int, err error) {
|
||||
if len(w.inbuf) > 0 {
|
||||
// There are leftover bytes, a partial UTF-8 sequence.
|
||||
w.inbuf = w.inbuf[:0]
|
||||
w.WriteRune(0xfffd)
|
||||
}
|
||||
|
||||
if w.outbuf == nil {
|
||||
w.outbuf = make([]byte, 16)
|
||||
}
|
||||
|
||||
outpos := 0
|
||||
|
||||
retry:
|
||||
size, status := w.encode(w.outbuf[outpos:], c)
|
||||
|
||||
if status == NO_ROOM {
|
||||
w.outbuf = make([]byte, len(w.outbuf)*2)
|
||||
goto retry
|
||||
}
|
||||
|
||||
if status == STATE_ONLY {
|
||||
outpos += size
|
||||
goto retry
|
||||
}
|
||||
|
||||
outpos += size
|
||||
|
||||
return w.wr.Write(w.outbuf[0:outpos])
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,20 +0,0 @@
|
|||
Common Functions
|
||||
================
|
||||
|
||||
[![Build Status](https://travis-ci.org/Unknwon/com.svg)](https://travis-ci.org/Unknwon/com) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/Unknwon/com)
|
||||
|
||||
This is an open source project for commonly used functions for the Go programming language.
|
||||
|
||||
This package need >= **go 1.2**
|
||||
|
||||
Code Convention: based on [Go Code Convention](https://github.com/Unknwon/go-code-convention).
|
||||
|
||||
## Contribute
|
||||
|
||||
Your contribute is welcome, but you have to check following steps after you added some functions and commit them:
|
||||
|
||||
1. Make sure you wrote user-friendly comments for **all functions** .
|
||||
2. Make sure you wrote test cases with any possible condition for **all functions** in file `*_test.go`.
|
||||
3. Make sure you wrote benchmarks for **all functions** in file `*_test.go`.
|
||||
4. Make sure you wrote useful examples for **all functions** in file `example_test.go`.
|
||||
5. Make sure you ran `go test` and got **PASS** .
|
|
@ -1,161 +0,0 @@
|
|||
// +build go1.2
|
||||
|
||||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Package com is an open source project for commonly used functions for the Go programming language.
|
||||
package com
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ExecCmdDirBytes executes system command in given directory
|
||||
// and return stdout, stderr in bytes type, along with possible error.
|
||||
func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
|
||||
bufOut := new(bytes.Buffer)
|
||||
bufErr := new(bytes.Buffer)
|
||||
|
||||
cmd := exec.Command(cmdName, args...)
|
||||
cmd.Dir = dir
|
||||
cmd.Stdout = bufOut
|
||||
cmd.Stderr = bufErr
|
||||
|
||||
err := cmd.Run()
|
||||
return bufOut.Bytes(), bufErr.Bytes(), err
|
||||
}
|
||||
|
||||
// ExecCmdBytes executes system command
|
||||
// and return stdout, stderr in bytes type, along with possible error.
|
||||
func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) {
|
||||
return ExecCmdDirBytes("", cmdName, args...)
|
||||
}
|
||||
|
||||
// ExecCmdDir executes system command in given directory
|
||||
// and return stdout, stderr in string type, along with possible error.
|
||||
func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
|
||||
bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
|
||||
return string(bufOut), string(bufErr), err
|
||||
}
|
||||
|
||||
// ExecCmd executes system command
|
||||
// and return stdout, stderr in string type, along with possible error.
|
||||
func ExecCmd(cmdName string, args ...string) (string, string, error) {
|
||||
return ExecCmdDir("", cmdName, args...)
|
||||
}
|
||||
|
||||
// _________ .__ .____
|
||||
// \_ ___ \ ____ | | ___________ | | ____ ____
|
||||
// / \ \/ / _ \| | / _ \_ __ \ | | / _ \ / ___\
|
||||
// \ \___( <_> ) |_( <_> ) | \/ | |__( <_> ) /_/ >
|
||||
// \______ /\____/|____/\____/|__| |_______ \____/\___ /
|
||||
// \/ \/ /_____/
|
||||
|
||||
// Color number constants.
|
||||
const (
|
||||
Gray = uint8(iota + 90)
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
Blue
|
||||
Magenta
|
||||
//NRed = uint8(31) // Normal
|
||||
EndColor = "\033[0m"
|
||||
)
|
||||
|
||||
// getColorLevel returns colored level string by given level.
|
||||
func getColorLevel(level string) string {
|
||||
level = strings.ToUpper(level)
|
||||
switch level {
|
||||
case "TRAC":
|
||||
return fmt.Sprintf("\033[%dm%s\033[0m", Blue, level)
|
||||
case "ERRO":
|
||||
return fmt.Sprintf("\033[%dm%s\033[0m", Red, level)
|
||||
case "WARN":
|
||||
return fmt.Sprintf("\033[%dm%s\033[0m", Magenta, level)
|
||||
case "SUCC":
|
||||
return fmt.Sprintf("\033[%dm%s\033[0m", Green, level)
|
||||
default:
|
||||
return level
|
||||
}
|
||||
}
|
||||
|
||||
// ColorLogS colors log and return colored content.
|
||||
// Log format: <level> <content [highlight][path]> [ error ].
|
||||
// Level: TRAC -> blue; ERRO -> red; WARN -> Magenta; SUCC -> green; others -> default.
|
||||
// Content: default; path: yellow; error -> red.
|
||||
// Level has to be surrounded by "[" and "]".
|
||||
// Highlights have to be surrounded by "# " and " #"(space), "#" will be deleted.
|
||||
// Paths have to be surrounded by "( " and " )"(space).
|
||||
// Errors have to be surrounded by "[ " and " ]"(space).
|
||||
// Note: it hasn't support windows yet, contribute is welcome.
|
||||
func ColorLogS(format string, a ...interface{}) string {
|
||||
log := fmt.Sprintf(format, a...)
|
||||
|
||||
var clog string
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
// Level.
|
||||
i := strings.Index(log, "]")
|
||||
if log[0] == '[' && i > -1 {
|
||||
clog += "[" + getColorLevel(log[1:i]) + "]"
|
||||
}
|
||||
|
||||
log = log[i+1:]
|
||||
|
||||
// Error.
|
||||
log = strings.Replace(log, "[ ", fmt.Sprintf("[\033[%dm", Red), -1)
|
||||
log = strings.Replace(log, " ]", EndColor+"]", -1)
|
||||
|
||||
// Path.
|
||||
log = strings.Replace(log, "( ", fmt.Sprintf("(\033[%dm", Yellow), -1)
|
||||
log = strings.Replace(log, " )", EndColor+")", -1)
|
||||
|
||||
// Highlights.
|
||||
log = strings.Replace(log, "# ", fmt.Sprintf("\033[%dm", Gray), -1)
|
||||
log = strings.Replace(log, " #", EndColor, -1)
|
||||
|
||||
} else {
|
||||
// Level.
|
||||
i := strings.Index(log, "]")
|
||||
if log[0] == '[' && i > -1 {
|
||||
clog += "[" + log[1:i] + "]"
|
||||
}
|
||||
|
||||
log = log[i+1:]
|
||||
|
||||
// Error.
|
||||
log = strings.Replace(log, "[ ", "[", -1)
|
||||
log = strings.Replace(log, " ]", "]", -1)
|
||||
|
||||
// Path.
|
||||
log = strings.Replace(log, "( ", "(", -1)
|
||||
log = strings.Replace(log, " )", ")", -1)
|
||||
|
||||
// Highlights.
|
||||
log = strings.Replace(log, "# ", "", -1)
|
||||
log = strings.Replace(log, " #", "", -1)
|
||||
}
|
||||
return clog + log
|
||||
}
|
||||
|
||||
// ColorLog prints colored log to stdout.
|
||||
// See color rules in function 'ColorLogS'.
|
||||
func ColorLog(format string, a ...interface{}) {
|
||||
fmt.Print(ColorLogS(format, a...))
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
// Copyright 2014 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Convert string to specify type.
|
||||
type StrTo string
|
||||
|
||||
func (f StrTo) Exist() bool {
|
||||
return string(f) != string(0x1E)
|
||||
}
|
||||
|
||||
func (f StrTo) Uint8() (uint8, error) {
|
||||
v, err := strconv.ParseUint(f.String(), 10, 8)
|
||||
return uint8(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) Int() (int, error) {
|
||||
v, err := strconv.ParseInt(f.String(), 10, 0)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) Int64() (int64, error) {
|
||||
v, err := strconv.ParseInt(f.String(), 10, 64)
|
||||
return int64(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) MustUint8() uint8 {
|
||||
v, _ := f.Uint8()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) MustInt() int {
|
||||
v, _ := f.Int()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) MustInt64() int64 {
|
||||
v, _ := f.Int64()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) String() string {
|
||||
if f.Exist() {
|
||||
return string(f)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Convert any type to string.
|
||||
func ToStr(value interface{}, args ...int) (s string) {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
s = strconv.FormatBool(v)
|
||||
case float32:
|
||||
s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
|
||||
case float64:
|
||||
s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
|
||||
case int:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int8:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int16:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int32:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int64:
|
||||
s = strconv.FormatInt(v, argInt(args).Get(0, 10))
|
||||
case uint:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint8:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint16:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint32:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint64:
|
||||
s = strconv.FormatUint(v, argInt(args).Get(0, 10))
|
||||
case string:
|
||||
s = v
|
||||
case []byte:
|
||||
s = string(v)
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type argInt []int
|
||||
|
||||
func (a argInt) Get(i int, args ...int) (r int) {
|
||||
if i >= 0 && i < len(a) {
|
||||
r = a[i]
|
||||
} else if len(args) > 0 {
|
||||
r = args[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HexStr2int converts hex format string to decimal number.
|
||||
func HexStr2int(hexStr string) (int, error) {
|
||||
num := 0
|
||||
length := len(hexStr)
|
||||
for i := 0; i < length; i++ {
|
||||
char := hexStr[length-i-1]
|
||||
factor := -1
|
||||
|
||||
switch {
|
||||
case char >= '0' && char <= '9':
|
||||
factor = int(char) - '0'
|
||||
case char >= 'a' && char <= 'f':
|
||||
factor = int(char) - 'a' + 10
|
||||
default:
|
||||
return -1, fmt.Errorf("invalid hex: %s", string(char))
|
||||
}
|
||||
|
||||
num += factor * PowInt(16, i)
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// Int2HexStr converts decimal number to hex format string.
|
||||
func Int2HexStr(num int) (hex string) {
|
||||
if num == 0 {
|
||||
return "0"
|
||||
}
|
||||
|
||||
for num > 0 {
|
||||
r := num % 16
|
||||
|
||||
c := "?"
|
||||
if r >= 0 && r <= 9 {
|
||||
c = string(r + '0')
|
||||
} else {
|
||||
c = string(r + 'a' - 10)
|
||||
}
|
||||
hex = c + hex
|
||||
num = num / 16
|
||||
}
|
||||
return hex
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsDir returns true if given path is a directory,
|
||||
// or returns false when it's a file or does not exist.
|
||||
func IsDir(dir string) bool {
|
||||
f, e := os.Stat(dir)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return f.IsDir()
|
||||
}
|
||||
|
||||
func statDir(dirPath, recPath string, includeDir, isDirOnly bool) ([]string, error) {
|
||||
dir, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statList := make([]string, 0)
|
||||
for _, fi := range fis {
|
||||
if strings.Contains(fi.Name(), ".DS_Store") {
|
||||
continue
|
||||
}
|
||||
|
||||
relPath := path.Join(recPath, fi.Name())
|
||||
curPath := path.Join(dirPath, fi.Name())
|
||||
if fi.IsDir() {
|
||||
if includeDir {
|
||||
statList = append(statList, relPath+"/")
|
||||
}
|
||||
s, err := statDir(curPath, relPath, includeDir, isDirOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statList = append(statList, s...)
|
||||
} else if !isDirOnly {
|
||||
statList = append(statList, relPath)
|
||||
}
|
||||
}
|
||||
return statList, nil
|
||||
}
|
||||
|
||||
// StatDir gathers information of given directory by depth-first.
|
||||
// It returns slice of file list and includes subdirectories if enabled;
|
||||
// it returns error and nil slice when error occurs in underlying functions,
|
||||
// or given path is not a directory or does not exist.
|
||||
//
|
||||
// Slice does not include given path itself.
|
||||
// If subdirectories is enabled, they will have suffix '/'.
|
||||
func StatDir(rootPath string, includeDir ...bool) ([]string, error) {
|
||||
if !IsDir(rootPath) {
|
||||
return nil, errors.New("not a directory or does not exist: " + rootPath)
|
||||
}
|
||||
|
||||
isIncludeDir := false
|
||||
if len(includeDir) >= 1 {
|
||||
isIncludeDir = includeDir[0]
|
||||
}
|
||||
return statDir(rootPath, "", isIncludeDir, false)
|
||||
}
|
||||
|
||||
// GetAllSubDirs returns all subdirectories of given root path.
|
||||
// Slice does not include given path itself.
|
||||
func GetAllSubDirs(rootPath string) ([]string, error) {
|
||||
if !IsDir(rootPath) {
|
||||
return nil, errors.New("not a directory or does not exist: " + rootPath)
|
||||
}
|
||||
return statDir(rootPath, "", true, true)
|
||||
}
|
||||
|
||||
// GetFileListBySuffix returns an ordered list of file paths.
|
||||
// It recognize if given path is a file, and don't do recursive find.
|
||||
func GetFileListBySuffix(dirPath, suffix string) ([]string, error) {
|
||||
if !IsExist(dirPath) {
|
||||
return nil, fmt.Errorf("given path does not exist: %s", dirPath)
|
||||
} else if IsFile(dirPath) {
|
||||
return []string{dirPath}, nil
|
||||
}
|
||||
|
||||
// Given path is a directory.
|
||||
dir, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := make([]string, 0, len(fis))
|
||||
for _, fi := range fis {
|
||||
if strings.HasSuffix(fi.Name(), suffix) {
|
||||
files = append(files, path.Join(dirPath, fi.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// CopyDir copy files recursively from source to target directory.
|
||||
//
|
||||
// The filter accepts a function that process the path info.
|
||||
// and should return true for need to filter.
|
||||
//
|
||||
// It returns error when error occurs in underlying functions.
|
||||
func CopyDir(srcPath, destPath string, filters ...func(filePath string) bool) error {
|
||||
// Check if target directory exists.
|
||||
if IsExist(destPath) {
|
||||
return errors.New("file or directory alreay exists: " + destPath)
|
||||
}
|
||||
|
||||
err := os.MkdirAll(destPath, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Gather directory info.
|
||||
infos, err := StatDir(srcPath, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var filter func(filePath string) bool
|
||||
if len(filters) > 0 {
|
||||
filter = filters[0]
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
if filter != nil && filter(info) {
|
||||
continue
|
||||
}
|
||||
|
||||
curPath := path.Join(destPath, info)
|
||||
if strings.HasSuffix(info, "/") {
|
||||
err = os.MkdirAll(curPath, os.ModePerm)
|
||||
} else {
|
||||
err = Copy(path.Join(srcPath, info), curPath)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Storage unit constants.
|
||||
const (
|
||||
Byte = 1
|
||||
KByte = Byte * 1024
|
||||
MByte = KByte * 1024
|
||||
GByte = MByte * 1024
|
||||
TByte = GByte * 1024
|
||||
PByte = TByte * 1024
|
||||
EByte = PByte * 1024
|
||||
)
|
||||
|
||||
func logn(n, b float64) float64 {
|
||||
return math.Log(n) / math.Log(b)
|
||||
}
|
||||
|
||||
func humanateBytes(s uint64, base float64, sizes []string) string {
|
||||
if s < 10 {
|
||||
return fmt.Sprintf("%dB", s)
|
||||
}
|
||||
e := math.Floor(logn(float64(s), base))
|
||||
suffix := sizes[int(e)]
|
||||
val := float64(s) / math.Pow(base, math.Floor(e))
|
||||
f := "%.0f"
|
||||
if val < 10 {
|
||||
f = "%.1f"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(f+"%s", val, suffix)
|
||||
}
|
||||
|
||||
// HumaneFileSize calculates the file size and generate user-friendly string.
|
||||
func HumaneFileSize(s uint64) string {
|
||||
sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
|
||||
return humanateBytes(s, 1024, sizes)
|
||||
}
|
||||
|
||||
// FileMTime returns file modified time and possible error.
|
||||
func FileMTime(file string) (int64, error) {
|
||||
f, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return f.ModTime().Unix(), nil
|
||||
}
|
||||
|
||||
// FileSize returns file size in bytes and possible error.
|
||||
func FileSize(file string) (int64, error) {
|
||||
f, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return f.Size(), nil
|
||||
}
|
||||
|
||||
// Copy copies file from source to target path.
|
||||
func Copy(src, dest string) error {
|
||||
// Gather file information to set back later.
|
||||
si, err := os.Lstat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle symbolic link.
|
||||
if si.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link,
|
||||
// which will lead "no such file or directory" error.
|
||||
return os.Symlink(target, dest)
|
||||
}
|
||||
|
||||
sr, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sr.Close()
|
||||
|
||||
dw, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dw.Close()
|
||||
|
||||
if _, err = io.Copy(dw, sr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set back file information.
|
||||
if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chmod(dest, si.Mode())
|
||||
}
|
||||
|
||||
// WriteFile writes data to a file named by filename.
|
||||
// If the file does not exist, WriteFile creates it
|
||||
// and its upper level paths.
|
||||
func WriteFile(filename string, data []byte) error {
|
||||
os.MkdirAll(path.Dir(filename), os.ModePerm)
|
||||
return ioutil.WriteFile(filename, data, 0655)
|
||||
}
|
||||
|
||||
// IsFile returns true if given path is a file,
|
||||
// or returns false when it's a directory or does not exist.
|
||||
func IsFile(filePath string) bool {
|
||||
f, e := os.Stat(filePath)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return !f.IsDir()
|
||||
}
|
||||
|
||||
// IsExist checks whether a file or directory exists.
|
||||
// It returns false when the file or directory does not exist.
|
||||
func IsExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"html"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Html2JS converts []byte type of HTML content into JS format.
|
||||
func Html2JS(data []byte) []byte {
|
||||
s := string(data)
|
||||
s = strings.Replace(s, `\`, `\\`, -1)
|
||||
s = strings.Replace(s, "\n", `\n`, -1)
|
||||
s = strings.Replace(s, "\r", "", -1)
|
||||
s = strings.Replace(s, "\"", `\"`, -1)
|
||||
s = strings.Replace(s, "<table>", "<table>", -1)
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
// encode html chars to string
|
||||
func HtmlEncode(str string) string {
|
||||
return html.EscapeString(str)
|
||||
}
|
||||
|
||||
// decode string to html chars
|
||||
func HtmlDecode(str string) string {
|
||||
return html.UnescapeString(str)
|
||||
}
|
||||
|
||||
// strip tags in html string
|
||||
func StripTags(src string) string {
|
||||
//去除style,script,html tag
|
||||
re := regexp.MustCompile(`(?s)<(?:style|script)[^<>]*>.*?</(?:style|script)>|</?[a-z][a-z0-9]*[^<>]*>|<!--.*?-->`)
|
||||
src = re.ReplaceAllString(src, "")
|
||||
|
||||
//trim all spaces(2+) into \n
|
||||
re = regexp.MustCompile(`\s{2,}`)
|
||||
src = re.ReplaceAllString(src, "\n")
|
||||
|
||||
return strings.TrimSpace(src)
|
||||
}
|
||||
|
||||
// change \n to <br/>
|
||||
func Nl2br(str string) string {
|
||||
return strings.Replace(str, "\n", "<br/>", -1)
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
type NotFoundError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e NotFoundError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
type RemoteError struct {
|
||||
Host string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *RemoteError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
var UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36"
|
||||
|
||||
// HttpCall makes HTTP method call.
|
||||
func HttpCall(client *http.Client, method, url string, header http.Header, body io.Reader) (io.ReadCloser, error) {
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
for k, vs := range header {
|
||||
req.Header[k] = vs
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
return resp.Body, nil
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode == 404 { // 403 can be rate limit error. || resp.StatusCode == 403 {
|
||||
err = fmt.Errorf("resource not found: %s", url)
|
||||
} else {
|
||||
err = fmt.Errorf("%s %s -> %d", method, url, resp.StatusCode)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// HttpGet gets the specified resource.
|
||||
// ErrNotFound is returned if the server responds with status 404.
|
||||
func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) {
|
||||
return HttpCall(client, "GET", url, header, nil)
|
||||
}
|
||||
|
||||
// HttpPost posts the specified resource.
|
||||
// ErrNotFound is returned if the server responds with status 404.
|
||||
func HttpPost(client *http.Client, url string, header http.Header, body []byte) (io.ReadCloser, error) {
|
||||
return HttpCall(client, "POST", url, header, bytes.NewBuffer(body))
|
||||
}
|
||||
|
||||
// HttpGetToFile gets the specified resource and writes to file.
|
||||
// ErrNotFound is returned if the server responds with status 404.
|
||||
func HttpGetToFile(client *http.Client, url string, header http.Header, fileName string) error {
|
||||
rc, err := HttpGet(client, url, header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
os.MkdirAll(path.Dir(fileName), os.ModePerm)
|
||||
f, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, rc)
|
||||
return err
|
||||
}
|
||||
|
||||
// HttpGetBytes gets the specified resource. ErrNotFound is returned if the server
|
||||
// responds with status 404.
|
||||
func HttpGetBytes(client *http.Client, url string, header http.Header) ([]byte, error) {
|
||||
rc, err := HttpGet(client, url, header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
return ioutil.ReadAll(rc)
|
||||
}
|
||||
|
||||
// HttpGetJSON gets the specified resource and mapping to struct.
|
||||
// ErrNotFound is returned if the server responds with status 404.
|
||||
func HttpGetJSON(client *http.Client, url string, v interface{}) error {
|
||||
rc, err := HttpGet(client, url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
err = json.NewDecoder(rc).Decode(v)
|
||||
if _, ok := err.(*json.SyntaxError); ok {
|
||||
return fmt.Errorf("JSON syntax error at %s", url)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HttpPostJSON posts the specified resource with struct values,
|
||||
// and maps results to struct.
|
||||
// ErrNotFound is returned if the server responds with status 404.
|
||||
func HttpPostJSON(client *http.Client, url string, body, v interface{}) error {
|
||||
data, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rc, err := HttpPost(client, url, http.Header{"content-type": []string{"application/json"}}, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
err = json.NewDecoder(rc).Decode(v)
|
||||
if _, ok := err.(*json.SyntaxError); ok {
|
||||
return fmt.Errorf("JSON syntax error at %s", url)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A RawFile describes a file that can be downloaded.
|
||||
type RawFile interface {
|
||||
Name() string
|
||||
RawUrl() string
|
||||
Data() []byte
|
||||
SetData([]byte)
|
||||
}
|
||||
|
||||
// FetchFiles fetches files specified by the rawURL field in parallel.
|
||||
func FetchFiles(client *http.Client, files []RawFile, header http.Header) error {
|
||||
ch := make(chan error, len(files))
|
||||
for i := range files {
|
||||
go func(i int) {
|
||||
p, err := HttpGetBytes(client, files[i].RawUrl(), nil)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
files[i].SetData(p)
|
||||
ch <- nil
|
||||
}(i)
|
||||
}
|
||||
for _ = range files {
|
||||
if err := <-ch; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchFiles uses command `curl` to fetch files specified by the rawURL field in parallel.
|
||||
func FetchFilesCurl(files []RawFile, curlOptions ...string) error {
|
||||
ch := make(chan error, len(files))
|
||||
for i := range files {
|
||||
go func(i int) {
|
||||
stdout, _, err := ExecCmd("curl", append(curlOptions, files[i].RawUrl())...)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
|
||||
files[i].SetData([]byte(stdout))
|
||||
ch <- nil
|
||||
}(i)
|
||||
}
|
||||
for _ = range files {
|
||||
if err := <-ch; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2014 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
// PowInt is int type of math.Pow function.
|
||||
func PowInt(x int, y int) int {
|
||||
if y <= 0 {
|
||||
return 1
|
||||
} else {
|
||||
if y%2 == 0 {
|
||||
sqrt := PowInt(x, y/2)
|
||||
return sqrt * sqrt
|
||||
} else {
|
||||
return PowInt(x, y-1) * x
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetGOPATHs returns all paths in GOPATH variable.
|
||||
func GetGOPATHs() []string {
|
||||
gopath := os.Getenv("GOPATH")
|
||||
var paths []string
|
||||
if runtime.GOOS == "windows" {
|
||||
gopath = strings.Replace(gopath, "\\", "/", -1)
|
||||
paths = strings.Split(gopath, ";")
|
||||
} else {
|
||||
paths = strings.Split(gopath, ":")
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// GetSrcPath returns app. source code path.
|
||||
// It only works when you have src. folder in GOPATH,
|
||||
// it returns error not able to locate source folder path.
|
||||
func GetSrcPath(importPath string) (appPath string, err error) {
|
||||
paths := GetGOPATHs()
|
||||
for _, p := range paths {
|
||||
if IsExist(p + "/src/" + importPath + "/") {
|
||||
appPath = p + "/src/" + importPath + "/"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(appPath) == 0 {
|
||||
return "", errors.New("Unable to locate source folder path")
|
||||
}
|
||||
|
||||
appPath = filepath.Dir(appPath) + "/"
|
||||
if runtime.GOOS == "windows" {
|
||||
// Replace all '\' to '/'.
|
||||
appPath = strings.Replace(appPath, "\\", "/", -1)
|
||||
}
|
||||
|
||||
return appPath, nil
|
||||
}
|
||||
|
||||
// HomeDir returns path of '~'(in Linux) on Windows,
|
||||
// it returns error when the variable does not exist.
|
||||
func HomeDir() (home string, err error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
home = os.Getenv("USERPROFILE")
|
||||
if len(home) == 0 {
|
||||
home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||
}
|
||||
} else {
|
||||
home = os.Getenv("HOME")
|
||||
}
|
||||
|
||||
if len(home) == 0 {
|
||||
return "", errors.New("Cannot specify home directory because it's empty")
|
||||
}
|
||||
|
||||
return home, nil
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import "regexp"
|
||||
|
||||
const (
|
||||
regex_email_pattern = `(?i)[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}`
|
||||
regex_strict_email_pattern = `(?i)[A-Z0-9!#$%&'*+/=?^_{|}~-]+` +
|
||||
`(?:\.[A-Z0-9!#$%&'*+/=?^_{|}~-]+)*` +
|
||||
`@(?:[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?\.)+` +
|
||||
`[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?`
|
||||
regex_url_pattern = `(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?`
|
||||
)
|
||||
|
||||
var (
|
||||
regex_email *regexp.Regexp
|
||||
regex_strict_email *regexp.Regexp
|
||||
regex_url *regexp.Regexp
|
||||
)
|
||||
|
||||
func init() {
|
||||
regex_email = regexp.MustCompile(regex_email_pattern)
|
||||
regex_strict_email = regexp.MustCompile(regex_strict_email_pattern)
|
||||
regex_url = regexp.MustCompile(regex_url_pattern)
|
||||
}
|
||||
|
||||
// validate string is an email address, if not return false
|
||||
// basically validation can match 99% cases
|
||||
func IsEmail(email string) bool {
|
||||
return regex_email.MatchString(email)
|
||||
}
|
||||
|
||||
// validate string is an email address, if not return false
|
||||
// this validation omits RFC 2822
|
||||
func IsEmailRFC(email string) bool {
|
||||
return regex_strict_email.MatchString(email)
|
||||
}
|
||||
|
||||
// validate string is a url link, if not return false
|
||||
// simple validation can match 99% cases
|
||||
func IsUrl(url string) bool {
|
||||
return regex_url.MatchString(url)
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AppendStr appends string to slice with no duplicates.
|
||||
func AppendStr(strs []string, str string) []string {
|
||||
for _, s := range strs {
|
||||
if s == str {
|
||||
return strs
|
||||
}
|
||||
}
|
||||
return append(strs, str)
|
||||
}
|
||||
|
||||
// CompareSliceStr compares two 'string' type slices.
|
||||
// It returns true if elements and order are both the same.
|
||||
func CompareSliceStr(s1, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range s1 {
|
||||
if s1[i] != s2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CompareSliceStr compares two 'string' type slices.
|
||||
// It returns true if elements are the same, and ignores the order.
|
||||
func CompareSliceStrU(s1, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range s1 {
|
||||
for j := len(s2) - 1; j >= 0; j-- {
|
||||
if s1[i] == s2[j] {
|
||||
s2 = append(s2[:j], s2[j+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(s2) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSliceContainsStr returns true if the string exists in given slice, ignore case.
|
||||
func IsSliceContainsStr(sl []string, str string) bool {
|
||||
str = strings.ToLower(str)
|
||||
for _, s := range sl {
|
||||
if strings.ToLower(s) == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSliceContainsInt64 returns true if the int64 exists in given slice.
|
||||
func IsSliceContainsInt64(sl []int64, i int64) bool {
|
||||
for _, s := range sl {
|
||||
if s == i {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,253 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
r "math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// AESGCMEncrypt encrypts plaintext with the given key using AES in GCM mode.
|
||||
func AESGCMEncrypt(key, plaintext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
|
||||
return append(nonce, ciphertext...), nil
|
||||
}
|
||||
|
||||
// AESGCMDecrypt decrypts ciphertext with the given key using AES in GCM mode.
|
||||
func AESGCMDecrypt(key, ciphertext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
size := gcm.NonceSize()
|
||||
if len(ciphertext)-size <= 0 {
|
||||
return nil, errors.New("Ciphertext is empty")
|
||||
}
|
||||
|
||||
nonce := ciphertext[:size]
|
||||
ciphertext = ciphertext[size:]
|
||||
|
||||
plainText, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plainText, nil
|
||||
}
|
||||
|
||||
// IsLetter returns true if the 'l' is an English letter.
|
||||
func IsLetter(l uint8) bool {
|
||||
n := (l | 0x20) - 'a'
|
||||
if n >= 0 && n < 26 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
|
||||
func Expand(template string, match map[string]string, subs ...string) string {
|
||||
var p []byte
|
||||
var i int
|
||||
for {
|
||||
i = strings.Index(template, "{")
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
p = append(p, template[:i]...)
|
||||
template = template[i+1:]
|
||||
i = strings.Index(template, "}")
|
||||
if s, ok := match[template[:i]]; ok {
|
||||
p = append(p, s...)
|
||||
} else {
|
||||
j, _ := strconv.Atoi(template[:i])
|
||||
if j >= len(subs) {
|
||||
p = append(p, []byte("Missing")...)
|
||||
} else {
|
||||
p = append(p, subs[j]...)
|
||||
}
|
||||
}
|
||||
template = template[i+1:]
|
||||
}
|
||||
p = append(p, template...)
|
||||
return string(p)
|
||||
}
|
||||
|
||||
// Reverse s string, support unicode
|
||||
func Reverse(s string) string {
|
||||
n := len(s)
|
||||
runes := make([]rune, n)
|
||||
for _, rune := range s {
|
||||
n--
|
||||
runes[n] = rune
|
||||
}
|
||||
return string(runes[n:])
|
||||
}
|
||||
|
||||
// RandomCreateBytes generate random []byte by specify chars.
|
||||
func RandomCreateBytes(n int, alphabets ...byte) []byte {
|
||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
var bytes = make([]byte, n)
|
||||
var randby bool
|
||||
if num, err := rand.Read(bytes); num != n || err != nil {
|
||||
r.Seed(time.Now().UnixNano())
|
||||
randby = true
|
||||
}
|
||||
for i, b := range bytes {
|
||||
if len(alphabets) == 0 {
|
||||
if randby {
|
||||
bytes[i] = alphanum[r.Intn(len(alphanum))]
|
||||
} else {
|
||||
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||
}
|
||||
} else {
|
||||
if randby {
|
||||
bytes[i] = alphabets[r.Intn(len(alphabets))]
|
||||
} else {
|
||||
bytes[i] = alphabets[b%byte(len(alphabets))]
|
||||
}
|
||||
}
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
// ToSnakeCase can convert all upper case characters in a string to
|
||||
// underscore format.
|
||||
//
|
||||
// Some samples.
|
||||
// "FirstName" => "first_name"
|
||||
// "HTTPServer" => "http_server"
|
||||
// "NoHTTPS" => "no_https"
|
||||
// "GO_PATH" => "go_path"
|
||||
// "GO PATH" => "go_path" // space is converted to underscore.
|
||||
// "GO-PATH" => "go_path" // hyphen is converted to underscore.
|
||||
//
|
||||
// From https://github.com/huandu/xstrings
|
||||
func ToSnakeCase(str string) string {
|
||||
if len(str) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
var prev, r0, r1 rune
|
||||
var size int
|
||||
|
||||
r0 = '_'
|
||||
|
||||
for len(str) > 0 {
|
||||
prev = r0
|
||||
r0, size = utf8.DecodeRuneInString(str)
|
||||
str = str[size:]
|
||||
|
||||
switch {
|
||||
case r0 == utf8.RuneError:
|
||||
buf.WriteByte(byte(str[0]))
|
||||
|
||||
case unicode.IsUpper(r0):
|
||||
if prev != '_' {
|
||||
buf.WriteRune('_')
|
||||
}
|
||||
|
||||
buf.WriteRune(unicode.ToLower(r0))
|
||||
|
||||
if len(str) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
r0, size = utf8.DecodeRuneInString(str)
|
||||
str = str[size:]
|
||||
|
||||
if !unicode.IsUpper(r0) {
|
||||
buf.WriteRune(r0)
|
||||
break
|
||||
}
|
||||
|
||||
// find next non-upper-case character and insert `_` properly.
|
||||
// it's designed to convert `HTTPServer` to `http_server`.
|
||||
// if there are more than 2 adjacent upper case characters in a word,
|
||||
// treat them as an abbreviation plus a normal word.
|
||||
for len(str) > 0 {
|
||||
r1 = r0
|
||||
r0, size = utf8.DecodeRuneInString(str)
|
||||
str = str[size:]
|
||||
|
||||
if r0 == utf8.RuneError {
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
buf.WriteByte(byte(str[0]))
|
||||
break
|
||||
}
|
||||
|
||||
if !unicode.IsUpper(r0) {
|
||||
if r0 == '_' || r0 == ' ' || r0 == '-' {
|
||||
r0 = '_'
|
||||
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
} else {
|
||||
buf.WriteRune('_')
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
buf.WriteRune(r0)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
}
|
||||
|
||||
if len(str) == 0 || r0 == '_' {
|
||||
buf.WriteRune(unicode.ToLower(r0))
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
if r0 == ' ' || r0 == '-' {
|
||||
r0 = '_'
|
||||
}
|
||||
|
||||
buf.WriteRune(r0)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Format unix time int64 to string
|
||||
func Date(ti int64, format string) string {
|
||||
t := time.Unix(int64(ti), 0)
|
||||
return DateT(t, format)
|
||||
}
|
||||
|
||||
// Format unix time string to string
|
||||
func DateS(ts string, format string) string {
|
||||
i, _ := strconv.ParseInt(ts, 10, 64)
|
||||
return Date(i, format)
|
||||
}
|
||||
|
||||
// Format time.Time struct to string
|
||||
// MM - month - 01
|
||||
// M - month - 1, single bit
|
||||
// DD - day - 02
|
||||
// D - day 2
|
||||
// YYYY - year - 2006
|
||||
// YY - year - 06
|
||||
// HH - 24 hours - 03
|
||||
// H - 24 hours - 3
|
||||
// hh - 12 hours - 03
|
||||
// h - 12 hours - 3
|
||||
// mm - minute - 04
|
||||
// m - minute - 4
|
||||
// ss - second - 05
|
||||
// s - second = 5
|
||||
func DateT(t time.Time, format string) string {
|
||||
res := strings.Replace(format, "MM", t.Format("01"), -1)
|
||||
res = strings.Replace(res, "M", t.Format("1"), -1)
|
||||
res = strings.Replace(res, "DD", t.Format("02"), -1)
|
||||
res = strings.Replace(res, "D", t.Format("2"), -1)
|
||||
res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
|
||||
res = strings.Replace(res, "YY", t.Format("06"), -1)
|
||||
res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
|
||||
res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
|
||||
res = strings.Replace(res, "hh", t.Format("03"), -1)
|
||||
res = strings.Replace(res, "h", t.Format("3"), -1)
|
||||
res = strings.Replace(res, "mm", t.Format("04"), -1)
|
||||
res = strings.Replace(res, "m", t.Format("4"), -1)
|
||||
res = strings.Replace(res, "ss", t.Format("05"), -1)
|
||||
res = strings.Replace(res, "s", t.Format("5"), -1)
|
||||
return res
|
||||
}
|
||||
|
||||
// DateFormat pattern rules.
|
||||
var datePatterns = []string{
|
||||
// year
|
||||
"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
|
||||
"y", "06", //A two digit representation of a year Examples: 99 or 03
|
||||
|
||||
// month
|
||||
"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
|
||||
"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
|
||||
"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
|
||||
"F", "January", // A full textual representation of a month, such as January or March January through December
|
||||
|
||||
// day
|
||||
"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
|
||||
"j", "2", // Day of the month without leading zeros 1 to 31
|
||||
|
||||
// week
|
||||
"D", "Mon", // A textual representation of a day, three letters Mon through Sun
|
||||
"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
|
||||
|
||||
// time
|
||||
"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
|
||||
"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
|
||||
"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
|
||||
"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
|
||||
|
||||
"a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
|
||||
"A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
|
||||
|
||||
"i", "04", // Minutes with leading zeros 00 to 59
|
||||
"s", "05", // Seconds, with leading zeros 00 through 59
|
||||
|
||||
// time zone
|
||||
"T", "MST",
|
||||
"P", "-07:00",
|
||||
"O", "-0700",
|
||||
|
||||
// RFC 2822
|
||||
"r", time.RFC1123Z,
|
||||
}
|
||||
|
||||
// Parse Date use PHP time format.
|
||||
func DateParse(dateString, format string) (time.Time, error) {
|
||||
replacer := strings.NewReplacer(datePatterns...)
|
||||
format = replacer.Replace(format)
|
||||
return time.ParseInLocation(format, dateString, time.Local)
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright 2013 com authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// url encode string, is + not %20
|
||||
func UrlEncode(str string) string {
|
||||
return url.QueryEscape(str)
|
||||
}
|
||||
|
||||
// url decode string
|
||||
func UrlDecode(str string) (string, error) {
|
||||
return url.QueryUnescape(str)
|
||||
}
|
||||
|
||||
// base64 encode
|
||||
func Base64Encode(str string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(str))
|
||||
}
|
||||
|
||||
// base64 decode
|
||||
func Base64Decode(str string) (string, error) {
|
||||
s, e := base64.StdEncoding.DecodeString(str)
|
||||
return string(s), e
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
Copyright (c) 2012, Cloud Instruments Co., Ltd. <info@cin.io>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Cloud Instruments Co., Ltd. nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,116 +0,0 @@
|
|||
Seelog
|
||||
=======
|
||||
|
||||
Seelog is a powerful and easy-to-learn logging framework that provides functionality for flexible dispatching, filtering, and formatting log messages.
|
||||
It is natively written in the [Go](http://golang.org/) programming language.
|
||||
|
||||
[![Build Status](https://drone.io/github.com/cihub/seelog/status.png)](https://drone.io/github.com/cihub/seelog/latest)
|
||||
|
||||
Features
|
||||
------------------
|
||||
|
||||
* Xml configuring to be able to change logger parameters without recompilation
|
||||
* Changing configurations on the fly without app restart
|
||||
* Possibility to set different log configurations for different project files and functions
|
||||
* Adjustable message formatting
|
||||
* Simultaneous log output to multiple streams
|
||||
* Choosing logger priority strategy to minimize performance hit
|
||||
* Different output writers
|
||||
* Console writer
|
||||
* File writer
|
||||
* Buffered writer (Chunk writer)
|
||||
* Rolling log writer (Logging with rotation)
|
||||
* SMTP writer
|
||||
* Others... (See [Wiki](https://github.com/cihub/seelog/wiki))
|
||||
* Log message wrappers (JSON, XML, etc.)
|
||||
* Global variables and functions for easy usage in standalone apps
|
||||
* Functions for flexible usage in libraries
|
||||
|
||||
Quick-start
|
||||
-----------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import log "github.com/cihub/seelog"
|
||||
|
||||
func main() {
|
||||
defer log.Flush()
|
||||
log.Info("Hello from Seelog!")
|
||||
}
|
||||
```
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
If you don't have the Go development environment installed, visit the
|
||||
[Getting Started](http://golang.org/doc/install.html) document and follow the instructions. Once you're ready, execute the following command:
|
||||
|
||||
```
|
||||
go get -u github.com/cihub/seelog
|
||||
```
|
||||
|
||||
*IMPORTANT*: If you are not using the latest release version of Go, check out this [wiki page](https://github.com/cihub/seelog/wiki/Notes-on-'go-get')
|
||||
|
||||
Documentation
|
||||
---------------
|
||||
|
||||
Seelog has github wiki pages, which contain detailed how-tos references: https://github.com/cihub/seelog/wiki
|
||||
|
||||
Examples
|
||||
---------------
|
||||
|
||||
Seelog examples can be found here: [seelog-examples](https://github.com/cihub/seelog-examples)
|
||||
|
||||
Issues
|
||||
---------------
|
||||
|
||||
Feel free to push issues that could make Seelog better: https://github.com/cihub/seelog/issues
|
||||
|
||||
Changelog
|
||||
---------------
|
||||
* **v2.6** : Config using code and custom formatters
|
||||
* Configuration using code in addition to xml (All internal receiver/dispatcher/logger types are now exported).
|
||||
* Custom formatters. Check [wiki](https://github.com/cihub/seelog/wiki/Custom-formatters)
|
||||
* Bugfixes and internal improvements.
|
||||
* **v2.5** : Interaction with other systems. Part 2: custom receivers
|
||||
* Finished custom receivers feature. Check [wiki](https://github.com/cihub/seelog/wiki/custom-receivers)
|
||||
* Added 'LoggerFromCustomReceiver'
|
||||
* Added 'LoggerFromWriterWithMinLevelAndFormat'
|
||||
* Added 'LoggerFromCustomReceiver'
|
||||
* Added 'LoggerFromParamConfigAs...'
|
||||
* **v2.4** : Interaction with other systems. Part 1: wrapping seelog
|
||||
* Added configurable caller stack skip logic
|
||||
* Added 'SetAdditionalStackDepth' to 'LoggerInterface'
|
||||
* **v2.3** : Rethinking 'rolling' receiver
|
||||
* Reimplemented 'rolling' receiver
|
||||
* Added 'Max rolls' feature for 'rolling' receiver with type='date'
|
||||
* Fixed 'rolling' receiver issue: renaming on Windows
|
||||
* **v2.2** : go1.0 compatibility point [go1.0 tag]
|
||||
* Fixed internal bugs
|
||||
* Added 'ANSI n [;k]' format identifier: %EscN
|
||||
* Made current release go1 compatible
|
||||
* **v2.1** : Some new features
|
||||
* Rolling receiver archiving option.
|
||||
* Added format identifier: %Line
|
||||
* Smtp: added paths to PEM files directories
|
||||
* Added format identifier: %FuncShort
|
||||
* Warn, Error and Critical methods now return an error
|
||||
* **v2.0** : Second major release. BREAKING CHANGES.
|
||||
* Support of binaries with stripped symbols
|
||||
* Added log strategy: adaptive
|
||||
* Critical message now forces Flush()
|
||||
* Added predefined formats: xml-debug, xml-debug-short, xml, xml-short, json-debug, json-debug-short, json, json-short, debug, debug-short, fast
|
||||
* Added receiver: conn (network connection writer)
|
||||
* BREAKING CHANGE: added Tracef, Debugf, Infof, etc. to satisfy the print/printf principle
|
||||
* Bug fixes
|
||||
* **v1.0** : Initial release. Features:
|
||||
* Xml config
|
||||
* Changing configurations on the fly without app restart
|
||||
* Contraints and exceptions
|
||||
* Formatting
|
||||
* Log strategies: sync, async loop, async timer
|
||||
* Receivers: buffered, console, file, rolling, smtp
|
||||
|
||||
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/cihub/seelog/archive/gzip"
|
||||
)
|
||||
|
||||
// Reader is the interface for reading files from an archive.
|
||||
type Reader interface {
|
||||
NextFile() (name string, err error)
|
||||
io.Reader
|
||||
}
|
||||
|
||||
// ReadCloser is the interface that groups Reader with the Close method.
|
||||
type ReadCloser interface {
|
||||
Reader
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// Writer is the interface for writing files to an archived format.
|
||||
type Writer interface {
|
||||
NextFile(name string, fi os.FileInfo) error
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// WriteCloser is the interface that groups Writer with the Close method.
|
||||
type WriteCloser interface {
|
||||
Writer
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type nopCloser struct{ Reader }
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
// NopCloser returns a ReadCloser with a no-op Close method wrapping the
|
||||
// provided Reader r.
|
||||
func NopCloser(r Reader) ReadCloser {
|
||||
return nopCloser{r}
|
||||
}
|
||||
|
||||
// Copy copies from src to dest until either EOF is reached on src or an error
|
||||
// occurs.
|
||||
//
|
||||
// When the archive format of src matches that of dst, Copy streams the files
|
||||
// directly into dst. Otherwise, copy buffers the contents to disk to compute
|
||||
// headers before writing to dst.
|
||||
func Copy(dst Writer, src Reader) error {
|
||||
switch src := src.(type) {
|
||||
case tarReader:
|
||||
if dst, ok := dst.(tarWriter); ok {
|
||||
return copyTar(dst, src)
|
||||
}
|
||||
case zipReader:
|
||||
if dst, ok := dst.(zipWriter); ok {
|
||||
return copyZip(dst, src)
|
||||
}
|
||||
// Switch on concrete type because gzip has no special methods
|
||||
case *gzip.Reader:
|
||||
if dst, ok := dst.(*gzip.Writer); ok {
|
||||
_, err := io.Copy(dst, src)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return copyBuffer(dst, src)
|
||||
}
|
||||
|
||||
func copyBuffer(dst Writer, src Reader) (err error) {
|
||||
const defaultFileMode = 0666
|
||||
|
||||
buf, err := ioutil.TempFile("", "archive_copy_buffer")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(buf.Name()) // Do not care about failure removing temp
|
||||
defer buf.Close() // Do not care about failure closing temp
|
||||
for {
|
||||
// Handle the next file
|
||||
name, err := src.NextFile()
|
||||
switch err {
|
||||
case io.EOF: // Done copying
|
||||
return nil
|
||||
default: // Failed to write: bail out
|
||||
return err
|
||||
case nil: // Proceed below
|
||||
}
|
||||
|
||||
// Buffer the file
|
||||
if _, err := io.Copy(buf, src); err != nil {
|
||||
return fmt.Errorf("buffer to disk: %v", err)
|
||||
}
|
||||
|
||||
// Seek to the start of the file for full file copy
|
||||
if _, err := buf.Seek(0, os.SEEK_SET); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set desired file permissions
|
||||
if err := os.Chmod(buf.Name(), defaultFileMode); err != nil {
|
||||
return err
|
||||
}
|
||||
fi, err := buf.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the buffered file
|
||||
if err := dst.NextFile(name, fi); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(dst, buf); err != nil {
|
||||
return fmt.Errorf("copy to dst: %v", err)
|
||||
}
|
||||
if err := buf.Truncate(0); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := buf.Seek(0, os.SEEK_SET); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type tarReader interface {
|
||||
Next() (*tar.Header, error)
|
||||
io.Reader
|
||||
}
|
||||
|
||||
type tarWriter interface {
|
||||
WriteHeader(hdr *tar.Header) error
|
||||
io.Writer
|
||||
}
|
||||
|
||||
type zipReader interface {
|
||||
Files() []*zip.File
|
||||
}
|
||||
|
||||
type zipWriter interface {
|
||||
CreateHeader(fh *zip.FileHeader) (io.Writer, error)
|
||||
}
|
||||
|
||||
func copyTar(w tarWriter, r tarReader) error {
|
||||
for {
|
||||
hdr, err := r.Next()
|
||||
switch err {
|
||||
case io.EOF:
|
||||
return nil
|
||||
default: // Handle error
|
||||
return err
|
||||
case nil: // Proceed below
|
||||
}
|
||||
|
||||
info := hdr.FileInfo()
|
||||
// Skip directories
|
||||
if info.IsDir() {
|
||||
continue
|
||||
}
|
||||
if err := w.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(w, r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copyZip(zw zipWriter, r zipReader) error {
|
||||
for _, f := range r.Files() {
|
||||
if err := copyZipFile(zw, f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyZipFile(zw zipWriter, f *zip.File) error {
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close() // Read-only
|
||||
|
||||
hdr := f.FileHeader
|
||||
hdr.SetModTime(time.Now())
|
||||
w, err := zw.CreateHeader(&hdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(w, rc)
|
||||
return err
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
// Package gzip implements reading and writing of gzip format compressed files.
|
||||
// See the compress/gzip package for more details.
|
||||
package gzip
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Reader is an io.Reader that can be read to retrieve uncompressed data from a
|
||||
// gzip-format compressed file.
|
||||
type Reader struct {
|
||||
gzip.Reader
|
||||
name string
|
||||
isEOF bool
|
||||
}
|
||||
|
||||
// NewReader creates a new Reader reading the given reader.
|
||||
func NewReader(r io.Reader, name string) (*Reader, error) {
|
||||
gr, err := gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Reader{
|
||||
Reader: *gr,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NextFile returns the file name. Calls subsequent to the first call will
|
||||
// return EOF.
|
||||
func (r *Reader) NextFile() (name string, err error) {
|
||||
if r.isEOF {
|
||||
return "", io.EOF
|
||||
}
|
||||
|
||||
r.isEOF = true
|
||||
return r.name, nil
|
||||
}
|
||||
|
||||
// Writer is an io.WriteCloser. Writes to a Writer are compressed and written to w.
|
||||
type Writer struct {
|
||||
gzip.Writer
|
||||
name string
|
||||
noMoreFiles bool
|
||||
}
|
||||
|
||||
// NextFile never returns a next file, and should not be called more than once.
|
||||
func (w *Writer) NextFile(name string, _ os.FileInfo) error {
|
||||
if w.noMoreFiles {
|
||||
return fmt.Errorf("gzip: only accepts one file: already received %q and now %q", w.name, name)
|
||||
}
|
||||
w.noMoreFiles = true
|
||||
w.name = name
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewWriter returns a new Writer. Writes to the returned writer are compressed
|
||||
// and written to w.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{Writer: *gzip.NewWriter(w)}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package tar
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Reader provides sequential access to the contents of a tar archive.
|
||||
type Reader struct {
|
||||
tar.Reader
|
||||
}
|
||||
|
||||
// NewReader creates a new Reader reading from r.
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{Reader: *tar.NewReader(r)}
|
||||
}
|
||||
|
||||
// NextFile advances to the next file in the tar archive.
|
||||
func (r *Reader) NextFile() (name string, err error) {
|
||||
hdr, err := r.Next()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hdr.Name, nil
|
||||
}
|
||||
|
||||
// Writer provides sequential writing of a tar archive in POSIX.1 format.
|
||||
type Writer struct {
|
||||
tar.Writer
|
||||
closers []io.Closer
|
||||
}
|
||||
|
||||
// NewWriter creates a new Writer writing to w.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{Writer: *tar.NewWriter(w)}
|
||||
}
|
||||
|
||||
// NewWriteMultiCloser creates a new Writer writing to w that also closes all
|
||||
// closers in order on close.
|
||||
func NewWriteMultiCloser(w io.WriteCloser, closers ...io.Closer) *Writer {
|
||||
return &Writer{
|
||||
Writer: *tar.NewWriter(w),
|
||||
closers: closers,
|
||||
}
|
||||
}
|
||||
|
||||
// NextFile computes and writes a header and prepares to accept the file's
|
||||
// contents.
|
||||
func (w *Writer) NextFile(name string, fi os.FileInfo) error {
|
||||
if name == "" {
|
||||
name = fi.Name()
|
||||
}
|
||||
hdr, err := tar.FileInfoHeader(fi, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Name = name
|
||||
return w.WriteHeader(hdr)
|
||||
}
|
||||
|
||||
// Close closes the tar archive and all other closers, flushing any unwritten
|
||||
// data to the underlying writer.
|
||||
func (w *Writer) Close() error {
|
||||
err := w.Writer.Close()
|
||||
for _, c := range w.closers {
|
||||
if cerr := c.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package zip
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Reader provides sequential access to the contents of a zip archive.
|
||||
type Reader struct {
|
||||
zip.Reader
|
||||
unread []*zip.File
|
||||
rc io.ReadCloser
|
||||
}
|
||||
|
||||
// NewReader returns a new Reader reading from r, which is assumed to have the
|
||||
// given size in bytes.
|
||||
func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
|
||||
zr, err := zip.NewReader(r, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Reader{Reader: *zr}, nil
|
||||
}
|
||||
|
||||
// NextFile advances to the next file in the zip archive.
|
||||
func (r *Reader) NextFile() (name string, err error) {
|
||||
// Initialize unread
|
||||
if r.unread == nil {
|
||||
r.unread = r.Files()[:]
|
||||
}
|
||||
|
||||
// Close previous file
|
||||
if r.rc != nil {
|
||||
r.rc.Close() // Read-only
|
||||
}
|
||||
|
||||
if len(r.unread) == 0 {
|
||||
return "", io.EOF
|
||||
}
|
||||
|
||||
// Open and return next unread
|
||||
f := r.unread[0]
|
||||
name, r.unread = f.Name, r.unread[1:]
|
||||
r.rc, err = f.Open()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (r *Reader) Read(p []byte) (n int, err error) {
|
||||
return r.rc.Read(p)
|
||||
}
|
||||
|
||||
// Files returns the full list of files in the zip archive.
|
||||
func (r *Reader) Files() []*zip.File {
|
||||
return r.File
|
||||
}
|
||||
|
||||
// Writer provides sequential writing of a zip archive.1 format.
|
||||
type Writer struct {
|
||||
zip.Writer
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
// NewWriter returns a new Writer writing to w.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{Writer: *zip.NewWriter(w)}
|
||||
}
|
||||
|
||||
// NextFile computes and writes a header and prepares to accept the file's
|
||||
// contents.
|
||||
func (w *Writer) NextFile(name string, fi os.FileInfo) error {
|
||||
if name == "" {
|
||||
name = fi.Name()
|
||||
}
|
||||
hdr, err := zip.FileInfoHeader(fi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Name = name
|
||||
w.w, err = w.CreateHeader(hdr)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||
return w.w.Write(p)
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
adaptiveLoggerMaxInterval = time.Minute
|
||||
adaptiveLoggerMaxCriticalMsgCount = uint32(1000)
|
||||
)
|
||||
|
||||
// asyncAdaptiveLogger represents asynchronous adaptive logger which acts like
|
||||
// an async timer logger, but its interval depends on the current message count
|
||||
// in the queue.
|
||||
//
|
||||
// Interval = I, minInterval = m, maxInterval = M, criticalMsgCount = C, msgCount = c:
|
||||
// I = m + (C - Min(c, C)) / C * (M - m)
|
||||
type asyncAdaptiveLogger struct {
|
||||
asyncLogger
|
||||
minInterval time.Duration
|
||||
criticalMsgCount uint32
|
||||
maxInterval time.Duration
|
||||
}
|
||||
|
||||
// NewAsyncLoopLogger creates a new asynchronous adaptive logger
|
||||
func NewAsyncAdaptiveLogger(
|
||||
config *logConfig,
|
||||
minInterval time.Duration,
|
||||
maxInterval time.Duration,
|
||||
criticalMsgCount uint32) (*asyncAdaptiveLogger, error) {
|
||||
|
||||
if minInterval <= 0 {
|
||||
return nil, errors.New("async adaptive logger min interval should be > 0")
|
||||
}
|
||||
|
||||
if maxInterval > adaptiveLoggerMaxInterval {
|
||||
return nil, fmt.Errorf("async adaptive logger max interval should be <= %s",
|
||||
adaptiveLoggerMaxInterval)
|
||||
}
|
||||
|
||||
if criticalMsgCount <= 0 {
|
||||
return nil, errors.New("async adaptive logger critical msg count should be > 0")
|
||||
}
|
||||
|
||||
if criticalMsgCount > adaptiveLoggerMaxCriticalMsgCount {
|
||||
return nil, fmt.Errorf("async adaptive logger critical msg count should be <= %s",
|
||||
adaptiveLoggerMaxInterval)
|
||||
}
|
||||
|
||||
asnAdaptiveLogger := new(asyncAdaptiveLogger)
|
||||
|
||||
asnAdaptiveLogger.asyncLogger = *newAsyncLogger(config)
|
||||
asnAdaptiveLogger.minInterval = minInterval
|
||||
asnAdaptiveLogger.maxInterval = maxInterval
|
||||
asnAdaptiveLogger.criticalMsgCount = criticalMsgCount
|
||||
|
||||
go asnAdaptiveLogger.processQueue()
|
||||
|
||||
return asnAdaptiveLogger, nil
|
||||
}
|
||||
|
||||
func (asnAdaptiveLogger *asyncAdaptiveLogger) processItem() (closed bool, itemCount int) {
|
||||
asnAdaptiveLogger.queueHasElements.L.Lock()
|
||||
defer asnAdaptiveLogger.queueHasElements.L.Unlock()
|
||||
|
||||
for asnAdaptiveLogger.msgQueue.Len() == 0 && !asnAdaptiveLogger.Closed() {
|
||||
asnAdaptiveLogger.queueHasElements.Wait()
|
||||
}
|
||||
|
||||
if asnAdaptiveLogger.Closed() {
|
||||
return true, asnAdaptiveLogger.msgQueue.Len()
|
||||
}
|
||||
|
||||
asnAdaptiveLogger.processQueueElement()
|
||||
return false, asnAdaptiveLogger.msgQueue.Len() - 1
|
||||
}
|
||||
|
||||
// I = m + (C - Min(c, C)) / C * (M - m) =>
|
||||
// I = m + cDiff * mDiff,
|
||||
// cDiff = (C - Min(c, C)) / C)
|
||||
// mDiff = (M - m)
|
||||
func (asnAdaptiveLogger *asyncAdaptiveLogger) calcAdaptiveInterval(msgCount int) time.Duration {
|
||||
critCountF := float64(asnAdaptiveLogger.criticalMsgCount)
|
||||
cDiff := (critCountF - math.Min(float64(msgCount), critCountF)) / critCountF
|
||||
mDiff := float64(asnAdaptiveLogger.maxInterval - asnAdaptiveLogger.minInterval)
|
||||
|
||||
return asnAdaptiveLogger.minInterval + time.Duration(cDiff*mDiff)
|
||||
}
|
||||
|
||||
func (asnAdaptiveLogger *asyncAdaptiveLogger) processQueue() {
|
||||
for !asnAdaptiveLogger.Closed() {
|
||||
closed, itemCount := asnAdaptiveLogger.processItem()
|
||||
|
||||
if closed {
|
||||
break
|
||||
}
|
||||
|
||||
interval := asnAdaptiveLogger.calcAdaptiveInterval(itemCount)
|
||||
|
||||
<-time.After(interval)
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MaxQueueSize is the critical number of messages in the queue that result in an immediate flush.
|
||||
const (
|
||||
MaxQueueSize = 10000
|
||||
)
|
||||
|
||||
type msgQueueItem struct {
|
||||
level LogLevel
|
||||
context LogContextInterface
|
||||
message fmt.Stringer
|
||||
}
|
||||
|
||||
// asyncLogger represents common data for all asynchronous loggers
|
||||
type asyncLogger struct {
|
||||
commonLogger
|
||||
msgQueue *list.List
|
||||
queueHasElements *sync.Cond
|
||||
}
|
||||
|
||||
// newAsyncLogger creates a new asynchronous logger
|
||||
func newAsyncLogger(config *logConfig) *asyncLogger {
|
||||
asnLogger := new(asyncLogger)
|
||||
|
||||
asnLogger.msgQueue = list.New()
|
||||
asnLogger.queueHasElements = sync.NewCond(new(sync.Mutex))
|
||||
|
||||
asnLogger.commonLogger = *newCommonLogger(config, asnLogger)
|
||||
|
||||
return asnLogger
|
||||
}
|
||||
|
||||
func (asnLogger *asyncLogger) innerLog(
|
||||
level LogLevel,
|
||||
context LogContextInterface,
|
||||
message fmt.Stringer) {
|
||||
|
||||
asnLogger.addMsgToQueue(level, context, message)
|
||||
}
|
||||
|
||||
func (asnLogger *asyncLogger) Close() {
|
||||
asnLogger.m.Lock()
|
||||
defer asnLogger.m.Unlock()
|
||||
|
||||
if !asnLogger.Closed() {
|
||||
asnLogger.flushQueue(true)
|
||||
asnLogger.config.RootDispatcher.Flush()
|
||||
|
||||
if err := asnLogger.config.RootDispatcher.Close(); err != nil {
|
||||
reportInternalError(err)
|
||||
}
|
||||
|
||||
asnLogger.closedM.Lock()
|
||||
asnLogger.closed = true
|
||||
asnLogger.closedM.Unlock()
|
||||
asnLogger.queueHasElements.Broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
func (asnLogger *asyncLogger) Flush() {
|
||||
asnLogger.m.Lock()
|
||||
defer asnLogger.m.Unlock()
|
||||
|
||||
if !asnLogger.Closed() {
|
||||
asnLogger.flushQueue(true)
|
||||
asnLogger.config.RootDispatcher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (asnLogger *asyncLogger) flushQueue(lockNeeded bool) {
|
||||
if lockNeeded {
|
||||
asnLogger.queueHasElements.L.Lock()
|
||||
defer asnLogger.queueHasElements.L.Unlock()
|
||||
}
|
||||
|
||||
for asnLogger.msgQueue.Len() > 0 {
|
||||
asnLogger.processQueueElement()
|
||||
}
|
||||
}
|
||||
|
||||
func (asnLogger *asyncLogger) processQueueElement() {
|
||||
if asnLogger.msgQueue.Len() > 0 {
|
||||
backElement := asnLogger.msgQueue.Front()
|
||||
msg, _ := backElement.Value.(msgQueueItem)
|
||||
asnLogger.processLogMsg(msg.level, msg.message, msg.context)
|
||||
asnLogger.msgQueue.Remove(backElement)
|
||||
}
|
||||
}
|
||||
|
||||
func (asnLogger *asyncLogger) addMsgToQueue(
|
||||
level LogLevel,
|
||||
context LogContextInterface,
|
||||
message fmt.Stringer) {
|
||||
|
||||
if !asnLogger.Closed() {
|
||||
asnLogger.queueHasElements.L.Lock()
|
||||
defer asnLogger.queueHasElements.L.Unlock()
|
||||
|
||||
if asnLogger.msgQueue.Len() >= MaxQueueSize {
|
||||
fmt.Printf("Seelog queue overflow: more than %v messages in the queue. Flushing.\n", MaxQueueSize)
|
||||
asnLogger.flushQueue(false)
|
||||
}
|
||||
|
||||
queueItem := msgQueueItem{level, context, message}
|
||||
|
||||
asnLogger.msgQueue.PushBack(queueItem)
|
||||
asnLogger.queueHasElements.Broadcast()
|
||||
} else {
|
||||
err := fmt.Errorf("queue closed! Cannot process element: %d %#v", level, message)
|
||||
reportInternalError(err)
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
// asyncLoopLogger represents asynchronous logger which processes the log queue in
|
||||
// a 'for' loop
|
||||
type asyncLoopLogger struct {
|
||||
asyncLogger
|
||||
}
|
||||
|
||||
// NewAsyncLoopLogger creates a new asynchronous loop logger
|
||||
func NewAsyncLoopLogger(config *logConfig) *asyncLoopLogger {
|
||||
|
||||
asnLoopLogger := new(asyncLoopLogger)
|
||||
|
||||
asnLoopLogger.asyncLogger = *newAsyncLogger(config)
|
||||
|
||||
go asnLoopLogger.processQueue()
|
||||
|
||||
return asnLoopLogger
|
||||
}
|
||||
|
||||
func (asnLoopLogger *asyncLoopLogger) processItem() (closed bool) {
|
||||
asnLoopLogger.queueHasElements.L.Lock()
|
||||
defer asnLoopLogger.queueHasElements.L.Unlock()
|
||||
|
||||
for asnLoopLogger.msgQueue.Len() == 0 && !asnLoopLogger.Closed() {
|
||||
asnLoopLogger.queueHasElements.Wait()
|
||||
}
|
||||
|
||||
if asnLoopLogger.Closed() {
|
||||
return true
|
||||
}
|
||||
|
||||
asnLoopLogger.processQueueElement()
|
||||
return false
|
||||
}
|
||||
|
||||
func (asnLoopLogger *asyncLoopLogger) processQueue() {
|
||||
for !asnLoopLogger.Closed() {
|
||||
closed := asnLoopLogger.processItem()
|
||||
|
||||
if closed {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// asyncTimerLogger represents asynchronous logger which processes the log queue each
|
||||
// 'duration' nanoseconds
|
||||
type asyncTimerLogger struct {
|
||||
asyncLogger
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
// NewAsyncLoopLogger creates a new asynchronous loop logger
|
||||
func NewAsyncTimerLogger(config *logConfig, interval time.Duration) (*asyncTimerLogger, error) {
|
||||
|
||||
if interval <= 0 {
|
||||
return nil, errors.New("async logger interval should be > 0")
|
||||
}
|
||||
|
||||
asnTimerLogger := new(asyncTimerLogger)
|
||||
|
||||
asnTimerLogger.asyncLogger = *newAsyncLogger(config)
|
||||
asnTimerLogger.interval = interval
|
||||
|
||||
go asnTimerLogger.processQueue()
|
||||
|
||||
return asnTimerLogger, nil
|
||||
}
|
||||
|
||||
func (asnTimerLogger *asyncTimerLogger) processItem() (closed bool) {
|
||||
asnTimerLogger.queueHasElements.L.Lock()
|
||||
defer asnTimerLogger.queueHasElements.L.Unlock()
|
||||
|
||||
for asnTimerLogger.msgQueue.Len() == 0 && !asnTimerLogger.Closed() {
|
||||
asnTimerLogger.queueHasElements.Wait()
|
||||
}
|
||||
|
||||
if asnTimerLogger.Closed() {
|
||||
return true
|
||||
}
|
||||
|
||||
asnTimerLogger.processQueueElement()
|
||||
return false
|
||||
}
|
||||
|
||||
func (asnTimerLogger *asyncTimerLogger) processQueue() {
|
||||
for !asnTimerLogger.Closed() {
|
||||
closed := asnTimerLogger.processItem()
|
||||
|
||||
if closed {
|
||||
break
|
||||
}
|
||||
|
||||
<-time.After(asnTimerLogger.interval)
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// syncLogger performs logging in the same goroutine where 'Trace/Debug/...'
|
||||
// func was called
|
||||
type syncLogger struct {
|
||||
commonLogger
|
||||
}
|
||||
|
||||
// NewSyncLogger creates a new synchronous logger
|
||||
func NewSyncLogger(config *logConfig) *syncLogger {
|
||||
syncLogger := new(syncLogger)
|
||||
|
||||
syncLogger.commonLogger = *newCommonLogger(config, syncLogger)
|
||||
|
||||
return syncLogger
|
||||
}
|
||||
|
||||
func (syncLogger *syncLogger) innerLog(
|
||||
level LogLevel,
|
||||
context LogContextInterface,
|
||||
message fmt.Stringer) {
|
||||
|
||||
syncLogger.processLogMsg(level, message, context)
|
||||
}
|
||||
|
||||
func (syncLogger *syncLogger) Close() {
|
||||
syncLogger.m.Lock()
|
||||
defer syncLogger.m.Unlock()
|
||||
|
||||
if !syncLogger.Closed() {
|
||||
if err := syncLogger.config.RootDispatcher.Close(); err != nil {
|
||||
reportInternalError(err)
|
||||
}
|
||||
syncLogger.closedM.Lock()
|
||||
syncLogger.closed = true
|
||||
syncLogger.closedM.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (syncLogger *syncLogger) Flush() {
|
||||
syncLogger.m.Lock()
|
||||
defer syncLogger.m.Unlock()
|
||||
|
||||
if !syncLogger.Closed() {
|
||||
syncLogger.config.RootDispatcher.Flush()
|
||||
}
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// LoggerFromConfigAsFile creates logger with config from file. File should contain valid seelog xml.
|
||||
func LoggerFromConfigAsFile(fileName string) (LoggerInterface, error) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
conf, err := configFromReader(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createLoggerFromFullConfig(conf)
|
||||
}
|
||||
|
||||
// LoggerFromConfigAsBytes creates a logger with config from bytes stream. Bytes should contain valid seelog xml.
|
||||
func LoggerFromConfigAsBytes(data []byte) (LoggerInterface, error) {
|
||||
conf, err := configFromReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createLoggerFromFullConfig(conf)
|
||||
}
|
||||
|
||||
// LoggerFromConfigAsString creates a logger with config from a string. String should contain valid seelog xml.
|
||||
func LoggerFromConfigAsString(data string) (LoggerInterface, error) {
|
||||
return LoggerFromConfigAsBytes([]byte(data))
|
||||
}
|
||||
|
||||
// LoggerFromParamConfigAsFile does the same as LoggerFromConfigAsFile, but includes special parser options.
|
||||
// See 'CfgParseParams' comments.
|
||||
func LoggerFromParamConfigAsFile(fileName string, parserParams *CfgParseParams) (LoggerInterface, error) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
conf, err := configFromReaderWithConfig(file, parserParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createLoggerFromFullConfig(conf)
|
||||
}
|
||||
|
||||
// LoggerFromParamConfigAsBytes does the same as LoggerFromConfigAsBytes, but includes special parser options.
|
||||
// See 'CfgParseParams' comments.
|
||||
func LoggerFromParamConfigAsBytes(data []byte, parserParams *CfgParseParams) (LoggerInterface, error) {
|
||||
conf, err := configFromReaderWithConfig(bytes.NewBuffer(data), parserParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createLoggerFromFullConfig(conf)
|
||||
}
|
||||
|
||||
// LoggerFromParamConfigAsString does the same as LoggerFromConfigAsString, but includes special parser options.
|
||||
// See 'CfgParseParams' comments.
|
||||
func LoggerFromParamConfigAsString(data string, parserParams *CfgParseParams) (LoggerInterface, error) {
|
||||
return LoggerFromParamConfigAsBytes([]byte(data), parserParams)
|
||||
}
|
||||
|
||||
// LoggerFromWriterWithMinLevel is shortcut for LoggerFromWriterWithMinLevelAndFormat(output, minLevel, DefaultMsgFormat)
|
||||
func LoggerFromWriterWithMinLevel(output io.Writer, minLevel LogLevel) (LoggerInterface, error) {
|
||||
return LoggerFromWriterWithMinLevelAndFormat(output, minLevel, DefaultMsgFormat)
|
||||
}
|
||||
|
||||
// LoggerFromWriterWithMinLevelAndFormat creates a proxy logger that uses io.Writer as the
|
||||
// receiver with minimal level = minLevel and with specified format.
|
||||
//
|
||||
// All messages with level more or equal to minLevel will be written to output and
|
||||
// formatted using the default seelog format.
|
||||
//
|
||||
// Can be called for usage with non-Seelog systems
|
||||
func LoggerFromWriterWithMinLevelAndFormat(output io.Writer, minLevel LogLevel, format string) (LoggerInterface, error) {
|
||||
constraints, err := NewMinMaxConstraints(minLevel, CriticalLvl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
formatter, err := NewFormatter(format)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dispatcher, err := NewSplitDispatcher(formatter, []interface{}{output})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf, err := newFullLoggerConfig(constraints, make([]*LogLevelException, 0), dispatcher, syncloggerTypeFromString, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createLoggerFromFullConfig(conf)
|
||||
}
|
||||
|
||||
// LoggerFromXMLDecoder creates logger with config from a XML decoder starting from a specific node.
|
||||
// It should contain valid seelog xml, except for root node name.
|
||||
func LoggerFromXMLDecoder(xmlParser *xml.Decoder, rootNode xml.Token) (LoggerInterface, error) {
|
||||
conf, err := configFromXMLDecoder(xmlParser, rootNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createLoggerFromFullConfig(conf)
|
||||
}
|
||||
|
||||
// LoggerFromCustomReceiver creates a proxy logger that uses a CustomReceiver as the
|
||||
// receiver.
|
||||
//
|
||||
// All messages will be sent to the specified custom receiver without additional
|
||||
// formatting ('%Msg' format is used).
|
||||
//
|
||||
// Check CustomReceiver, RegisterReceiver for additional info.
|
||||
//
|
||||
// NOTE 1: CustomReceiver.AfterParse is only called when a receiver is instantiated
|
||||
// by the config parser while parsing config. So, if you are not planning to use the
|
||||
// same CustomReceiver for both proxying (via LoggerFromCustomReceiver call) and
|
||||
// loading from config, just leave AfterParse implementation empty.
|
||||
//
|
||||
// NOTE 2: Unlike RegisterReceiver, LoggerFromCustomReceiver takes an already initialized
|
||||
// instance that implements CustomReceiver. So, fill it with data and perform any initialization
|
||||
// logic before calling this func and it won't be lost.
|
||||
//
|
||||
// So:
|
||||
// * RegisterReceiver takes value just to get the reflect.Type from it and then
|
||||
// instantiate it as many times as config is reloaded.
|
||||
//
|
||||
// * LoggerFromCustomReceiver takes value and uses it without modification and
|
||||
// reinstantiation, directy passing it to the dispatcher tree.
|
||||
func LoggerFromCustomReceiver(receiver CustomReceiver) (LoggerInterface, error) {
|
||||
constraints, err := NewMinMaxConstraints(TraceLvl, CriticalLvl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := NewCustomReceiverDispatcherByValue(msgonlyformatter, receiver, "user-proxy", CustomReceiverInitArgs{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dispatcher, err := NewSplitDispatcher(msgonlyformatter, []interface{}{output})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf, err := newFullLoggerConfig(constraints, make([]*LogLevelException, 0), dispatcher, syncloggerTypeFromString, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createLoggerFromFullConfig(conf)
|
||||
}
|
||||
|
||||
func CloneLogger(logger LoggerInterface) (LoggerInterface, error) {
|
||||
switch logger := logger.(type) {
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected type %T", logger)
|
||||
case *asyncAdaptiveLogger:
|
||||
clone, err := NewAsyncAdaptiveLogger(logger.commonLogger.config, logger.minInterval, logger.maxInterval, logger.criticalMsgCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clone, nil
|
||||
case *asyncLoopLogger:
|
||||
return NewAsyncLoopLogger(logger.commonLogger.config), nil
|
||||
case *asyncTimerLogger:
|
||||
clone, err := NewAsyncTimerLogger(logger.commonLogger.config, logger.interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clone, nil
|
||||
case *syncLogger:
|
||||
return NewSyncLogger(logger.commonLogger.config), nil
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errNodeMustHaveChildren = errors.New("node must have children")
|
||||
errNodeCannotHaveChildren = errors.New("node cannot have children")
|
||||
)
|
||||
|
||||
type unexpectedChildElementError struct {
|
||||
baseError
|
||||
}
|
||||
|
||||
func newUnexpectedChildElementError(msg string) *unexpectedChildElementError {
|
||||
custmsg := "Unexpected child element: " + msg
|
||||
return &unexpectedChildElementError{baseError{message: custmsg}}
|
||||
}
|
||||
|
||||
type missingArgumentError struct {
|
||||
baseError
|
||||
}
|
||||
|
||||
func newMissingArgumentError(nodeName, attrName string) *missingArgumentError {
|
||||
custmsg := "Output '" + nodeName + "' has no '" + attrName + "' attribute"
|
||||
return &missingArgumentError{baseError{message: custmsg}}
|
||||
}
|
||||
|
||||
type unexpectedAttributeError struct {
|
||||
baseError
|
||||
}
|
||||
|
||||
func newUnexpectedAttributeError(nodeName, attr string) *unexpectedAttributeError {
|
||||
custmsg := nodeName + " has unexpected attribute: " + attr
|
||||
return &unexpectedAttributeError{baseError{message: custmsg}}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type loggerTypeFromString uint8
|
||||
|
||||
const (
|
||||
syncloggerTypeFromString = iota
|
||||
asyncLooploggerTypeFromString
|
||||
asyncTimerloggerTypeFromString
|
||||
adaptiveLoggerTypeFromString
|
||||
defaultloggerTypeFromString = asyncLooploggerTypeFromString
|
||||
)
|
||||
|
||||
const (
|
||||
syncloggerTypeFromStringStr = "sync"
|
||||
asyncloggerTypeFromStringStr = "asyncloop"
|
||||
asyncTimerloggerTypeFromStringStr = "asynctimer"
|
||||
adaptiveLoggerTypeFromStringStr = "adaptive"
|
||||
)
|
||||
|
||||
// asyncTimerLoggerData represents specific data for async timer logger
|
||||
type asyncTimerLoggerData struct {
|
||||
AsyncInterval uint32
|
||||
}
|
||||
|
||||
// adaptiveLoggerData represents specific data for adaptive timer logger
|
||||
type adaptiveLoggerData struct {
|
||||
MinInterval uint32
|
||||
MaxInterval uint32
|
||||
CriticalMsgCount uint32
|
||||
}
|
||||
|
||||
var loggerTypeToStringRepresentations = map[loggerTypeFromString]string{
|
||||
syncloggerTypeFromString: syncloggerTypeFromStringStr,
|
||||
asyncLooploggerTypeFromString: asyncloggerTypeFromStringStr,
|
||||
asyncTimerloggerTypeFromString: asyncTimerloggerTypeFromStringStr,
|
||||
adaptiveLoggerTypeFromString: adaptiveLoggerTypeFromStringStr,
|
||||
}
|
||||
|
||||
// getLoggerTypeFromString parses a string and returns a corresponding logger type, if successful.
|
||||
func getLoggerTypeFromString(logTypeString string) (level loggerTypeFromString, found bool) {
|
||||
for logType, logTypeStr := range loggerTypeToStringRepresentations {
|
||||
if logTypeStr == logTypeString {
|
||||
return logType, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// logConfig stores logging configuration. Contains messages dispatcher, allowed log level rules
|
||||
// (general constraints and exceptions)
|
||||
type logConfig struct {
|
||||
Constraints logLevelConstraints // General log level rules (>min and <max, or set of allowed levels)
|
||||
Exceptions []*LogLevelException // Exceptions to general rules for specific files or funcs
|
||||
RootDispatcher dispatcherInterface // Root of output tree
|
||||
}
|
||||
|
||||
func NewLoggerConfig(c logLevelConstraints, e []*LogLevelException, d dispatcherInterface) *logConfig {
|
||||
return &logConfig{c, e, d}
|
||||
}
|
||||
|
||||
// configForParsing is used when parsing config from file: logger type is deduced from string, params
|
||||
// need to be converted from attributes to values and passed to specific logger constructor. Also,
|
||||
// custom registered receivers and other parse params are used in this case.
|
||||
type configForParsing struct {
|
||||
logConfig
|
||||
LogType loggerTypeFromString
|
||||
LoggerData interface{}
|
||||
Params *CfgParseParams // Check cfg_parser: CfgParseParams
|
||||
}
|
||||
|
||||
func newFullLoggerConfig(
|
||||
constraints logLevelConstraints,
|
||||
exceptions []*LogLevelException,
|
||||
rootDispatcher dispatcherInterface,
|
||||
logType loggerTypeFromString,
|
||||
logData interface{},
|
||||
cfgParams *CfgParseParams) (*configForParsing, error) {
|
||||
if constraints == nil {
|
||||
return nil, errors.New("constraints can not be nil")
|
||||
}
|
||||
if rootDispatcher == nil {
|
||||
return nil, errors.New("rootDispatcher can not be nil")
|
||||
}
|
||||
|
||||
config := new(configForParsing)
|
||||
config.Constraints = constraints
|
||||
config.Exceptions = exceptions
|
||||
config.RootDispatcher = rootDispatcher
|
||||
config.LogType = logType
|
||||
config.LoggerData = logData
|
||||
config.Params = cfgParams
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// IsAllowed returns true if logging with specified log level is allowed in current context.
|
||||
// If any of exception patterns match current context, then exception constraints are applied. Otherwise,
|
||||
// the general constraints are used.
|
||||
func (config *logConfig) IsAllowed(level LogLevel, context LogContextInterface) bool {
|
||||
allowed := config.Constraints.IsAllowed(level) // General rule
|
||||
|
||||
// Exceptions:
|
||||
if context.IsValid() {
|
||||
for _, exception := range config.Exceptions {
|
||||
if exception.MatchesContext(context) {
|
||||
return exception.IsAllowed(level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allowed
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
|
@ -1,162 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Represents constraints which form a general rule for log levels selection
|
||||
type logLevelConstraints interface {
|
||||
IsAllowed(level LogLevel) bool
|
||||
}
|
||||
|
||||
// A minMaxConstraints represents constraints which use minimal and maximal allowed log levels.
|
||||
type minMaxConstraints struct {
|
||||
min LogLevel
|
||||
max LogLevel
|
||||
}
|
||||
|
||||
// NewMinMaxConstraints creates a new minMaxConstraints struct with the specified min and max levels.
|
||||
func NewMinMaxConstraints(min LogLevel, max LogLevel) (*minMaxConstraints, error) {
|
||||
if min > max {
|
||||
return nil, fmt.Errorf("min level can't be greater than max. Got min: %d, max: %d", min, max)
|
||||
}
|
||||
if min < TraceLvl || min > CriticalLvl {
|
||||
return nil, fmt.Errorf("min level can't be less than Trace or greater than Critical. Got min: %d", min)
|
||||
}
|
||||
if max < TraceLvl || max > CriticalLvl {
|
||||
return nil, fmt.Errorf("max level can't be less than Trace or greater than Critical. Got max: %d", max)
|
||||
}
|
||||
|
||||
return &minMaxConstraints{min, max}, nil
|
||||
}
|
||||
|
||||
// IsAllowed returns true, if log level is in [min, max] range (inclusive).
|
||||
func (minMaxConstr *minMaxConstraints) IsAllowed(level LogLevel) bool {
|
||||
return level >= minMaxConstr.min && level <= minMaxConstr.max
|
||||
}
|
||||
|
||||
func (minMaxConstr *minMaxConstraints) String() string {
|
||||
return fmt.Sprintf("Min: %s. Max: %s", minMaxConstr.min, minMaxConstr.max)
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
|
||||
// A listConstraints represents constraints which use allowed log levels list.
|
||||
type listConstraints struct {
|
||||
allowedLevels map[LogLevel]bool
|
||||
}
|
||||
|
||||
// NewListConstraints creates a new listConstraints struct with the specified allowed levels.
|
||||
func NewListConstraints(allowList []LogLevel) (*listConstraints, error) {
|
||||
if allowList == nil {
|
||||
return nil, errors.New("list can't be nil")
|
||||
}
|
||||
|
||||
allowLevels, err := createMapFromList(allowList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = validateOffLevel(allowLevels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &listConstraints{allowLevels}, nil
|
||||
}
|
||||
|
||||
func (listConstr *listConstraints) String() string {
|
||||
allowedList := "List: "
|
||||
|
||||
listLevel := make([]string, len(listConstr.allowedLevels))
|
||||
|
||||
var logLevel LogLevel
|
||||
i := 0
|
||||
for logLevel = TraceLvl; logLevel <= Off; logLevel++ {
|
||||
if listConstr.allowedLevels[logLevel] {
|
||||
listLevel[i] = logLevel.String()
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
allowedList += strings.Join(listLevel, ",")
|
||||
|
||||
return allowedList
|
||||
}
|
||||
|
||||
func createMapFromList(allowedList []LogLevel) (map[LogLevel]bool, error) {
|
||||
allowedLevels := make(map[LogLevel]bool, 0)
|
||||
for _, level := range allowedList {
|
||||
if level < TraceLvl || level > Off {
|
||||
return nil, fmt.Errorf("level can't be less than Trace or greater than Critical. Got level: %d", level)
|
||||
}
|
||||
allowedLevels[level] = true
|
||||
}
|
||||
return allowedLevels, nil
|
||||
}
|
||||
func validateOffLevel(allowedLevels map[LogLevel]bool) error {
|
||||
if _, ok := allowedLevels[Off]; ok && len(allowedLevels) > 1 {
|
||||
return errors.New("logLevel Off cant be mixed with other levels")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsAllowed returns true, if log level is in allowed log levels list.
|
||||
// If the list contains the only item 'common.Off' then IsAllowed will always return false for any input values.
|
||||
func (listConstr *listConstraints) IsAllowed(level LogLevel) bool {
|
||||
for l := range listConstr.allowedLevels {
|
||||
if l == level && level != Off {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// AllowedLevels returns allowed levels configuration as a map.
|
||||
func (listConstr *listConstraints) AllowedLevels() map[LogLevel]bool {
|
||||
return listConstr.allowedLevels
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
|
||||
type offConstraints struct {
|
||||
}
|
||||
|
||||
func NewOffConstraints() (*offConstraints, error) {
|
||||
return &offConstraints{}, nil
|
||||
}
|
||||
|
||||
func (offConstr *offConstraints) IsAllowed(level LogLevel) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (offConstr *offConstraints) String() string {
|
||||
return "Off constraint"
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
workingDir = "/"
|
||||
stackCache map[uintptr]*logContext
|
||||
stackCacheLock sync.RWMutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
wd, err := os.Getwd()
|
||||
if err == nil {
|
||||
workingDir = filepath.ToSlash(wd) + "/"
|
||||
}
|
||||
stackCache = make(map[uintptr]*logContext)
|
||||
}
|
||||
|
||||
// Represents runtime caller context.
|
||||
type LogContextInterface interface {
|
||||
// Caller's function name.
|
||||
Func() string
|
||||
// Caller's line number.
|
||||
Line() int
|
||||
// Caller's file short path (in slashed form).
|
||||
ShortPath() string
|
||||
// Caller's file full path (in slashed form).
|
||||
FullPath() string
|
||||
// Caller's file name (without path).
|
||||
FileName() string
|
||||
// True if the context is correct and may be used.
|
||||
// If false, then an error in context evaluation occurred and
|
||||
// all its other data may be corrupted.
|
||||
IsValid() bool
|
||||
// Time when log function was called.
|
||||
CallTime() time.Time
|
||||
// Custom context that can be set by calling logger.SetContext
|
||||
CustomContext() interface{}
|
||||
}
|
||||
|
||||
// Returns context of the caller
|
||||
func currentContext(custom interface{}) (LogContextInterface, error) {
|
||||
return specifyContext(1, custom)
|
||||
}
|
||||
|
||||
func extractCallerInfo(skip int) (*logContext, error) {
|
||||
var stack [1]uintptr
|
||||
if runtime.Callers(skip+1, stack[:]) != 1 {
|
||||
return nil, errors.New("error during runtime.Callers")
|
||||
}
|
||||
pc := stack[0]
|
||||
|
||||
// do we have a cache entry?
|
||||
stackCacheLock.RLock()
|
||||
ctx, ok := stackCache[pc]
|
||||
stackCacheLock.RUnlock()
|
||||
if ok {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// look up the details of the given caller
|
||||
funcInfo := runtime.FuncForPC(pc)
|
||||
if funcInfo == nil {
|
||||
return nil, errors.New("error during runtime.FuncForPC")
|
||||
}
|
||||
|
||||
var shortPath string
|
||||
fullPath, line := funcInfo.FileLine(pc)
|
||||
if strings.HasPrefix(fullPath, workingDir) {
|
||||
shortPath = fullPath[len(workingDir):]
|
||||
} else {
|
||||
shortPath = fullPath
|
||||
}
|
||||
funcName := funcInfo.Name()
|
||||
if strings.HasPrefix(funcName, workingDir) {
|
||||
funcName = funcName[len(workingDir):]
|
||||
}
|
||||
|
||||
ctx = &logContext{
|
||||
funcName: funcName,
|
||||
line: line,
|
||||
shortPath: shortPath,
|
||||
fullPath: fullPath,
|
||||
fileName: filepath.Base(fullPath),
|
||||
}
|
||||
|
||||
// save the details in the cache; note that it's possible we might
|
||||
// have written an entry into the map in between the test above and
|
||||
// this section, but the behaviour is still correct
|
||||
stackCacheLock.Lock()
|
||||
stackCache[pc] = ctx
|
||||
stackCacheLock.Unlock()
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Returns context of the function with placed "skip" stack frames of the caller
|
||||
// If skip == 0 then behaves like currentContext
|
||||
// Context is returned in any situation, even if error occurs. But, if an error
|
||||
// occurs, the returned context is an error context, which contains no paths
|
||||
// or names, but states that they can't be extracted.
|
||||
func specifyContext(skip int, custom interface{}) (LogContextInterface, error) {
|
||||
callTime := time.Now()
|
||||
if skip < 0 {
|
||||
err := fmt.Errorf("can not skip negative stack frames")
|
||||
return &errorContext{callTime, err}, err
|
||||
}
|
||||
caller, err := extractCallerInfo(skip + 2)
|
||||
if err != nil {
|
||||
return &errorContext{callTime, err}, err
|
||||
}
|
||||
ctx := new(logContext)
|
||||
*ctx = *caller
|
||||
ctx.callTime = callTime
|
||||
ctx.custom = custom
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Represents a normal runtime caller context.
|
||||
type logContext struct {
|
||||
funcName string
|
||||
line int
|
||||
shortPath string
|
||||
fullPath string
|
||||
fileName string
|
||||
callTime time.Time
|
||||
custom interface{}
|
||||
}
|
||||
|
||||
func (context *logContext) IsValid() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (context *logContext) Func() string {
|
||||
return context.funcName
|
||||
}
|
||||
|
||||
func (context *logContext) Line() int {
|
||||
return context.line
|
||||
}
|
||||
|
||||
func (context *logContext) ShortPath() string {
|
||||
return context.shortPath
|
||||
}
|
||||
|
||||
func (context *logContext) FullPath() string {
|
||||
return context.fullPath
|
||||
}
|
||||
|
||||
func (context *logContext) FileName() string {
|
||||
return context.fileName
|
||||
}
|
||||
|
||||
func (context *logContext) CallTime() time.Time {
|
||||
return context.callTime
|
||||
}
|
||||
|
||||
func (context *logContext) CustomContext() interface{} {
|
||||
return context.custom
|
||||
}
|
||||
|
||||
// Represents an error context
|
||||
type errorContext struct {
|
||||
errorTime time.Time
|
||||
err error
|
||||
}
|
||||
|
||||
func (errContext *errorContext) getErrorText(prefix string) string {
|
||||
return fmt.Sprintf("%s() error: %s", prefix, errContext.err)
|
||||
}
|
||||
|
||||
func (errContext *errorContext) IsValid() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (errContext *errorContext) Line() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (errContext *errorContext) Func() string {
|
||||
return errContext.getErrorText("Func")
|
||||
}
|
||||
|
||||
func (errContext *errorContext) ShortPath() string {
|
||||
return errContext.getErrorText("ShortPath")
|
||||
}
|
||||
|
||||
func (errContext *errorContext) FullPath() string {
|
||||
return errContext.getErrorText("FullPath")
|
||||
}
|
||||
|
||||
func (errContext *errorContext) FileName() string {
|
||||
return errContext.getErrorText("FileName")
|
||||
}
|
||||
|
||||
func (errContext *errorContext) CallTime() time.Time {
|
||||
return errContext.errorTime
|
||||
}
|
||||
|
||||
func (errContext *errorContext) CustomContext() interface{} {
|
||||
return nil
|
||||
}
|
|
@ -1,194 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Used in rules creation to validate input file and func filters
|
||||
var (
|
||||
fileFormatValidator = regexp.MustCompile(`[a-zA-Z0-9\\/ _\*\.]*`)
|
||||
funcFormatValidator = regexp.MustCompile(`[a-zA-Z0-9_\*\.]*`)
|
||||
)
|
||||
|
||||
// LogLevelException represents an exceptional case used when you need some specific files or funcs to
|
||||
// override general constraints and to use their own.
|
||||
type LogLevelException struct {
|
||||
funcPatternParts []string
|
||||
filePatternParts []string
|
||||
|
||||
funcPattern string
|
||||
filePattern string
|
||||
|
||||
constraints logLevelConstraints
|
||||
}
|
||||
|
||||
// NewLogLevelException creates a new exception.
|
||||
func NewLogLevelException(funcPattern string, filePattern string, constraints logLevelConstraints) (*LogLevelException, error) {
|
||||
if constraints == nil {
|
||||
return nil, errors.New("constraints can not be nil")
|
||||
}
|
||||
|
||||
exception := new(LogLevelException)
|
||||
|
||||
err := exception.initFuncPatternParts(funcPattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exception.funcPattern = strings.Join(exception.funcPatternParts, "")
|
||||
|
||||
err = exception.initFilePatternParts(filePattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exception.filePattern = strings.Join(exception.filePatternParts, "")
|
||||
|
||||
exception.constraints = constraints
|
||||
|
||||
return exception, nil
|
||||
}
|
||||
|
||||
// MatchesContext returns true if context matches the patterns of this LogLevelException
|
||||
func (logLevelEx *LogLevelException) MatchesContext(context LogContextInterface) bool {
|
||||
return logLevelEx.match(context.Func(), context.FullPath())
|
||||
}
|
||||
|
||||
// IsAllowed returns true if log level is allowed according to the constraints of this LogLevelException
|
||||
func (logLevelEx *LogLevelException) IsAllowed(level LogLevel) bool {
|
||||
return logLevelEx.constraints.IsAllowed(level)
|
||||
}
|
||||
|
||||
// FuncPattern returns the function pattern of a exception
|
||||
func (logLevelEx *LogLevelException) FuncPattern() string {
|
||||
return logLevelEx.funcPattern
|
||||
}
|
||||
|
||||
// FuncPattern returns the file pattern of a exception
|
||||
func (logLevelEx *LogLevelException) FilePattern() string {
|
||||
return logLevelEx.filePattern
|
||||
}
|
||||
|
||||
// initFuncPatternParts checks whether the func filter has a correct format and splits funcPattern on parts
|
||||
func (logLevelEx *LogLevelException) initFuncPatternParts(funcPattern string) (err error) {
|
||||
|
||||
if funcFormatValidator.FindString(funcPattern) != funcPattern {
|
||||
return errors.New("func path \"" + funcPattern + "\" contains incorrect symbols. Only a-z A-Z 0-9 _ * . allowed)")
|
||||
}
|
||||
|
||||
logLevelEx.funcPatternParts = splitPattern(funcPattern)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks whether the file filter has a correct format and splits file patterns using splitPattern.
|
||||
func (logLevelEx *LogLevelException) initFilePatternParts(filePattern string) (err error) {
|
||||
|
||||
if fileFormatValidator.FindString(filePattern) != filePattern {
|
||||
return errors.New("file path \"" + filePattern + "\" contains incorrect symbols. Only a-z A-Z 0-9 \\ / _ * . allowed)")
|
||||
}
|
||||
|
||||
logLevelEx.filePatternParts = splitPattern(filePattern)
|
||||
return err
|
||||
}
|
||||
|
||||
func (logLevelEx *LogLevelException) match(funcPath string, filePath string) bool {
|
||||
if !stringMatchesPattern(logLevelEx.funcPatternParts, funcPath) {
|
||||
return false
|
||||
}
|
||||
return stringMatchesPattern(logLevelEx.filePatternParts, filePath)
|
||||
}
|
||||
|
||||
func (logLevelEx *LogLevelException) String() string {
|
||||
str := fmt.Sprintf("Func: %s File: %s", logLevelEx.funcPattern, logLevelEx.filePattern)
|
||||
|
||||
if logLevelEx.constraints != nil {
|
||||
str += fmt.Sprintf("Constr: %s", logLevelEx.constraints)
|
||||
} else {
|
||||
str += "nil"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// splitPattern splits pattern into strings and asterisks. Example: "ab*cde**f" -> ["ab", "*", "cde", "*", "f"]
|
||||
func splitPattern(pattern string) []string {
|
||||
var patternParts []string
|
||||
var lastChar rune
|
||||
for _, char := range pattern {
|
||||
if char == '*' {
|
||||
if lastChar != '*' {
|
||||
patternParts = append(patternParts, "*")
|
||||
}
|
||||
} else {
|
||||
if len(patternParts) != 0 && lastChar != '*' {
|
||||
patternParts[len(patternParts)-1] += string(char)
|
||||
} else {
|
||||
patternParts = append(patternParts, string(char))
|
||||
}
|
||||
}
|
||||
lastChar = char
|
||||
}
|
||||
|
||||
return patternParts
|
||||
}
|
||||
|
||||
// stringMatchesPattern check whether testString matches pattern with asterisks.
|
||||
// Standard regexp functionality is not used here because of performance issues.
|
||||
func stringMatchesPattern(patternparts []string, testString string) bool {
|
||||
if len(patternparts) == 0 {
|
||||
return len(testString) == 0
|
||||
}
|
||||
|
||||
part := patternparts[0]
|
||||
if part != "*" {
|
||||
index := strings.Index(testString, part)
|
||||
if index == 0 {
|
||||
return stringMatchesPattern(patternparts[1:], testString[len(part):])
|
||||
}
|
||||
} else {
|
||||
if len(patternparts) == 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
newTestString := testString
|
||||
part = patternparts[1]
|
||||
for {
|
||||
index := strings.Index(newTestString, part)
|
||||
if index == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
newTestString = newTestString[index+len(part):]
|
||||
result := stringMatchesPattern(patternparts[2:], newTestString)
|
||||
if result {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
// flusherInterface represents all objects that have to do cleanup
|
||||
// at certain moments of time (e.g. before app shutdown to avoid data loss)
|
||||
type flusherInterface interface {
|
||||
Flush()
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
// Log level type
|
||||
type LogLevel uint8
|
||||
|
||||
// Log levels
|
||||
const (
|
||||
TraceLvl = iota
|
||||
DebugLvl
|
||||
InfoLvl
|
||||
WarnLvl
|
||||
ErrorLvl
|
||||
CriticalLvl
|
||||
Off
|
||||
)
|
||||
|
||||
// Log level string representations (used in configuration files)
|
||||
const (
|
||||
TraceStr = "trace"
|
||||
DebugStr = "debug"
|
||||
InfoStr = "info"
|
||||
WarnStr = "warn"
|
||||
ErrorStr = "error"
|
||||
CriticalStr = "critical"
|
||||
OffStr = "off"
|
||||
)
|
||||
|
||||
var levelToStringRepresentations = map[LogLevel]string{
|
||||
TraceLvl: TraceStr,
|
||||
DebugLvl: DebugStr,
|
||||
InfoLvl: InfoStr,
|
||||
WarnLvl: WarnStr,
|
||||
ErrorLvl: ErrorStr,
|
||||
CriticalLvl: CriticalStr,
|
||||
Off: OffStr,
|
||||
}
|
||||
|
||||
// LogLevelFromString parses a string and returns a corresponding log level, if sucessfull.
|
||||
func LogLevelFromString(levelStr string) (level LogLevel, found bool) {
|
||||
for lvl, lvlStr := range levelToStringRepresentations {
|
||||
if lvlStr == levelStr {
|
||||
return lvl, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// LogLevelToString returns seelog string representation for a specified level. Returns "" for invalid log levels.
|
||||
func (level LogLevel) String() string {
|
||||
levelStr, ok := levelToStringRepresentations[level]
|
||||
if ok {
|
||||
return levelStr
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
// Copyright (c) 2013 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var registeredReceivers = make(map[string]reflect.Type)
|
||||
|
||||
// RegisterReceiver records a custom receiver type, identified by a value
|
||||
// of that type (second argument), under the specified name. Registered
|
||||
// names can be used in the "name" attribute of <custom> config items.
|
||||
//
|
||||
// RegisterReceiver takes the type of the receiver argument, without taking
|
||||
// the value into the account. So do NOT enter any data to the second argument
|
||||
// and only call it like:
|
||||
// RegisterReceiver("somename", &MyReceiverType{})
|
||||
//
|
||||
// After that, when a '<custom>' config tag with this name is used,
|
||||
// a receiver of the specified type would be instantiated. Check
|
||||
// CustomReceiver comments for interface details.
|
||||
//
|
||||
// NOTE 1: RegisterReceiver fails if you attempt to register different types
|
||||
// with the same name.
|
||||
//
|
||||
// NOTE 2: RegisterReceiver registers those receivers that must be used in
|
||||
// the configuration files (<custom> items). Basically it is just the way
|
||||
// you tell seelog config parser what should it do when it meets a
|
||||
// <custom> tag with a specific name and data attributes.
|
||||
//
|
||||
// But If you are only using seelog as a proxy to an already instantiated
|
||||
// CustomReceiver (via LoggerFromCustomReceiver func), you should not call RegisterReceiver.
|
||||
func RegisterReceiver(name string, receiver CustomReceiver) {
|
||||
newType := reflect.TypeOf(reflect.ValueOf(receiver).Elem().Interface())
|
||||
if t, ok := registeredReceivers[name]; ok && t != newType {
|
||||
panic(fmt.Sprintf("duplicate types for %s: %s != %s", name, t, newType))
|
||||
}
|
||||
registeredReceivers[name] = newType
|
||||
}
|
||||
|
||||
func customReceiverByName(name string) (creceiver CustomReceiver, err error) {
|
||||
rt, ok := registeredReceivers[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("custom receiver name not registered: '%s'", name)
|
||||
}
|
||||
v, ok := reflect.New(rt).Interface().(CustomReceiver)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot instantiate receiver with name='%s'", name)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// CustomReceiverInitArgs represent arguments passed to the CustomReceiver.Init
|
||||
// func when custom receiver is being initialized.
|
||||
type CustomReceiverInitArgs struct {
|
||||
// XmlCustomAttrs represent '<custom>' xml config item attributes that
|
||||
// start with "data-". Map keys will be the attribute names without the "data-".
|
||||
// Map values will the those attribute values.
|
||||
//
|
||||
// E.g. if you have a '<custom name="somename" data-attr1="a1" data-attr2="a2"/>'
|
||||
// you will get map with 2 key-value pairs: "attr1"->"a1", "attr2"->"a2"
|
||||
//
|
||||
// Note that in custom items you can only use allowed attributes, like "name" and
|
||||
// your custom attributes, starting with "data-". Any other will lead to a
|
||||
// parsing error.
|
||||
XmlCustomAttrs map[string]string
|
||||
}
|
||||
|
||||
// CustomReceiver is the interface that external custom seelog message receivers
|
||||
// must implement in order to be able to process seelog messages. Those receivers
|
||||
// are set in the xml config file using the <custom> tag. Check receivers reference
|
||||
// wiki section on that.
|
||||
//
|
||||
// Use seelog.RegisterReceiver on the receiver type before using it.
|
||||
type CustomReceiver interface {
|
||||
// ReceiveMessage is called when the custom receiver gets seelog message from
|
||||
// a parent dispatcher.
|
||||
//
|
||||
// Message, level and context args represent all data that was included in the seelog
|
||||
// message at the time it was logged.
|
||||
//
|
||||
// The formatting is already applied to the message and depends on the config
|
||||
// like with any other receiver.
|
||||
//
|
||||
// If you would like to inform seelog of an error that happened during the handling of
|
||||
// the message, return a non-nil error. This way you'll end up seeing your error like
|
||||
// any other internal seelog error.
|
||||
ReceiveMessage(message string, level LogLevel, context LogContextInterface) error
|
||||
|
||||
// AfterParse is called immediately after your custom receiver is instantiated by
|
||||
// the xml config parser. So, if you need to do any startup logic after config parsing,
|
||||
// like opening file or allocating any resources after the receiver is instantiated, do it here.
|
||||
//
|
||||
// If this func returns a non-nil error, then the loading procedure will fail. E.g.
|
||||
// if you are loading a seelog xml config, the parser would not finish the loading
|
||||
// procedure and inform about an error like with any other config error.
|
||||
//
|
||||
// If your custom logger needs some configuration, you can use custom attributes in
|
||||
// your config. Check CustomReceiverInitArgs.XmlCustomAttrs comments.
|
||||
//
|
||||
// IMPORTANT: This func is NOT called when the LoggerFromCustomReceiver func is used
|
||||
// to create seelog proxy logger using the custom receiver. This func is only called when
|
||||
// receiver is instantiated from a config.
|
||||
AfterParse(initArgs CustomReceiverInitArgs) error
|
||||
|
||||
// Flush is called when the custom receiver gets a 'flush' directive from a
|
||||
// parent receiver. If custom receiver implements some kind of buffering or
|
||||
// queing, then the appropriate reaction on a flush message is synchronous
|
||||
// flushing of all those queues/buffers. If custom receiver doesn't have
|
||||
// such mechanisms, then flush implementation may be left empty.
|
||||
Flush()
|
||||
|
||||
// Close is called when the custom receiver gets a 'close' directive from a
|
||||
// parent receiver. This happens when a top-level seelog dispatcher is sending
|
||||
// 'close' to all child nodes and it means that current seelog logger is being closed.
|
||||
// If you need to do any cleanup after your custom receiver is done, you should do
|
||||
// it here.
|
||||
Close() error
|
||||
}
|
||||
|
||||
type customReceiverDispatcher struct {
|
||||
formatter *formatter
|
||||
innerReceiver CustomReceiver
|
||||
customReceiverName string
|
||||
usedArgs CustomReceiverInitArgs
|
||||
}
|
||||
|
||||
// NewCustomReceiverDispatcher creates a customReceiverDispatcher which dispatches data to a specific receiver created
|
||||
// using a <custom> tag in the config file.
|
||||
func NewCustomReceiverDispatcher(formatter *formatter, customReceiverName string, cArgs CustomReceiverInitArgs) (*customReceiverDispatcher, error) {
|
||||
if formatter == nil {
|
||||
return nil, errors.New("formatter cannot be nil")
|
||||
}
|
||||
if len(customReceiverName) == 0 {
|
||||
return nil, errors.New("custom receiver name cannot be empty")
|
||||
}
|
||||
|
||||
creceiver, err := customReceiverByName(customReceiverName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = creceiver.AfterParse(cArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disp := &customReceiverDispatcher{formatter, creceiver, customReceiverName, cArgs}
|
||||
|
||||
return disp, nil
|
||||
}
|
||||
|
||||
// NewCustomReceiverDispatcherByValue is basically the same as NewCustomReceiverDispatcher, but using
|
||||
// a specific CustomReceiver value instead of instantiating a new one by type.
|
||||
func NewCustomReceiverDispatcherByValue(formatter *formatter, customReceiver CustomReceiver, name string, cArgs CustomReceiverInitArgs) (*customReceiverDispatcher, error) {
|
||||
if formatter == nil {
|
||||
return nil, errors.New("formatter cannot be nil")
|
||||
}
|
||||
if customReceiver == nil {
|
||||
return nil, errors.New("customReceiver cannot be nil")
|
||||
}
|
||||
disp := &customReceiverDispatcher{formatter, customReceiver, name, cArgs}
|
||||
|
||||
return disp, nil
|
||||
}
|
||||
|
||||
// CustomReceiver implementation. Check CustomReceiver comments.
|
||||
func (disp *customReceiverDispatcher) Dispatch(
|
||||
message string,
|
||||
level LogLevel,
|
||||
context LogContextInterface,
|
||||
errorFunc func(err error)) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
errorFunc(fmt.Errorf("panic in custom receiver '%s'.Dispatch: %s", reflect.TypeOf(disp.innerReceiver), err))
|
||||
}
|
||||
}()
|
||||
|
||||
err := disp.innerReceiver.ReceiveMessage(disp.formatter.Format(message, level, context), level, context)
|
||||
if err != nil {
|
||||
errorFunc(err)
|
||||
}
|
||||
}
|
||||
|
||||
// CustomReceiver implementation. Check CustomReceiver comments.
|
||||
func (disp *customReceiverDispatcher) Flush() {
|
||||
disp.innerReceiver.Flush()
|
||||
}
|
||||
|
||||
// CustomReceiver implementation. Check CustomReceiver comments.
|
||||
func (disp *customReceiverDispatcher) Close() error {
|
||||
disp.innerReceiver.Flush()
|
||||
|
||||
err := disp.innerReceiver.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (disp *customReceiverDispatcher) String() string {
|
||||
datas := ""
|
||||
skeys := make([]string, 0, len(disp.usedArgs.XmlCustomAttrs))
|
||||
for i := range disp.usedArgs.XmlCustomAttrs {
|
||||
skeys = append(skeys, i)
|
||||
}
|
||||
sort.Strings(skeys)
|
||||
for _, key := range skeys {
|
||||
datas += fmt.Sprintf("<%s, %s> ", key, disp.usedArgs.XmlCustomAttrs[key])
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("Custom receiver %s [fmt='%s'],[data='%s'],[inner='%s']\n",
|
||||
disp.customReceiverName, disp.formatter.String(), datas, disp.innerReceiver)
|
||||
|
||||
return str
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// A dispatcherInterface is used to dispatch message to all underlying receivers.
|
||||
// Dispatch logic depends on given context and log level. Any errors are reported using errorFunc.
|
||||
// Also, as underlying receivers may have a state, dispatcher has a ShuttingDown method which performs
|
||||
// an immediate cleanup of all data that is stored in the receivers
|
||||
type dispatcherInterface interface {
|
||||
flusherInterface
|
||||
io.Closer
|
||||
Dispatch(message string, level LogLevel, context LogContextInterface, errorFunc func(err error))
|
||||
}
|
||||
|
||||
type dispatcher struct {
|
||||
formatter *formatter
|
||||
writers []*formattedWriter
|
||||
dispatchers []dispatcherInterface
|
||||
}
|
||||
|
||||
// Creates a dispatcher which dispatches data to a list of receivers.
|
||||
// Each receiver should be either a Dispatcher or io.Writer, otherwise an error will be returned
|
||||
func createDispatcher(formatter *formatter, receivers []interface{}) (*dispatcher, error) {
|
||||
if formatter == nil {
|
||||
return nil, errors.New("formatter cannot be nil")
|
||||
}
|
||||
if receivers == nil || len(receivers) == 0 {
|
||||
return nil, errors.New("receivers cannot be nil or empty")
|
||||
}
|
||||
|
||||
disp := &dispatcher{formatter, make([]*formattedWriter, 0), make([]dispatcherInterface, 0)}
|
||||
for _, receiver := range receivers {
|
||||
writer, ok := receiver.(*formattedWriter)
|
||||
if ok {
|
||||
disp.writers = append(disp.writers, writer)
|
||||
continue
|
||||
}
|
||||
|
||||
ioWriter, ok := receiver.(io.Writer)
|
||||
if ok {
|
||||
writer, err := NewFormattedWriter(ioWriter, disp.formatter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disp.writers = append(disp.writers, writer)
|
||||
continue
|
||||
}
|
||||
|
||||
dispInterface, ok := receiver.(dispatcherInterface)
|
||||
if ok {
|
||||
disp.dispatchers = append(disp.dispatchers, dispInterface)
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, errors.New("method can receive either io.Writer or dispatcherInterface")
|
||||
}
|
||||
|
||||
return disp, nil
|
||||
}
|
||||
|
||||
func (disp *dispatcher) Dispatch(
|
||||
message string,
|
||||
level LogLevel,
|
||||
context LogContextInterface,
|
||||
errorFunc func(err error)) {
|
||||
|
||||
for _, writer := range disp.writers {
|
||||
err := writer.Write(message, level, context)
|
||||
if err != nil {
|
||||
errorFunc(err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, dispInterface := range disp.dispatchers {
|
||||
dispInterface.Dispatch(message, level, context, errorFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush goes through all underlying writers which implement flusherInterface interface
|
||||
// and closes them. Recursively performs the same action for underlying dispatchers
|
||||
func (disp *dispatcher) Flush() {
|
||||
for _, disp := range disp.Dispatchers() {
|
||||
disp.Flush()
|
||||
}
|
||||
|
||||
for _, formatWriter := range disp.Writers() {
|
||||
flusher, ok := formatWriter.Writer().(flusherInterface)
|
||||
if ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close goes through all underlying writers which implement io.Closer interface
|
||||
// and closes them. Recursively performs the same action for underlying dispatchers
|
||||
// Before closing, writers are flushed to prevent loss of any buffered data, so
|
||||
// a call to Flush() func before Close() is not necessary
|
||||
func (disp *dispatcher) Close() error {
|
||||
for _, disp := range disp.Dispatchers() {
|
||||
disp.Flush()
|
||||
err := disp.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, formatWriter := range disp.Writers() {
|
||||
flusher, ok := formatWriter.Writer().(flusherInterface)
|
||||
if ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
closer, ok := formatWriter.Writer().(io.Closer)
|
||||
if ok {
|
||||
err := closer.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (disp *dispatcher) Writers() []*formattedWriter {
|
||||
return disp.writers
|
||||
}
|
||||
|
||||
func (disp *dispatcher) Dispatchers() []dispatcherInterface {
|
||||
return disp.dispatchers
|
||||
}
|
||||
|
||||
func (disp *dispatcher) String() string {
|
||||
str := "formatter: " + disp.formatter.String() + "\n"
|
||||
|
||||
str += " ->Dispatchers:"
|
||||
|
||||
if len(disp.dispatchers) == 0 {
|
||||
str += "none\n"
|
||||
} else {
|
||||
str += "\n"
|
||||
|
||||
for _, disp := range disp.dispatchers {
|
||||
str += fmt.Sprintf(" ->%s", disp)
|
||||
}
|
||||
}
|
||||
|
||||
str += " ->Writers:"
|
||||
|
||||
if len(disp.writers) == 0 {
|
||||
str += "none\n"
|
||||
} else {
|
||||
str += "\n"
|
||||
|
||||
for _, writer := range disp.writers {
|
||||
str += fmt.Sprintf(" ->%s\n", writer)
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A filterDispatcher writes the given message to underlying receivers only if message log level
|
||||
// is in the allowed list.
|
||||
type filterDispatcher struct {
|
||||
*dispatcher
|
||||
allowList map[LogLevel]bool
|
||||
}
|
||||
|
||||
// NewFilterDispatcher creates a new filterDispatcher using a list of allowed levels.
|
||||
func NewFilterDispatcher(formatter *formatter, receivers []interface{}, allowList ...LogLevel) (*filterDispatcher, error) {
|
||||
disp, err := createDispatcher(formatter, receivers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allows := make(map[LogLevel]bool)
|
||||
for _, allowLevel := range allowList {
|
||||
allows[allowLevel] = true
|
||||
}
|
||||
|
||||
return &filterDispatcher{disp, allows}, nil
|
||||
}
|
||||
|
||||
func (filter *filterDispatcher) Dispatch(
|
||||
message string,
|
||||
level LogLevel,
|
||||
context LogContextInterface,
|
||||
errorFunc func(err error)) {
|
||||
isAllowed, ok := filter.allowList[level]
|
||||
if ok && isAllowed {
|
||||
filter.dispatcher.Dispatch(message, level, context, errorFunc)
|
||||
}
|
||||
}
|
||||
|
||||
func (filter *filterDispatcher) String() string {
|
||||
return fmt.Sprintf("filterDispatcher ->\n%s", filter.dispatcher)
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A splitDispatcher just writes the given message to underlying receivers. (Splits the message stream.)
|
||||
type splitDispatcher struct {
|
||||
*dispatcher
|
||||
}
|
||||
|
||||
func NewSplitDispatcher(formatter *formatter, receivers []interface{}) (*splitDispatcher, error) {
|
||||
disp, err := createDispatcher(formatter, receivers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &splitDispatcher{disp}, nil
|
||||
}
|
||||
|
||||
func (splitter *splitDispatcher) String() string {
|
||||
return fmt.Sprintf("splitDispatcher ->\n%s", splitter.dispatcher.String())
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
// Copyright (c) 2014 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/*
|
||||
Package seelog implements logging functionality with flexible dispatching, filtering, and formatting.
|
||||
|
||||
Creation
|
||||
|
||||
To create a logger, use one of the following constructors:
|
||||
func LoggerFromConfigAsBytes
|
||||
func LoggerFromConfigAsFile
|
||||
func LoggerFromConfigAsString
|
||||
func LoggerFromWriterWithMinLevel
|
||||
func LoggerFromWriterWithMinLevelAndFormat
|
||||
func LoggerFromCustomReceiver (check https://github.com/cihub/seelog/wiki/Custom-receivers)
|
||||
Example:
|
||||
import log "github.com/cihub/seelog"
|
||||
|
||||
func main() {
|
||||
logger, err := log.LoggerFromConfigAsFile("seelog.xml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer logger.Flush()
|
||||
... use logger ...
|
||||
}
|
||||
The "defer" line is important because if you are using asynchronous logger behavior, without this line you may end up losing some
|
||||
messages when you close your application because they are processed in another non-blocking goroutine. To avoid that you
|
||||
explicitly defer flushing all messages before closing.
|
||||
|
||||
Usage
|
||||
|
||||
Logger created using one of the LoggerFrom* funcs can be used directly by calling one of the main log funcs.
|
||||
Example:
|
||||
import log "github.com/cihub/seelog"
|
||||
|
||||
func main() {
|
||||
logger, err := log.LoggerFromConfigAsFile("seelog.xml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer logger.Flush()
|
||||
logger.Trace("test")
|
||||
logger.Debugf("var = %s", "abc")
|
||||
}
|
||||
|
||||
Having loggers as variables is convenient if you are writing your own package with internal logging or if you have
|
||||
several loggers with different options.
|
||||
But for most standalone apps it is more convenient to use package level funcs and vars. There is a package level
|
||||
var 'Current' made for it. You can replace it with another logger using 'ReplaceLogger' and then use package level funcs:
|
||||
import log "github.com/cihub/seelog"
|
||||
|
||||
func main() {
|
||||
logger, err := log.LoggerFromConfigAsFile("seelog.xml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.ReplaceLogger(logger)
|
||||
defer log.Flush()
|
||||
log.Trace("test")
|
||||
log.Debugf("var = %s", "abc")
|
||||
}
|
||||
Last lines
|
||||
log.Trace("test")
|
||||
log.Debugf("var = %s", "abc")
|
||||
do the same as
|
||||
log.Current.Trace("test")
|
||||
log.Current.Debugf("var = %s", "abc")
|
||||
In this example the 'Current' logger was replaced using a 'ReplaceLogger' call and became equal to 'logger' variable created from config.
|
||||
This way you are able to use package level funcs instead of passing the logger variable.
|
||||
|
||||
Configuration
|
||||
|
||||
Main seelog point is to configure logger via config files and not the code.
|
||||
The configuration is read by LoggerFrom* funcs. These funcs read xml configuration from different sources and try
|
||||
to create a logger using it.
|
||||
|
||||
All the configuration features are covered in detail in the official wiki: https://github.com/cihub/seelog/wiki.
|
||||
There are many sections covering different aspects of seelog, but the most important for understanding configs are:
|
||||
https://github.com/cihub/seelog/wiki/Constraints-and-exceptions
|
||||
https://github.com/cihub/seelog/wiki/Dispatchers-and-receivers
|
||||
https://github.com/cihub/seelog/wiki/Formatting
|
||||
https://github.com/cihub/seelog/wiki/Logger-types
|
||||
After you understand these concepts, check the 'Reference' section on the main wiki page to get the up-to-date
|
||||
list of dispatchers, receivers, formats, and logger types.
|
||||
|
||||
Here is an example config with all these features:
|
||||
<seelog type="adaptive" mininterval="2000000" maxinterval="100000000" critmsgcount="500" minlevel="debug">
|
||||
<exceptions>
|
||||
<exception filepattern="test*" minlevel="error"/>
|
||||
</exceptions>
|
||||
<outputs formatid="all">
|
||||
<file path="all.log"/>
|
||||
<filter levels="info">
|
||||
<console formatid="fmtinfo"/>
|
||||
</filter>
|
||||
<filter levels="error,critical" formatid="fmterror">
|
||||
<console/>
|
||||
<file path="errors.log"/>
|
||||
</filter>
|
||||
</outputs>
|
||||
<formats>
|
||||
<format id="fmtinfo" format="[%Level] [%Time] %Msg%n"/>
|
||||
<format id="fmterror" format="[%LEVEL] [%Time] [%FuncShort @ %File.%Line] %Msg%n"/>
|
||||
<format id="all" format="[%Level] [%Time] [@ %File.%Line] %Msg%n"/>
|
||||
<format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/>
|
||||
</formats>
|
||||
</seelog>
|
||||
This config represents a logger with adaptive timeout between log messages (check logger types reference) which
|
||||
logs to console, all.log, and errors.log depending on the log level. Its output formats also depend on log level. This logger will only
|
||||
use log level 'debug' and higher (minlevel is set) for all files with names that don't start with 'test'. For files starting with 'test'
|
||||
this logger prohibits all levels below 'error'.
|
||||
|
||||
Configuration using code
|
||||
|
||||
Although configuration using code is not recommended, it is sometimes needed and it is possible to do with seelog. Basically, what
|
||||
you need to do to get started is to create constraints, exceptions and a dispatcher tree (same as with config). Most of the New*
|
||||
functions in this package are used to provide such capabilities.
|
||||
|
||||
Here is an example of configuration in code, that demonstrates an async loop logger that logs to a simple split dispatcher with
|
||||
a console receiver using a specified format and is filtered using a top-level min-max constraints and one expection for
|
||||
the 'main.go' file. So, this is basically a demonstration of configuration of most of the features:
|
||||
|
||||
package main
|
||||
|
||||
import log "github.com/cihub/seelog"
|
||||
|
||||
func main() {
|
||||
defer log.Flush()
|
||||
log.Info("Hello from Seelog!")
|
||||
|
||||
consoleWriter, _ := log.NewConsoleWriter()
|
||||
formatter, _ := log.NewFormatter("%Level %Msg %File%n")
|
||||
root, _ := log.NewSplitDispatcher(formatter, []interface{}{consoleWriter})
|
||||
constraints, _ := log.NewMinMaxConstraints(log.TraceLvl, log.CriticalLvl)
|
||||
specificConstraints, _ := log.NewListConstraints([]log.LogLevel{log.InfoLvl, log.ErrorLvl})
|
||||
ex, _ := log.NewLogLevelException("*", "*main.go", specificConstraints)
|
||||
exceptions := []*log.LogLevelException{ex}
|
||||
|
||||
logger := log.NewAsyncLoopLogger(log.NewLoggerConfig(constraints, exceptions, root))
|
||||
log.ReplaceLogger(logger)
|
||||
|
||||
log.Trace("This should not be seen")
|
||||
log.Debug("This should not be seen")
|
||||
log.Info("Test")
|
||||
log.Error("Test2")
|
||||
}
|
||||
|
||||
Examples
|
||||
|
||||
To learn seelog features faster you should check the examples package: https://github.com/cihub/seelog-examples
|
||||
It contains many example configs and usecases.
|
||||
*/
|
||||
package seelog
|
|
@ -1,466 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// FormatterSymbol is a special symbol used in config files to mark special format aliases.
|
||||
const (
|
||||
FormatterSymbol = '%'
|
||||
)
|
||||
|
||||
const (
|
||||
formatterParameterStart = '('
|
||||
formatterParameterEnd = ')'
|
||||
)
|
||||
|
||||
// Time and date formats used for %Date and %Time aliases.
|
||||
const (
|
||||
DateDefaultFormat = "2006-01-02"
|
||||
TimeFormat = "15:04:05"
|
||||
)
|
||||
|
||||
var DefaultMsgFormat = "%Ns [%Level] %Msg%n"
|
||||
|
||||
var (
|
||||
DefaultFormatter *formatter
|
||||
msgonlyformatter *formatter
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if DefaultFormatter, err = NewFormatter(DefaultMsgFormat); err != nil {
|
||||
reportInternalError(fmt.Errorf("error during creating DefaultFormatter: %s", err))
|
||||
}
|
||||
if msgonlyformatter, err = NewFormatter("%Msg"); err != nil {
|
||||
reportInternalError(fmt.Errorf("error during creating msgonlyformatter: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// FormatterFunc represents one formatter object that starts with '%' sign in the 'format' attribute
|
||||
// of the 'format' config item. These special symbols are replaced with context values or special
|
||||
// strings when message is written to byte receiver.
|
||||
//
|
||||
// Check https://github.com/cihub/seelog/wiki/Formatting for details.
|
||||
// Full list (with descriptions) of formatters: https://github.com/cihub/seelog/wiki/Format-reference
|
||||
//
|
||||
// FormatterFunc takes raw log message, level, log context and returns a string, number (of any type) or any object
|
||||
// that can be evaluated as string.
|
||||
type FormatterFunc func(message string, level LogLevel, context LogContextInterface) interface{}
|
||||
|
||||
// FormatterFuncCreator is a factory of FormatterFunc objects. It is used to generate parameterized
|
||||
// formatters (such as %Date or %EscM) and custom user formatters.
|
||||
type FormatterFuncCreator func(param string) FormatterFunc
|
||||
|
||||
var formatterFuncs = map[string]FormatterFunc{
|
||||
"Level": formatterLevel,
|
||||
"Lev": formatterLev,
|
||||
"LEVEL": formatterLEVEL,
|
||||
"LEV": formatterLEV,
|
||||
"l": formatterl,
|
||||
"Msg": formatterMsg,
|
||||
"FullPath": formatterFullPath,
|
||||
"File": formatterFile,
|
||||
"RelFile": formatterRelFile,
|
||||
"Func": FormatterFunction,
|
||||
"FuncShort": FormatterFunctionShort,
|
||||
"Line": formatterLine,
|
||||
"Time": formatterTime,
|
||||
"UTCTime": formatterUTCTime,
|
||||
"Ns": formatterNs,
|
||||
"UTCNs": formatterUTCNs,
|
||||
"r": formatterr,
|
||||
"n": formattern,
|
||||
"t": formattert,
|
||||
}
|
||||
|
||||
var formatterFuncsParameterized = map[string]FormatterFuncCreator{
|
||||
"Date": createDateTimeFormatterFunc,
|
||||
"UTCDate": createUTCDateTimeFormatterFunc,
|
||||
"EscM": createANSIEscapeFunc,
|
||||
}
|
||||
|
||||
func errorAliasReserved(name string) error {
|
||||
return fmt.Errorf("cannot use '%s' as custom formatter name. Name is reserved", name)
|
||||
}
|
||||
|
||||
// RegisterCustomFormatter registers a new custom formatter factory with a given name. If returned error is nil,
|
||||
// then this name (prepended by '%' symbol) can be used in 'format' attributes in configuration and
|
||||
// it will be treated like the standard parameterized formatter identifiers.
|
||||
//
|
||||
// RegisterCustomFormatter needs to be called before creating a logger for it to take effect. The general recommendation
|
||||
// is to call it once in 'init' func of your application or any initializer func.
|
||||
//
|
||||
// For usage examples, check https://github.com/cihub/seelog/wiki/Custom-formatters.
|
||||
//
|
||||
// Name must only consist of letters (unicode.IsLetter).
|
||||
//
|
||||
// Name must not be one of the already registered standard formatter names
|
||||
// (https://github.com/cihub/seelog/wiki/Format-reference) and previously registered
|
||||
// custom format names. To avoid any potential name conflicts (in future releases), it is recommended
|
||||
// to start your custom formatter name with a namespace (e.g. 'MyCompanySomething') or a 'Custom' keyword.
|
||||
func RegisterCustomFormatter(name string, creator FormatterFuncCreator) error {
|
||||
if _, ok := formatterFuncs[name]; ok {
|
||||
return errorAliasReserved(name)
|
||||
}
|
||||
if _, ok := formatterFuncsParameterized[name]; ok {
|
||||
return errorAliasReserved(name)
|
||||
}
|
||||
formatterFuncsParameterized[name] = creator
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatter is used to write messages in a specific format, inserting such additional data
|
||||
// as log level, date/time, etc.
|
||||
type formatter struct {
|
||||
fmtStringOriginal string
|
||||
fmtString string
|
||||
formatterFuncs []FormatterFunc
|
||||
}
|
||||
|
||||
// NewFormatter creates a new formatter using a format string
|
||||
func NewFormatter(formatString string) (*formatter, error) {
|
||||
fmtr := new(formatter)
|
||||
fmtr.fmtStringOriginal = formatString
|
||||
if err := buildFormatterFuncs(fmtr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fmtr, nil
|
||||
}
|
||||
|
||||
func buildFormatterFuncs(formatter *formatter) error {
|
||||
var (
|
||||
fsbuf = new(bytes.Buffer)
|
||||
fsolm1 = len(formatter.fmtStringOriginal) - 1
|
||||
)
|
||||
for i := 0; i <= fsolm1; i++ {
|
||||
if char := formatter.fmtStringOriginal[i]; char != FormatterSymbol {
|
||||
fsbuf.WriteByte(char)
|
||||
continue
|
||||
}
|
||||
// Check if the index is at the end of the string.
|
||||
if i == fsolm1 {
|
||||
return fmt.Errorf("format error: %c cannot be last symbol", FormatterSymbol)
|
||||
}
|
||||
// Check if the formatter symbol is doubled and skip it as nonmatching.
|
||||
if formatter.fmtStringOriginal[i+1] == FormatterSymbol {
|
||||
fsbuf.WriteRune(FormatterSymbol)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
function, ni, err := formatter.extractFormatterFunc(i + 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Append formatting string "%v".
|
||||
fsbuf.Write([]byte{37, 118})
|
||||
i = ni
|
||||
formatter.formatterFuncs = append(formatter.formatterFuncs, function)
|
||||
}
|
||||
formatter.fmtString = fsbuf.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (formatter *formatter) extractFormatterFunc(index int) (FormatterFunc, int, error) {
|
||||
letterSequence := formatter.extractLetterSequence(index)
|
||||
if len(letterSequence) == 0 {
|
||||
return nil, 0, fmt.Errorf("format error: lack of formatter after %c at %d", FormatterSymbol, index)
|
||||
}
|
||||
|
||||
function, formatterLength, ok := formatter.findFormatterFunc(letterSequence)
|
||||
if ok {
|
||||
return function, index + formatterLength - 1, nil
|
||||
}
|
||||
|
||||
function, formatterLength, ok, err := formatter.findFormatterFuncParametrized(letterSequence, index)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if ok {
|
||||
return function, index + formatterLength - 1, nil
|
||||
}
|
||||
|
||||
return nil, 0, errors.New("format error: unrecognized formatter at " + strconv.Itoa(index) + ": " + letterSequence)
|
||||
}
|
||||
|
||||
func (formatter *formatter) extractLetterSequence(index int) string {
|
||||
letters := ""
|
||||
|
||||
bytesToParse := []byte(formatter.fmtStringOriginal[index:])
|
||||
runeCount := utf8.RuneCount(bytesToParse)
|
||||
for i := 0; i < runeCount; i++ {
|
||||
rune, runeSize := utf8.DecodeRune(bytesToParse)
|
||||
bytesToParse = bytesToParse[runeSize:]
|
||||
|
||||
if unicode.IsLetter(rune) {
|
||||
letters += string(rune)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return letters
|
||||
}
|
||||
|
||||
func (formatter *formatter) findFormatterFunc(letters string) (FormatterFunc, int, bool) {
|
||||
currentVerb := letters
|
||||
for i := 0; i < len(letters); i++ {
|
||||
function, ok := formatterFuncs[currentVerb]
|
||||
if ok {
|
||||
return function, len(currentVerb), ok
|
||||
}
|
||||
currentVerb = currentVerb[:len(currentVerb)-1]
|
||||
}
|
||||
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
func (formatter *formatter) findFormatterFuncParametrized(letters string, lettersStartIndex int) (FormatterFunc, int, bool, error) {
|
||||
currentVerb := letters
|
||||
for i := 0; i < len(letters); i++ {
|
||||
functionCreator, ok := formatterFuncsParameterized[currentVerb]
|
||||
if ok {
|
||||
parameter := ""
|
||||
parameterLen := 0
|
||||
isVerbEqualsLetters := i == 0 // if not, then letter goes after formatter, and formatter is parameterless
|
||||
if isVerbEqualsLetters {
|
||||
userParameter := ""
|
||||
var err error
|
||||
userParameter, parameterLen, ok, err = formatter.findparameter(lettersStartIndex + len(currentVerb))
|
||||
if ok {
|
||||
parameter = userParameter
|
||||
} else if err != nil {
|
||||
return nil, 0, false, err
|
||||
}
|
||||
}
|
||||
|
||||
return functionCreator(parameter), len(currentVerb) + parameterLen, true, nil
|
||||
}
|
||||
|
||||
currentVerb = currentVerb[:len(currentVerb)-1]
|
||||
}
|
||||
|
||||
return nil, 0, false, nil
|
||||
}
|
||||
|
||||
func (formatter *formatter) findparameter(startIndex int) (string, int, bool, error) {
|
||||
if len(formatter.fmtStringOriginal) == startIndex || formatter.fmtStringOriginal[startIndex] != formatterParameterStart {
|
||||
return "", 0, false, nil
|
||||
}
|
||||
|
||||
endIndex := strings.Index(formatter.fmtStringOriginal[startIndex:], string(formatterParameterEnd))
|
||||
if endIndex == -1 {
|
||||
return "", 0, false, fmt.Errorf("Unmatched parenthesis or invalid parameter at %d: %s",
|
||||
startIndex, formatter.fmtStringOriginal[startIndex:])
|
||||
}
|
||||
endIndex += startIndex
|
||||
|
||||
length := endIndex - startIndex + 1
|
||||
|
||||
return formatter.fmtStringOriginal[startIndex+1 : endIndex], length, true, nil
|
||||
}
|
||||
|
||||
// Format processes a message with special formatters, log level, and context. Returns formatted string
|
||||
// with all formatter identifiers changed to appropriate values.
|
||||
func (formatter *formatter) Format(message string, level LogLevel, context LogContextInterface) string {
|
||||
if len(formatter.formatterFuncs) == 0 {
|
||||
return formatter.fmtString
|
||||
}
|
||||
|
||||
params := make([]interface{}, len(formatter.formatterFuncs))
|
||||
for i, function := range formatter.formatterFuncs {
|
||||
params[i] = function(message, level, context)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(formatter.fmtString, params...)
|
||||
}
|
||||
|
||||
func (formatter *formatter) String() string {
|
||||
return formatter.fmtStringOriginal
|
||||
}
|
||||
|
||||
//=====================================================
|
||||
|
||||
const (
|
||||
wrongLogLevel = "WRONG_LOGLEVEL"
|
||||
wrongEscapeCode = "WRONG_ESCAPE"
|
||||
)
|
||||
|
||||
var levelToString = map[LogLevel]string{
|
||||
TraceLvl: "Trace",
|
||||
DebugLvl: "Debug",
|
||||
InfoLvl: "Info",
|
||||
WarnLvl: "Warn",
|
||||
ErrorLvl: "Error",
|
||||
CriticalLvl: "Critical",
|
||||
Off: "Off",
|
||||
}
|
||||
|
||||
var levelToShortString = map[LogLevel]string{
|
||||
TraceLvl: "Trc",
|
||||
DebugLvl: "Dbg",
|
||||
InfoLvl: "Inf",
|
||||
WarnLvl: "Wrn",
|
||||
ErrorLvl: "Err",
|
||||
CriticalLvl: "Crt",
|
||||
Off: "Off",
|
||||
}
|
||||
|
||||
var levelToShortestString = map[LogLevel]string{
|
||||
TraceLvl: "t",
|
||||
DebugLvl: "d",
|
||||
InfoLvl: "i",
|
||||
WarnLvl: "w",
|
||||
ErrorLvl: "e",
|
||||
CriticalLvl: "c",
|
||||
Off: "o",
|
||||
}
|
||||
|
||||
func formatterLevel(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
levelStr, ok := levelToString[level]
|
||||
if !ok {
|
||||
return wrongLogLevel
|
||||
}
|
||||
return levelStr
|
||||
}
|
||||
|
||||
func formatterLev(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
levelStr, ok := levelToShortString[level]
|
||||
if !ok {
|
||||
return wrongLogLevel
|
||||
}
|
||||
return levelStr
|
||||
}
|
||||
|
||||
func formatterLEVEL(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return strings.ToTitle(formatterLevel(message, level, context).(string))
|
||||
}
|
||||
|
||||
func formatterLEV(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return strings.ToTitle(formatterLev(message, level, context).(string))
|
||||
}
|
||||
|
||||
func formatterl(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
levelStr, ok := levelToShortestString[level]
|
||||
if !ok {
|
||||
return wrongLogLevel
|
||||
}
|
||||
return levelStr
|
||||
}
|
||||
|
||||
func formatterMsg(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return message
|
||||
}
|
||||
|
||||
func formatterFullPath(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.FullPath()
|
||||
}
|
||||
|
||||
func formatterFile(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.FileName()
|
||||
}
|
||||
|
||||
func formatterRelFile(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.ShortPath()
|
||||
}
|
||||
|
||||
func FormatterFunction(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.Func()
|
||||
}
|
||||
|
||||
func FormatterFunctionShort(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
f := context.Func()
|
||||
spl := strings.Split(f, ".")
|
||||
return spl[len(spl)-1]
|
||||
}
|
||||
|
||||
func formatterLine(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.Line()
|
||||
}
|
||||
|
||||
func formatterTime(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.CallTime().Format(TimeFormat)
|
||||
}
|
||||
|
||||
func formatterUTCTime(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.CallTime().UTC().Format(TimeFormat)
|
||||
}
|
||||
|
||||
func formatterNs(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.CallTime().UnixNano()
|
||||
}
|
||||
|
||||
func formatterUTCNs(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.CallTime().UTC().UnixNano()
|
||||
}
|
||||
|
||||
func formatterr(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return "\r"
|
||||
}
|
||||
|
||||
func formattern(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return "\n"
|
||||
}
|
||||
|
||||
func formattert(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return "\t"
|
||||
}
|
||||
|
||||
func createDateTimeFormatterFunc(dateTimeFormat string) FormatterFunc {
|
||||
format := dateTimeFormat
|
||||
if format == "" {
|
||||
format = DateDefaultFormat
|
||||
}
|
||||
return func(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.CallTime().Format(format)
|
||||
}
|
||||
}
|
||||
|
||||
func createUTCDateTimeFormatterFunc(dateTimeFormat string) FormatterFunc {
|
||||
format := dateTimeFormat
|
||||
if format == "" {
|
||||
format = DateDefaultFormat
|
||||
}
|
||||
return func(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
return context.CallTime().UTC().Format(format)
|
||||
}
|
||||
}
|
||||
|
||||
func createANSIEscapeFunc(escapeCodeString string) FormatterFunc {
|
||||
return func(message string, level LogLevel, context LogContextInterface) interface{} {
|
||||
if len(escapeCodeString) == 0 {
|
||||
return wrongEscapeCode
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%c[%sm", 0x1B, escapeCodeString)
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package seelog
|
||||
|
||||
// Base struct for custom errors.
|
||||
type baseError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (be baseError) Error() string {
|
||||
return be.message
|
||||
}
|
|
@ -1,320 +0,0 @@
|
|||
package seelog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// File and directory permitions.
|
||||
const (
|
||||
defaultFilePermissions = 0666
|
||||
defaultDirectoryPermissions = 0767
|
||||
)
|
||||
|
||||
const (
|
||||
// Max number of directories can be read asynchronously.
|
||||
maxDirNumberReadAsync = 1000
|
||||
)
|
||||
|
||||
type cannotOpenFileError struct {
|
||||
baseError
|
||||
}
|
||||
|
||||
func newCannotOpenFileError(fname string) *cannotOpenFileError {
|
||||
return &cannotOpenFileError{baseError{message: "Cannot open file: " + fname}}
|
||||
}
|
||||
|
||||
type notDirectoryError struct {
|
||||
baseError
|
||||
}
|
||||
|
||||
func newNotDirectoryError(dname string) *notDirectoryError {
|
||||
return ¬DirectoryError{baseError{message: dname + " is not directory"}}
|
||||
}
|
||||
|
||||
// fileFilter is a filtering criteria function for '*os.File'.
|
||||
// Must return 'false' to set aside the given file.
|
||||
type fileFilter func(os.FileInfo, *os.File) bool
|
||||
|
||||
// filePathFilter is a filtering creteria function for file path.
|
||||
// Must return 'false' to set aside the given file.
|
||||
type filePathFilter func(filePath string) bool
|
||||
|
||||
// GetSubdirNames returns a list of directories found in
|
||||
// the given one with dirPath.
|
||||
func getSubdirNames(dirPath string) ([]string, error) {
|
||||
fi, err := os.Stat(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return nil, newNotDirectoryError(dirPath)
|
||||
}
|
||||
dd, err := os.Open(dirPath)
|
||||
// Cannot open file.
|
||||
if err != nil {
|
||||
if dd != nil {
|
||||
dd.Close()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer dd.Close()
|
||||
// TODO: Improve performance by buffering reading.
|
||||
allEntities, err := dd.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subDirs := []string{}
|
||||
for _, entity := range allEntities {
|
||||
if entity.IsDir() {
|
||||
subDirs = append(subDirs, entity.Name())
|
||||
}
|
||||
}
|
||||
return subDirs, nil
|
||||
}
|
||||
|
||||
// getSubdirAbsPaths recursively visit all the subdirectories
|
||||
// starting from the given directory and returns absolute paths for them.
|
||||
func getAllSubdirAbsPaths(dirPath string) (res []string, err error) {
|
||||
dps, err := getSubdirAbsPaths(dirPath)
|
||||
if err != nil {
|
||||
res = []string{}
|
||||
return
|
||||
}
|
||||
res = append(res, dps...)
|
||||
for _, dp := range dps {
|
||||
sdps, err := getAllSubdirAbsPaths(dp)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
res = append(res, sdps...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getSubdirAbsPaths supplies absolute paths for all subdirectiries in a given directory.
|
||||
// Input: (I1) dirPath - absolute path of a directory in question.
|
||||
// Out: (O1) - slice of subdir asbolute paths; (O2) - error of the operation.
|
||||
// Remark: If error (O2) is non-nil then (O1) is nil and vice versa.
|
||||
func getSubdirAbsPaths(dirPath string) ([]string, error) {
|
||||
sdns, err := getSubdirNames(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsdns := []string{}
|
||||
for _, sdn := range sdns {
|
||||
rsdns = append(rsdns, filepath.Join(dirPath, sdn))
|
||||
}
|
||||
return rsdns, nil
|
||||
}
|
||||
|
||||
// getOpenFilesInDir supplies a slice of os.File pointers to files located in the directory.
|
||||
// Remark: Ignores files for which fileFilter returns false
|
||||
func getOpenFilesInDir(dirPath string, fFilter fileFilter) ([]*os.File, error) {
|
||||
dfi, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
|
||||
}
|
||||
defer dfi.Close()
|
||||
// Size of read buffer (i.e. chunk of items read at a time).
|
||||
rbs := 64
|
||||
resFiles := []*os.File{}
|
||||
L:
|
||||
for {
|
||||
// Read directory entities by reasonable chuncks
|
||||
// to prevent overflows on big number of files.
|
||||
fis, e := dfi.Readdir(rbs)
|
||||
switch e {
|
||||
// It's OK.
|
||||
case nil:
|
||||
// Do nothing, just continue cycle.
|
||||
case io.EOF:
|
||||
break L
|
||||
// Something went wrong.
|
||||
default:
|
||||
return nil, e
|
||||
}
|
||||
// THINK: Maybe, use async running.
|
||||
for _, fi := range fis {
|
||||
// NB: On Linux this could be a problem as
|
||||
// there are lots of file types available.
|
||||
if !fi.IsDir() {
|
||||
f, e := os.Open(filepath.Join(dirPath, fi.Name()))
|
||||
if e != nil {
|
||||
if f != nil {
|
||||
f.Close()
|
||||
}
|
||||
// THINK: Add nil as indicator that a problem occurred.
|
||||
resFiles = append(resFiles, nil)
|
||||
continue
|
||||
}
|
||||
// Check filter condition.
|
||||
if fFilter != nil && !fFilter(fi, f) {
|
||||
continue
|
||||
}
|
||||
resFiles = append(resFiles, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resFiles, nil
|
||||
}
|
||||
|
||||
func isRegular(m os.FileMode) bool {
|
||||
return m&os.ModeType == 0
|
||||
}
|
||||
|
||||
// getDirFilePaths return full paths of the files located in the directory.
|
||||
// Remark: Ignores files for which fileFilter returns false.
|
||||
func getDirFilePaths(dirPath string, fpFilter filePathFilter, pathIsName bool) ([]string, error) {
|
||||
dfi, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
|
||||
}
|
||||
defer dfi.Close()
|
||||
|
||||
var absDirPath string
|
||||
if !filepath.IsAbs(dirPath) {
|
||||
absDirPath, err = filepath.Abs(dirPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get absolute path of directory: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
absDirPath = dirPath
|
||||
}
|
||||
|
||||
// TODO: check if dirPath is really directory.
|
||||
// Size of read buffer (i.e. chunk of items read at a time).
|
||||
rbs := 2 << 5
|
||||
filePaths := []string{}
|
||||
|
||||
var fp string
|
||||
L:
|
||||
for {
|
||||
// Read directory entities by reasonable chuncks
|
||||
// to prevent overflows on big number of files.
|
||||
fis, e := dfi.Readdir(rbs)
|
||||
switch e {
|
||||
// It's OK.
|
||||
case nil:
|
||||
// Do nothing, just continue cycle.
|
||||
case io.EOF:
|
||||
break L
|
||||
// Indicate that something went wrong.
|
||||
default:
|
||||
return nil, e
|
||||
}
|
||||
// THINK: Maybe, use async running.
|
||||
for _, fi := range fis {
|
||||
// NB: Should work on every Windows and non-Windows OS.
|
||||
if isRegular(fi.Mode()) {
|
||||
if pathIsName {
|
||||
fp = fi.Name()
|
||||
} else {
|
||||
// Build full path of a file.
|
||||
fp = filepath.Join(absDirPath, fi.Name())
|
||||
}
|
||||
// Check filter condition.
|
||||
if fpFilter != nil && !fpFilter(fp) {
|
||||
continue
|
||||
}
|
||||
filePaths = append(filePaths, fp)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filePaths, nil
|
||||
}
|
||||
|
||||
// getOpenFilesByDirectoryAsync runs async reading directories 'dirPaths' and inserts pairs
|
||||
// in map 'filesInDirMap': Key - directory name, value - *os.File slice.
|
||||
func getOpenFilesByDirectoryAsync(
|
||||
dirPaths []string,
|
||||
fFilter fileFilter,
|
||||
filesInDirMap map[string][]*os.File,
|
||||
) error {
|
||||
n := len(dirPaths)
|
||||
if n > maxDirNumberReadAsync {
|
||||
return fmt.Errorf("number of input directories to be read exceeded max value %d", maxDirNumberReadAsync)
|
||||
}
|
||||
type filesInDirResult struct {
|
||||
DirName string
|
||||
Files []*os.File
|
||||
Error error
|
||||
}
|
||||
dirFilesChan := make(chan *filesInDirResult, n)
|
||||
var wg sync.WaitGroup
|
||||
// Register n goroutines which are going to do work.
|
||||
wg.Add(n)
|
||||
for i := 0; i < n; i++ {
|
||||
// Launch asynchronously the piece of work.
|
||||
go func(dirPath string) {
|
||||
fs, e := getOpenFilesInDir(dirPath, fFilter)
|
||||
dirFilesChan <- &filesInDirResult{filepath.Base(dirPath), fs, e}
|
||||
// Mark the current goroutine as finished (work is done).
|
||||
wg.Done()
|
||||
}(dirPaths[i])
|
||||
}
|
||||
// Wait for all goroutines to finish their work.
|
||||
wg.Wait()
|
||||
// Close the error channel to let for-range clause
|
||||
// get all the buffered values without blocking and quit in the end.
|
||||
close(dirFilesChan)
|
||||
for fidr := range dirFilesChan {
|
||||
if fidr.Error == nil {
|
||||
// THINK: What will happen if the key is already present?
|
||||
filesInDirMap[fidr.DirName] = fidr.Files
|
||||
} else {
|
||||
return fidr.Error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fileExists return flag whether a given file exists
|
||||
// and operation error if an unclassified failure occurs.
|
||||
func fileExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// createDirectory makes directory with a given name
|
||||
// making all parent directories if necessary.
|
||||
func createDirectory(dirPath string) error {
|
||||
var dPath string
|
||||
var err error
|
||||
if !filepath.IsAbs(dirPath) {
|
||||
dPath, err = filepath.Abs(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
dPath = dirPath
|
||||
}
|
||||
exists, err := fileExists(dPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
return os.MkdirAll(dPath, os.ModeDir)
|
||||
}
|
||||
|
||||
// tryRemoveFile gives a try removing the file
|
||||
// only ignoring an error when the file does not exist.
|
||||
func tryRemoveFile(filePath string) (err error) {
|
||||
err = os.Remove(filePath)
|
||||
if os.IsNotExist(err) {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type xmlNode struct {
|
||||
name string
|
||||
attributes map[string]string
|
||||
children []*xmlNode
|
||||
value string
|
||||
}
|
||||
|
||||
func newNode() *xmlNode {
|
||||
node := new(xmlNode)
|
||||
node.children = make([]*xmlNode, 0)
|
||||
node.attributes = make(map[string]string)
|
||||
return node
|
||||
}
|
||||
|
||||
func (node *xmlNode) String() string {
|
||||
str := fmt.Sprintf("<%s", node.name)
|
||||
|
||||
for attrName, attrVal := range node.attributes {
|
||||
str += fmt.Sprintf(" %s=\"%s\"", attrName, attrVal)
|
||||
}
|
||||
|
||||
str += ">"
|
||||
str += node.value
|
||||
|
||||
if len(node.children) != 0 {
|
||||
for _, child := range node.children {
|
||||
str += fmt.Sprintf("%s", child)
|
||||
}
|
||||
}
|
||||
|
||||
str += fmt.Sprintf("</%s>", node.name)
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (node *xmlNode) unmarshal(startEl xml.StartElement) error {
|
||||
node.name = startEl.Name.Local
|
||||
|
||||
for _, v := range startEl.Attr {
|
||||
_, alreadyExists := node.attributes[v.Name.Local]
|
||||
if alreadyExists {
|
||||
return errors.New("tag '" + node.name + "' has duplicated attribute: '" + v.Name.Local + "'")
|
||||
}
|
||||
node.attributes[v.Name.Local] = v.Value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *xmlNode) add(child *xmlNode) {
|
||||
if node.children == nil {
|
||||
node.children = make([]*xmlNode, 0)
|
||||
}
|
||||
|
||||
node.children = append(node.children, child)
|
||||
}
|
||||
|
||||
func (node *xmlNode) hasChildren() bool {
|
||||
return node.children != nil && len(node.children) > 0
|
||||
}
|
||||
|
||||
//=============================================
|
||||
|
||||
func unmarshalConfig(reader io.Reader) (*xmlNode, error) {
|
||||
xmlParser := xml.NewDecoder(reader)
|
||||
|
||||
config, err := unmarshalNode(xmlParser, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config == nil {
|
||||
return nil, errors.New("xml has no content")
|
||||
}
|
||||
|
||||
nextConfigEntry, err := unmarshalNode(xmlParser, nil)
|
||||
if nextConfigEntry != nil {
|
||||
return nil, errors.New("xml contains more than one root element")
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func unmarshalNode(xmlParser *xml.Decoder, curToken xml.Token) (node *xmlNode, err error) {
|
||||
firstLoop := true
|
||||
for {
|
||||
var tok xml.Token
|
||||
if firstLoop && curToken != nil {
|
||||
tok = curToken
|
||||
firstLoop = false
|
||||
} else {
|
||||
tok, err = getNextToken(xmlParser)
|
||||
if err != nil || tok == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch tt := tok.(type) {
|
||||
case xml.SyntaxError:
|
||||
err = errors.New(tt.Error())
|
||||
return
|
||||
case xml.CharData:
|
||||
value := strings.TrimSpace(string([]byte(tt)))
|
||||
if node != nil {
|
||||
node.value += value
|
||||
}
|
||||
case xml.StartElement:
|
||||
if node == nil {
|
||||
node = newNode()
|
||||
err := node.unmarshal(tt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
childNode, childErr := unmarshalNode(xmlParser, tok)
|
||||
if childErr != nil {
|
||||
return nil, childErr
|
||||
}
|
||||
|
||||
if childNode != nil {
|
||||
node.add(childNode)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
case xml.EndElement:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getNextToken(xmlParser *xml.Decoder) (tok xml.Token, err error) {
|
||||
if tok, err = xmlParser.Token(); err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,307 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
staticFuncCallDepth = 3 // See 'commonLogger.log' method comments
|
||||
loggerFuncCallDepth = 3
|
||||
)
|
||||
|
||||
// Current is the logger used in all package level convenience funcs like 'Trace', 'Debug', 'Flush', etc.
|
||||
var Current LoggerInterface
|
||||
|
||||
// Default logger that is created from an empty config: "<seelog/>". It is not closed by a ReplaceLogger call.
|
||||
var Default LoggerInterface
|
||||
|
||||
// Disabled logger that doesn't produce any output in any circumstances. It is neither closed nor flushed by a ReplaceLogger call.
|
||||
var Disabled LoggerInterface
|
||||
|
||||
var pkgOperationsMutex *sync.Mutex
|
||||
|
||||
func init() {
|
||||
pkgOperationsMutex = new(sync.Mutex)
|
||||
var err error
|
||||
|
||||
if Default == nil {
|
||||
Default, err = LoggerFromConfigAsBytes([]byte("<seelog />"))
|
||||
}
|
||||
|
||||
if Disabled == nil {
|
||||
Disabled, err = LoggerFromConfigAsBytes([]byte("<seelog levels=\"off\"/>"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Seelog couldn't start. Error: %s", err.Error()))
|
||||
}
|
||||
|
||||
Current = Default
|
||||
}
|
||||
|
||||
func createLoggerFromFullConfig(config *configForParsing) (LoggerInterface, error) {
|
||||
if config.LogType == syncloggerTypeFromString {
|
||||
return NewSyncLogger(&config.logConfig), nil
|
||||
} else if config.LogType == asyncLooploggerTypeFromString {
|
||||
return NewAsyncLoopLogger(&config.logConfig), nil
|
||||
} else if config.LogType == asyncTimerloggerTypeFromString {
|
||||
logData := config.LoggerData
|
||||
if logData == nil {
|
||||
return nil, errors.New("async timer data not set")
|
||||
}
|
||||
|
||||
asyncInt, ok := logData.(asyncTimerLoggerData)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid async timer data")
|
||||
}
|
||||
|
||||
logger, err := NewAsyncTimerLogger(&config.logConfig, time.Duration(asyncInt.AsyncInterval))
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
} else if config.LogType == adaptiveLoggerTypeFromString {
|
||||
logData := config.LoggerData
|
||||
if logData == nil {
|
||||
return nil, errors.New("adaptive logger parameters not set")
|
||||
}
|
||||
|
||||
adaptData, ok := logData.(adaptiveLoggerData)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid adaptive logger parameters")
|
||||
}
|
||||
|
||||
logger, err := NewAsyncAdaptiveLogger(
|
||||
&config.logConfig,
|
||||
time.Duration(adaptData.MinInterval),
|
||||
time.Duration(adaptData.MaxInterval),
|
||||
adaptData.CriticalMsgCount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
return nil, errors.New("invalid config log type/data")
|
||||
}
|
||||
|
||||
// UseLogger sets the 'Current' package level logger variable to the specified value.
|
||||
// This variable is used in all Trace/Debug/... package level convenience funcs.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// after calling
|
||||
// seelog.UseLogger(somelogger)
|
||||
// the following:
|
||||
// seelog.Debug("abc")
|
||||
// will be equal to
|
||||
// somelogger.Debug("abc")
|
||||
//
|
||||
// IMPORTANT: UseLogger do NOT close the previous logger (only flushes it). So if
|
||||
// you constantly use it to replace loggers and don't close them in other code, you'll
|
||||
// end up having memory leaks.
|
||||
//
|
||||
// To safely replace loggers, use ReplaceLogger.
|
||||
func UseLogger(logger LoggerInterface) error {
|
||||
if logger == nil {
|
||||
return errors.New("logger can not be nil")
|
||||
}
|
||||
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
|
||||
oldLogger := Current
|
||||
Current = logger
|
||||
|
||||
if oldLogger != nil {
|
||||
oldLogger.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReplaceLogger acts as UseLogger but the logger that was previously
|
||||
// used is disposed (except Default and Disabled loggers).
|
||||
//
|
||||
// Example:
|
||||
// import log "github.com/cihub/seelog"
|
||||
//
|
||||
// func main() {
|
||||
// logger, err := log.LoggerFromConfigAsFile("seelog.xml")
|
||||
//
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// log.ReplaceLogger(logger)
|
||||
// defer log.Flush()
|
||||
//
|
||||
// log.Trace("test")
|
||||
// log.Debugf("var = %s", "abc")
|
||||
// }
|
||||
func ReplaceLogger(logger LoggerInterface) error {
|
||||
if logger == nil {
|
||||
return errors.New("logger can not be nil")
|
||||
}
|
||||
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
reportInternalError(fmt.Errorf("recovered from panic during ReplaceLogger: %s", err))
|
||||
}
|
||||
}()
|
||||
|
||||
if Current == Default {
|
||||
Current.Flush()
|
||||
} else if Current != nil && !Current.Closed() && Current != Disabled {
|
||||
Current.Flush()
|
||||
Current.Close()
|
||||
}
|
||||
|
||||
Current = logger
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tracef formats message according to format specifier
|
||||
// and writes to default logger with log level = Trace.
|
||||
func Tracef(format string, params ...interface{}) {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
Current.traceWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
|
||||
}
|
||||
|
||||
// Debugf formats message according to format specifier
|
||||
// and writes to default logger with log level = Debug.
|
||||
func Debugf(format string, params ...interface{}) {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
Current.debugWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
|
||||
}
|
||||
|
||||
// Infof formats message according to format specifier
|
||||
// and writes to default logger with log level = Info.
|
||||
func Infof(format string, params ...interface{}) {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
Current.infoWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
|
||||
}
|
||||
|
||||
// Warnf formats message according to format specifier and writes to default logger with log level = Warn
|
||||
func Warnf(format string, params ...interface{}) error {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
message := newLogFormattedMessage(format, params)
|
||||
Current.warnWithCallDepth(staticFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
// Errorf formats message according to format specifier and writes to default logger with log level = Error
|
||||
func Errorf(format string, params ...interface{}) error {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
message := newLogFormattedMessage(format, params)
|
||||
Current.errorWithCallDepth(staticFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
// Criticalf formats message according to format specifier and writes to default logger with log level = Critical
|
||||
func Criticalf(format string, params ...interface{}) error {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
message := newLogFormattedMessage(format, params)
|
||||
Current.criticalWithCallDepth(staticFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
// Trace formats message using the default formats for its operands and writes to default logger with log level = Trace
|
||||
func Trace(v ...interface{}) {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
Current.traceWithCallDepth(staticFuncCallDepth, newLogMessage(v))
|
||||
}
|
||||
|
||||
// Debug formats message using the default formats for its operands and writes to default logger with log level = Debug
|
||||
func Debug(v ...interface{}) {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
Current.debugWithCallDepth(staticFuncCallDepth, newLogMessage(v))
|
||||
}
|
||||
|
||||
// Info formats message using the default formats for its operands and writes to default logger with log level = Info
|
||||
func Info(v ...interface{}) {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
Current.infoWithCallDepth(staticFuncCallDepth, newLogMessage(v))
|
||||
}
|
||||
|
||||
// Warn formats message using the default formats for its operands and writes to default logger with log level = Warn
|
||||
func Warn(v ...interface{}) error {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
message := newLogMessage(v)
|
||||
Current.warnWithCallDepth(staticFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
// Error formats message using the default formats for its operands and writes to default logger with log level = Error
|
||||
func Error(v ...interface{}) error {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
message := newLogMessage(v)
|
||||
Current.errorWithCallDepth(staticFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
// Critical formats message using the default formats for its operands and writes to default logger with log level = Critical
|
||||
func Critical(v ...interface{}) error {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
message := newLogMessage(v)
|
||||
Current.criticalWithCallDepth(staticFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
// Flush immediately processes all currently queued messages and all currently buffered messages.
|
||||
// It is a blocking call which returns only after the queue is empty and all the buffers are empty.
|
||||
//
|
||||
// If Flush is called for a synchronous logger (type='sync'), it only flushes buffers (e.g. '<buffered>' receivers)
|
||||
// , because there is no queue.
|
||||
//
|
||||
// Call this method when your app is going to shut down not to lose any log messages.
|
||||
func Flush() {
|
||||
pkgOperationsMutex.Lock()
|
||||
defer pkgOperationsMutex.Unlock()
|
||||
Current.Flush()
|
||||
}
|
|
@ -1,370 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func reportInternalError(err error) {
|
||||
fmt.Fprintf(os.Stderr, "seelog internal error: %s\n", err)
|
||||
}
|
||||
|
||||
// LoggerInterface represents structs capable of logging Seelog messages
|
||||
type LoggerInterface interface {
|
||||
|
||||
// Tracef formats message according to format specifier
|
||||
// and writes to log with level = Trace.
|
||||
Tracef(format string, params ...interface{})
|
||||
|
||||
// Debugf formats message according to format specifier
|
||||
// and writes to log with level = Debug.
|
||||
Debugf(format string, params ...interface{})
|
||||
|
||||
// Infof formats message according to format specifier
|
||||
// and writes to log with level = Info.
|
||||
Infof(format string, params ...interface{})
|
||||
|
||||
// Warnf formats message according to format specifier
|
||||
// and writes to log with level = Warn.
|
||||
Warnf(format string, params ...interface{}) error
|
||||
|
||||
// Errorf formats message according to format specifier
|
||||
// and writes to log with level = Error.
|
||||
Errorf(format string, params ...interface{}) error
|
||||
|
||||
// Criticalf formats message according to format specifier
|
||||
// and writes to log with level = Critical.
|
||||
Criticalf(format string, params ...interface{}) error
|
||||
|
||||
// Trace formats message using the default formats for its operands
|
||||
// and writes to log with level = Trace
|
||||
Trace(v ...interface{})
|
||||
|
||||
// Debug formats message using the default formats for its operands
|
||||
// and writes to log with level = Debug
|
||||
Debug(v ...interface{})
|
||||
|
||||
// Info formats message using the default formats for its operands
|
||||
// and writes to log with level = Info
|
||||
Info(v ...interface{})
|
||||
|
||||
// Warn formats message using the default formats for its operands
|
||||
// and writes to log with level = Warn
|
||||
Warn(v ...interface{}) error
|
||||
|
||||
// Error formats message using the default formats for its operands
|
||||
// and writes to log with level = Error
|
||||
Error(v ...interface{}) error
|
||||
|
||||
// Critical formats message using the default formats for its operands
|
||||
// and writes to log with level = Critical
|
||||
Critical(v ...interface{}) error
|
||||
|
||||
traceWithCallDepth(callDepth int, message fmt.Stringer)
|
||||
debugWithCallDepth(callDepth int, message fmt.Stringer)
|
||||
infoWithCallDepth(callDepth int, message fmt.Stringer)
|
||||
warnWithCallDepth(callDepth int, message fmt.Stringer)
|
||||
errorWithCallDepth(callDepth int, message fmt.Stringer)
|
||||
criticalWithCallDepth(callDepth int, message fmt.Stringer)
|
||||
|
||||
// Close flushes all the messages in the logger and closes it. It cannot be used after this operation.
|
||||
Close()
|
||||
|
||||
// Flush flushes all the messages in the logger.
|
||||
Flush()
|
||||
|
||||
// Closed returns true if the logger was previously closed.
|
||||
Closed() bool
|
||||
|
||||
// SetAdditionalStackDepth sets the additional number of frames to skip by runtime.Caller
|
||||
// when getting function information needed to print seelog format identifiers such as %Func or %File.
|
||||
//
|
||||
// This func may be used when you wrap seelog funcs and want to print caller info of you own
|
||||
// wrappers instead of seelog func callers. In this case you should set depth = 1. If you then
|
||||
// wrap your wrapper, you should set depth = 2, etc.
|
||||
//
|
||||
// NOTE: Incorrect depth value may lead to errors in runtime.Caller evaluation or incorrect
|
||||
// function/file names in log files. Do not use it if you are not going to wrap seelog funcs.
|
||||
// You may reset the value to default using a SetAdditionalStackDepth(0) call.
|
||||
SetAdditionalStackDepth(depth int) error
|
||||
|
||||
// Sets logger context that can be used in formatter funcs and custom receivers
|
||||
SetContext(context interface{})
|
||||
}
|
||||
|
||||
// innerLoggerInterface is an internal logging interface
|
||||
type innerLoggerInterface interface {
|
||||
innerLog(level LogLevel, context LogContextInterface, message fmt.Stringer)
|
||||
Flush()
|
||||
}
|
||||
|
||||
// [file path][func name][level] -> [allowed]
|
||||
type allowedContextCache map[string]map[string]map[LogLevel]bool
|
||||
|
||||
// commonLogger contains all common data needed for logging and contains methods used to log messages.
|
||||
type commonLogger struct {
|
||||
config *logConfig // Config used for logging
|
||||
contextCache allowedContextCache // Caches whether log is enabled for specific "full path-func name-level" sets
|
||||
closed bool // 'true' when all writers are closed, all data is flushed, logger is unusable. Must be accessed while holding closedM
|
||||
closedM sync.RWMutex
|
||||
m sync.Mutex // Mutex for main operations
|
||||
unusedLevels []bool
|
||||
innerLogger innerLoggerInterface
|
||||
addStackDepth int // Additional stack depth needed for correct seelog caller context detection
|
||||
customContext interface{}
|
||||
}
|
||||
|
||||
func newCommonLogger(config *logConfig, internalLogger innerLoggerInterface) *commonLogger {
|
||||
cLogger := new(commonLogger)
|
||||
|
||||
cLogger.config = config
|
||||
cLogger.contextCache = make(allowedContextCache)
|
||||
cLogger.unusedLevels = make([]bool, Off)
|
||||
cLogger.fillUnusedLevels()
|
||||
cLogger.innerLogger = internalLogger
|
||||
|
||||
return cLogger
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) SetAdditionalStackDepth(depth int) error {
|
||||
if depth < 0 {
|
||||
return fmt.Errorf("negative depth: %d", depth)
|
||||
}
|
||||
cLogger.m.Lock()
|
||||
cLogger.addStackDepth = depth
|
||||
cLogger.m.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Tracef(format string, params ...interface{}) {
|
||||
cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Debugf(format string, params ...interface{}) {
|
||||
cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Infof(format string, params ...interface{}) {
|
||||
cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Warnf(format string, params ...interface{}) error {
|
||||
message := newLogFormattedMessage(format, params)
|
||||
cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Errorf(format string, params ...interface{}) error {
|
||||
message := newLogFormattedMessage(format, params)
|
||||
cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Criticalf(format string, params ...interface{}) error {
|
||||
message := newLogFormattedMessage(format, params)
|
||||
cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Trace(v ...interface{}) {
|
||||
cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Debug(v ...interface{}) {
|
||||
cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Info(v ...interface{}) {
|
||||
cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Warn(v ...interface{}) error {
|
||||
message := newLogMessage(v)
|
||||
cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Error(v ...interface{}) error {
|
||||
message := newLogMessage(v)
|
||||
cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Critical(v ...interface{}) error {
|
||||
message := newLogMessage(v)
|
||||
cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
|
||||
return errors.New(message.String())
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) SetContext(c interface{}) {
|
||||
cLogger.customContext = c
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) traceWithCallDepth(callDepth int, message fmt.Stringer) {
|
||||
cLogger.log(TraceLvl, message, callDepth)
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) debugWithCallDepth(callDepth int, message fmt.Stringer) {
|
||||
cLogger.log(DebugLvl, message, callDepth)
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) infoWithCallDepth(callDepth int, message fmt.Stringer) {
|
||||
cLogger.log(InfoLvl, message, callDepth)
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) warnWithCallDepth(callDepth int, message fmt.Stringer) {
|
||||
cLogger.log(WarnLvl, message, callDepth)
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) errorWithCallDepth(callDepth int, message fmt.Stringer) {
|
||||
cLogger.log(ErrorLvl, message, callDepth)
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) criticalWithCallDepth(callDepth int, message fmt.Stringer) {
|
||||
cLogger.log(CriticalLvl, message, callDepth)
|
||||
cLogger.innerLogger.Flush()
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) Closed() bool {
|
||||
cLogger.closedM.RLock()
|
||||
defer cLogger.closedM.RUnlock()
|
||||
return cLogger.closed
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) fillUnusedLevels() {
|
||||
for i := 0; i < len(cLogger.unusedLevels); i++ {
|
||||
cLogger.unusedLevels[i] = true
|
||||
}
|
||||
|
||||
cLogger.fillUnusedLevelsByContraint(cLogger.config.Constraints)
|
||||
|
||||
for _, exception := range cLogger.config.Exceptions {
|
||||
cLogger.fillUnusedLevelsByContraint(exception)
|
||||
}
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) fillUnusedLevelsByContraint(constraint logLevelConstraints) {
|
||||
for i := 0; i < len(cLogger.unusedLevels); i++ {
|
||||
if constraint.IsAllowed(LogLevel(i)) {
|
||||
cLogger.unusedLevels[i] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stackCallDepth is used to indicate the call depth of 'log' func.
|
||||
// This depth level is used in the runtime.Caller(...) call. See
|
||||
// common_context.go -> specifyContext, extractCallerInfo for details.
|
||||
func (cLogger *commonLogger) log(level LogLevel, message fmt.Stringer, stackCallDepth int) {
|
||||
if cLogger.unusedLevels[level] {
|
||||
return
|
||||
}
|
||||
cLogger.m.Lock()
|
||||
defer cLogger.m.Unlock()
|
||||
|
||||
if cLogger.Closed() {
|
||||
return
|
||||
}
|
||||
context, _ := specifyContext(stackCallDepth+cLogger.addStackDepth, cLogger.customContext)
|
||||
// Context errors are not reported because there are situations
|
||||
// in which context errors are normal Seelog usage cases. For
|
||||
// example in executables with stripped symbols.
|
||||
// Error contexts are returned instead. See common_context.go.
|
||||
/*if err != nil {
|
||||
reportInternalError(err)
|
||||
return
|
||||
}*/
|
||||
cLogger.innerLogger.innerLog(level, context, message)
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) processLogMsg(level LogLevel, message fmt.Stringer, context LogContextInterface) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
reportInternalError(fmt.Errorf("recovered from panic during message processing: %s", err))
|
||||
}
|
||||
}()
|
||||
if cLogger.config.IsAllowed(level, context) {
|
||||
cLogger.config.RootDispatcher.Dispatch(message.String(), level, context, reportInternalError)
|
||||
}
|
||||
}
|
||||
|
||||
func (cLogger *commonLogger) isAllowed(level LogLevel, context LogContextInterface) bool {
|
||||
funcMap, ok := cLogger.contextCache[context.FullPath()]
|
||||
if !ok {
|
||||
funcMap = make(map[string]map[LogLevel]bool, 0)
|
||||
cLogger.contextCache[context.FullPath()] = funcMap
|
||||
}
|
||||
|
||||
levelMap, ok := funcMap[context.Func()]
|
||||
if !ok {
|
||||
levelMap = make(map[LogLevel]bool, 0)
|
||||
funcMap[context.Func()] = levelMap
|
||||
}
|
||||
|
||||
isAllowValue, ok := levelMap[level]
|
||||
if !ok {
|
||||
isAllowValue = cLogger.config.IsAllowed(level, context)
|
||||
levelMap[level] = isAllowValue
|
||||
}
|
||||
|
||||
return isAllowValue
|
||||
}
|
||||
|
||||
type logMessage struct {
|
||||
params []interface{}
|
||||
}
|
||||
|
||||
type logFormattedMessage struct {
|
||||
format string
|
||||
params []interface{}
|
||||
}
|
||||
|
||||
func newLogMessage(params []interface{}) fmt.Stringer {
|
||||
message := new(logMessage)
|
||||
|
||||
message.params = params
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func newLogFormattedMessage(format string, params []interface{}) *logFormattedMessage {
|
||||
message := new(logFormattedMessage)
|
||||
|
||||
message.params = params
|
||||
message.format = format
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func (message *logMessage) String() string {
|
||||
return fmt.Sprint(message.params...)
|
||||
}
|
||||
|
||||
func (message *logFormattedMessage) String() string {
|
||||
return fmt.Sprintf(message.format, message.params...)
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// bufferedWriter stores data in memory and flushes it every flushPeriod or when buffer is full
|
||||
type bufferedWriter struct {
|
||||
flushPeriod time.Duration // data flushes interval (in microseconds)
|
||||
bufferMutex *sync.Mutex // mutex for buffer operations syncronization
|
||||
innerWriter io.Writer // inner writer
|
||||
buffer *bufio.Writer // buffered wrapper for inner writer
|
||||
bufferSize int // max size of data chunk in bytes
|
||||
}
|
||||
|
||||
// NewBufferedWriter creates a new buffered writer struct.
|
||||
// bufferSize -- size of memory buffer in bytes
|
||||
// flushPeriod -- period in which data flushes from memory buffer in milliseconds. 0 - turn off this functionality
|
||||
func NewBufferedWriter(innerWriter io.Writer, bufferSize int, flushPeriod time.Duration) (*bufferedWriter, error) {
|
||||
|
||||
if innerWriter == nil {
|
||||
return nil, errors.New("argument is nil: innerWriter")
|
||||
}
|
||||
if flushPeriod < 0 {
|
||||
return nil, fmt.Errorf("flushPeriod can not be less than 0. Got: %d", flushPeriod)
|
||||
}
|
||||
|
||||
if bufferSize <= 0 {
|
||||
return nil, fmt.Errorf("bufferSize can not be less or equal to 0. Got: %d", bufferSize)
|
||||
}
|
||||
|
||||
buffer := bufio.NewWriterSize(innerWriter, bufferSize)
|
||||
|
||||
/*if err != nil {
|
||||
return nil, err
|
||||
}*/
|
||||
|
||||
newWriter := new(bufferedWriter)
|
||||
|
||||
newWriter.innerWriter = innerWriter
|
||||
newWriter.buffer = buffer
|
||||
newWriter.bufferSize = bufferSize
|
||||
newWriter.flushPeriod = flushPeriod * 1e6
|
||||
newWriter.bufferMutex = new(sync.Mutex)
|
||||
|
||||
if flushPeriod != 0 {
|
||||
go newWriter.flushPeriodically()
|
||||
}
|
||||
|
||||
return newWriter, nil
|
||||
}
|
||||
|
||||
func (bufWriter *bufferedWriter) writeBigChunk(bytes []byte) (n int, err error) {
|
||||
bufferedLen := bufWriter.buffer.Buffered()
|
||||
|
||||
n, err = bufWriter.flushInner()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
written, writeErr := bufWriter.innerWriter.Write(bytes)
|
||||
return bufferedLen + written, writeErr
|
||||
}
|
||||
|
||||
// Sends data to buffer manager. Waits until all buffers are full.
|
||||
func (bufWriter *bufferedWriter) Write(bytes []byte) (n int, err error) {
|
||||
|
||||
bufWriter.bufferMutex.Lock()
|
||||
defer bufWriter.bufferMutex.Unlock()
|
||||
|
||||
bytesLen := len(bytes)
|
||||
|
||||
if bytesLen > bufWriter.bufferSize {
|
||||
return bufWriter.writeBigChunk(bytes)
|
||||
}
|
||||
|
||||
if bytesLen > bufWriter.buffer.Available() {
|
||||
n, err = bufWriter.flushInner()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
bufWriter.buffer.Write(bytes)
|
||||
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
func (bufWriter *bufferedWriter) Close() error {
|
||||
closer, ok := bufWriter.innerWriter.(io.Closer)
|
||||
if ok {
|
||||
return closer.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bufWriter *bufferedWriter) Flush() {
|
||||
|
||||
bufWriter.bufferMutex.Lock()
|
||||
defer bufWriter.bufferMutex.Unlock()
|
||||
|
||||
bufWriter.flushInner()
|
||||
}
|
||||
|
||||
func (bufWriter *bufferedWriter) flushInner() (n int, err error) {
|
||||
bufferedLen := bufWriter.buffer.Buffered()
|
||||
flushErr := bufWriter.buffer.Flush()
|
||||
|
||||
return bufWriter.buffer.Buffered() - bufferedLen, flushErr
|
||||
}
|
||||
|
||||
func (bufWriter *bufferedWriter) flushBuffer() {
|
||||
bufWriter.bufferMutex.Lock()
|
||||
defer bufWriter.bufferMutex.Unlock()
|
||||
|
||||
bufWriter.buffer.Flush()
|
||||
}
|
||||
|
||||
func (bufWriter *bufferedWriter) flushPeriodically() {
|
||||
if bufWriter.flushPeriod > 0 {
|
||||
ticker := time.NewTicker(bufWriter.flushPeriod)
|
||||
for {
|
||||
<-ticker.C
|
||||
bufWriter.flushBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bufWriter *bufferedWriter) String() string {
|
||||
return fmt.Sprintf("bufferedWriter size: %d, flushPeriod: %d", bufWriter.bufferSize, bufWriter.flushPeriod)
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
// connWriter is used to write to a stream-oriented network connection.
|
||||
type connWriter struct {
|
||||
innerWriter io.WriteCloser
|
||||
reconnectOnMsg bool
|
||||
reconnect bool
|
||||
net string
|
||||
addr string
|
||||
useTLS bool
|
||||
configTLS *tls.Config
|
||||
}
|
||||
|
||||
// Creates writer to the address addr on the network netName.
|
||||
// Connection will be opened on each write if reconnectOnMsg = true
|
||||
func NewConnWriter(netName string, addr string, reconnectOnMsg bool) *connWriter {
|
||||
newWriter := new(connWriter)
|
||||
|
||||
newWriter.net = netName
|
||||
newWriter.addr = addr
|
||||
newWriter.reconnectOnMsg = reconnectOnMsg
|
||||
|
||||
return newWriter
|
||||
}
|
||||
|
||||
// Creates a writer that uses SSL/TLS
|
||||
func newTLSWriter(netName string, addr string, reconnectOnMsg bool, config *tls.Config) *connWriter {
|
||||
newWriter := new(connWriter)
|
||||
|
||||
newWriter.net = netName
|
||||
newWriter.addr = addr
|
||||
newWriter.reconnectOnMsg = reconnectOnMsg
|
||||
newWriter.useTLS = true
|
||||
newWriter.configTLS = config
|
||||
|
||||
return newWriter
|
||||
}
|
||||
|
||||
func (connWriter *connWriter) Close() error {
|
||||
if connWriter.innerWriter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return connWriter.innerWriter.Close()
|
||||
}
|
||||
|
||||
func (connWriter *connWriter) Write(bytes []byte) (n int, err error) {
|
||||
if connWriter.neededConnectOnMsg() {
|
||||
err = connWriter.connect()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if connWriter.reconnectOnMsg {
|
||||
defer connWriter.innerWriter.Close()
|
||||
}
|
||||
|
||||
n, err = connWriter.innerWriter.Write(bytes)
|
||||
if err != nil {
|
||||
connWriter.reconnect = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (connWriter *connWriter) String() string {
|
||||
return fmt.Sprintf("Conn writer: [%s, %s, %v]", connWriter.net, connWriter.addr, connWriter.reconnectOnMsg)
|
||||
}
|
||||
|
||||
func (connWriter *connWriter) connect() error {
|
||||
if connWriter.innerWriter != nil {
|
||||
connWriter.innerWriter.Close()
|
||||
connWriter.innerWriter = nil
|
||||
}
|
||||
|
||||
if connWriter.useTLS {
|
||||
conn, err := tls.Dial(connWriter.net, connWriter.addr, connWriter.configTLS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
connWriter.innerWriter = conn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
conn, err := net.Dial(connWriter.net, connWriter.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tcpConn, ok := conn.(*net.TCPConn)
|
||||
if ok {
|
||||
tcpConn.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
connWriter.innerWriter = conn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (connWriter *connWriter) neededConnectOnMsg() bool {
|
||||
if connWriter.reconnect {
|
||||
connWriter.reconnect = false
|
||||
return true
|
||||
}
|
||||
|
||||
if connWriter.innerWriter == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return connWriter.reconnectOnMsg
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import "fmt"
|
||||
|
||||
// consoleWriter is used to write to console
|
||||
type consoleWriter struct {
|
||||
}
|
||||
|
||||
// Creates a new console writer. Returns error, if the console writer couldn't be created.
|
||||
func NewConsoleWriter() (writer *consoleWriter, err error) {
|
||||
newWriter := new(consoleWriter)
|
||||
|
||||
return newWriter, nil
|
||||
}
|
||||
|
||||
// Create folder and file on WriteLog/Write first call
|
||||
func (console *consoleWriter) Write(bytes []byte) (int, error) {
|
||||
return fmt.Print(string(bytes))
|
||||
}
|
||||
|
||||
func (console *consoleWriter) String() string {
|
||||
return "Console writer"
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// fileWriter is used to write to a file.
|
||||
type fileWriter struct {
|
||||
innerWriter io.WriteCloser
|
||||
fileName string
|
||||
}
|
||||
|
||||
// Creates a new file and a corresponding writer. Returns error, if the file couldn't be created.
|
||||
func NewFileWriter(fileName string) (writer *fileWriter, err error) {
|
||||
newWriter := new(fileWriter)
|
||||
newWriter.fileName = fileName
|
||||
|
||||
return newWriter, nil
|
||||
}
|
||||
|
||||
func (fw *fileWriter) Close() error {
|
||||
if fw.innerWriter != nil {
|
||||
err := fw.innerWriter.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fw.innerWriter = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create folder and file on WriteLog/Write first call
|
||||
func (fw *fileWriter) Write(bytes []byte) (n int, err error) {
|
||||
if fw.innerWriter == nil {
|
||||
if err := fw.createFile(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return fw.innerWriter.Write(bytes)
|
||||
}
|
||||
|
||||
func (fw *fileWriter) createFile() error {
|
||||
folder, _ := filepath.Split(fw.fileName)
|
||||
var err error
|
||||
|
||||
if 0 != len(folder) {
|
||||
err = os.MkdirAll(folder, defaultDirectoryPermissions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If exists
|
||||
fw.innerWriter, err = os.OpenFile(fw.fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, defaultFilePermissions)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fw *fileWriter) String() string {
|
||||
return fmt.Sprintf("File writer: %s", fw.fileName)
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type formattedWriter struct {
|
||||
writer io.Writer
|
||||
formatter *formatter
|
||||
}
|
||||
|
||||
func NewFormattedWriter(writer io.Writer, formatter *formatter) (*formattedWriter, error) {
|
||||
if formatter == nil {
|
||||
return nil, errors.New("formatter can not be nil")
|
||||
}
|
||||
|
||||
return &formattedWriter{writer, formatter}, nil
|
||||
}
|
||||
|
||||
func (formattedWriter *formattedWriter) Write(message string, level LogLevel, context LogContextInterface) error {
|
||||
str := formattedWriter.formatter.Format(message, level, context)
|
||||
_, err := formattedWriter.writer.Write([]byte(str))
|
||||
return err
|
||||
}
|
||||
|
||||
func (formattedWriter *formattedWriter) String() string {
|
||||
return fmt.Sprintf("writer: %s, format: %s", formattedWriter.writer, formattedWriter.formatter)
|
||||
}
|
||||
|
||||
func (formattedWriter *formattedWriter) Writer() io.Writer {
|
||||
return formattedWriter.writer
|
||||
}
|
||||
|
||||
func (formattedWriter *formattedWriter) Format() *formatter {
|
||||
return formattedWriter.formatter
|
||||
}
|
|
@ -1,763 +0,0 @@
|
|||
// Copyright (c) 2013 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cihub/seelog/archive"
|
||||
"github.com/cihub/seelog/archive/gzip"
|
||||
"github.com/cihub/seelog/archive/tar"
|
||||
"github.com/cihub/seelog/archive/zip"
|
||||
)
|
||||
|
||||
// Common constants
|
||||
const (
|
||||
rollingLogHistoryDelimiter = "."
|
||||
)
|
||||
|
||||
// Types of the rolling writer: roll by date, by time, etc.
|
||||
type rollingType uint8
|
||||
|
||||
const (
|
||||
rollingTypeSize = iota
|
||||
rollingTypeTime
|
||||
)
|
||||
|
||||
// Types of the rolled file naming mode: prefix, postfix, etc.
|
||||
type rollingNameMode uint8
|
||||
|
||||
const (
|
||||
rollingNameModePostfix = iota
|
||||
rollingNameModePrefix
|
||||
)
|
||||
|
||||
var rollingNameModesStringRepresentation = map[rollingNameMode]string{
|
||||
rollingNameModePostfix: "postfix",
|
||||
rollingNameModePrefix: "prefix",
|
||||
}
|
||||
|
||||
func rollingNameModeFromString(rollingNameStr string) (rollingNameMode, bool) {
|
||||
for tp, tpStr := range rollingNameModesStringRepresentation {
|
||||
if tpStr == rollingNameStr {
|
||||
return tp, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
var rollingTypesStringRepresentation = map[rollingType]string{
|
||||
rollingTypeSize: "size",
|
||||
rollingTypeTime: "date",
|
||||
}
|
||||
|
||||
func rollingTypeFromString(rollingTypeStr string) (rollingType, bool) {
|
||||
for tp, tpStr := range rollingTypesStringRepresentation {
|
||||
if tpStr == rollingTypeStr {
|
||||
return tp, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Old logs archivation type.
|
||||
type rollingArchiveType uint8
|
||||
|
||||
const (
|
||||
rollingArchiveNone = iota
|
||||
rollingArchiveZip
|
||||
rollingArchiveGzip
|
||||
)
|
||||
|
||||
var rollingArchiveTypesStringRepresentation = map[rollingArchiveType]string{
|
||||
rollingArchiveNone: "none",
|
||||
rollingArchiveZip: "zip",
|
||||
rollingArchiveGzip: "gzip",
|
||||
}
|
||||
|
||||
type archiver func(f *os.File, exploded bool) archive.WriteCloser
|
||||
|
||||
type unarchiver func(f *os.File) (archive.ReadCloser, error)
|
||||
|
||||
type compressionType struct {
|
||||
extension string
|
||||
handleMultipleEntries bool
|
||||
archiver archiver
|
||||
unarchiver unarchiver
|
||||
}
|
||||
|
||||
var compressionTypes = map[rollingArchiveType]compressionType{
|
||||
rollingArchiveZip: {
|
||||
extension: ".zip",
|
||||
handleMultipleEntries: true,
|
||||
archiver: func(f *os.File, _ bool) archive.WriteCloser {
|
||||
return zip.NewWriter(f)
|
||||
},
|
||||
unarchiver: func(f *os.File) (archive.ReadCloser, error) {
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := zip.NewReader(f, fi.Size())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return archive.NopCloser(r), nil
|
||||
},
|
||||
},
|
||||
rollingArchiveGzip: {
|
||||
extension: ".gz",
|
||||
handleMultipleEntries: false,
|
||||
archiver: func(f *os.File, exploded bool) archive.WriteCloser {
|
||||
gw := gzip.NewWriter(f)
|
||||
if exploded {
|
||||
return gw
|
||||
}
|
||||
return tar.NewWriteMultiCloser(gw, gw)
|
||||
},
|
||||
unarchiver: func(f *os.File) (archive.ReadCloser, error) {
|
||||
gr, err := gzip.NewReader(f, f.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine if the gzip is a tar
|
||||
tr := tar.NewReader(gr)
|
||||
_, err = tr.Next()
|
||||
isTar := err == nil
|
||||
|
||||
// Reset to beginning of file
|
||||
if _, err := f.Seek(0, os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gr.Reset(f)
|
||||
|
||||
if isTar {
|
||||
return archive.NopCloser(tar.NewReader(gr)), nil
|
||||
}
|
||||
return gr, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (compressionType *compressionType) rollingArchiveTypeName(name string, exploded bool) string {
|
||||
if !compressionType.handleMultipleEntries && !exploded {
|
||||
return name + ".tar" + compressionType.extension
|
||||
} else {
|
||||
return name + compressionType.extension
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func rollingArchiveTypeFromString(rollingArchiveTypeStr string) (rollingArchiveType, bool) {
|
||||
for tp, tpStr := range rollingArchiveTypesStringRepresentation {
|
||||
if tpStr == rollingArchiveTypeStr {
|
||||
return tp, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Default names for different archive types
|
||||
var rollingArchiveDefaultExplodedName = "old"
|
||||
|
||||
func rollingArchiveTypeDefaultName(archiveType rollingArchiveType, exploded bool) (string, error) {
|
||||
compressionType, ok := compressionTypes[archiveType]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("cannot get default filename for archive type = %v", archiveType)
|
||||
}
|
||||
return compressionType.rollingArchiveTypeName("log", exploded), nil
|
||||
}
|
||||
|
||||
// rollerVirtual is an interface that represents all virtual funcs that are
|
||||
// called in different rolling writer subtypes.
|
||||
type rollerVirtual interface {
|
||||
needsToRoll() bool // Returns true if needs to switch to another file.
|
||||
isFileRollNameValid(rname string) bool // Returns true if logger roll file name (postfix/prefix/etc.) is ok.
|
||||
sortFileRollNamesAsc(fs []string) ([]string, error) // Sorts logger roll file names in ascending order of their creation by logger.
|
||||
|
||||
// getNewHistoryRollFileName is called whenever we are about to roll the
|
||||
// current log file. It returns the name the current log file should be
|
||||
// rolled to.
|
||||
getNewHistoryRollFileName(otherHistoryFiles []string) string
|
||||
|
||||
getCurrentFileName() string
|
||||
}
|
||||
|
||||
// rollingFileWriter writes received messages to a file, until time interval passes
|
||||
// or file exceeds a specified limit. After that the current log file is renamed
|
||||
// and writer starts to log into a new file. You can set a limit for such renamed
|
||||
// files count, if you want, and then the rolling writer would delete older ones when
|
||||
// the files count exceed the specified limit.
|
||||
type rollingFileWriter struct {
|
||||
fileName string // log file name
|
||||
currentDirPath string
|
||||
currentFile *os.File
|
||||
currentName string
|
||||
currentFileSize int64
|
||||
rollingType rollingType // Rolling mode (Files roll by size/date/...)
|
||||
archiveType rollingArchiveType
|
||||
archivePath string
|
||||
archiveExploded bool
|
||||
fullName bool
|
||||
maxRolls int
|
||||
nameMode rollingNameMode
|
||||
self rollerVirtual // Used for virtual calls
|
||||
rollLock sync.Mutex
|
||||
}
|
||||
|
||||
func newRollingFileWriter(fpath string, rtype rollingType, atype rollingArchiveType, apath string, maxr int, namemode rollingNameMode,
|
||||
archiveExploded bool, fullName bool) (*rollingFileWriter, error) {
|
||||
rw := new(rollingFileWriter)
|
||||
rw.currentDirPath, rw.fileName = filepath.Split(fpath)
|
||||
if len(rw.currentDirPath) == 0 {
|
||||
rw.currentDirPath = "."
|
||||
}
|
||||
|
||||
rw.rollingType = rtype
|
||||
rw.archiveType = atype
|
||||
rw.archivePath = apath
|
||||
rw.nameMode = namemode
|
||||
rw.maxRolls = maxr
|
||||
rw.archiveExploded = archiveExploded
|
||||
rw.fullName = fullName
|
||||
return rw, nil
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) hasRollName(file string) bool {
|
||||
switch rw.nameMode {
|
||||
case rollingNameModePostfix:
|
||||
rname := rw.fileName + rollingLogHistoryDelimiter
|
||||
return strings.HasPrefix(file, rname)
|
||||
case rollingNameModePrefix:
|
||||
rname := rollingLogHistoryDelimiter + rw.fileName
|
||||
return strings.HasSuffix(file, rname)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) createFullFileName(originalName, rollname string) string {
|
||||
switch rw.nameMode {
|
||||
case rollingNameModePostfix:
|
||||
return originalName + rollingLogHistoryDelimiter + rollname
|
||||
case rollingNameModePrefix:
|
||||
return rollname + rollingLogHistoryDelimiter + originalName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) getSortedLogHistory() ([]string, error) {
|
||||
files, err := getDirFilePaths(rw.currentDirPath, nil, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var validRollNames []string
|
||||
for _, file := range files {
|
||||
if rw.hasRollName(file) {
|
||||
rname := rw.getFileRollName(file)
|
||||
if rw.self.isFileRollNameValid(rname) {
|
||||
validRollNames = append(validRollNames, rname)
|
||||
}
|
||||
}
|
||||
}
|
||||
sortedTails, err := rw.self.sortFileRollNamesAsc(validRollNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validSortedFiles := make([]string, len(sortedTails))
|
||||
for i, v := range sortedTails {
|
||||
validSortedFiles[i] = rw.createFullFileName(rw.fileName, v)
|
||||
}
|
||||
return validSortedFiles, nil
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) createFileAndFolderIfNeeded(first bool) error {
|
||||
var err error
|
||||
|
||||
if len(rw.currentDirPath) != 0 {
|
||||
err = os.MkdirAll(rw.currentDirPath, defaultDirectoryPermissions)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
rw.currentName = rw.self.getCurrentFileName()
|
||||
filePath := filepath.Join(rw.currentDirPath, rw.currentName)
|
||||
|
||||
// This will either open the existing file (without truncating it) or
|
||||
// create if necessary. Append mode avoids any race conditions.
|
||||
rw.currentFile, err = os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, defaultFilePermissions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, err := rw.currentFile.Stat()
|
||||
if err != nil {
|
||||
rw.currentFile.Close()
|
||||
rw.currentFile = nil
|
||||
return err
|
||||
}
|
||||
|
||||
rw.currentFileSize = stat.Size()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) archiveExplodedLogs(logFilename string, compressionType compressionType) (err error) {
|
||||
closeWithError := func(c io.Closer) {
|
||||
if cerr := c.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}
|
||||
|
||||
rollPath := filepath.Join(rw.currentDirPath, logFilename)
|
||||
src, err := os.Open(rollPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close() // Read-only
|
||||
|
||||
// Buffer to a temporary file on the same partition
|
||||
// Note: archivePath is a path to a directory when handling exploded logs
|
||||
dst, err := rw.tempArchiveFile(rw.archivePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
closeWithError(dst)
|
||||
if err != nil {
|
||||
os.Remove(dst.Name()) // Can't do anything when we fail to remove temp file
|
||||
return
|
||||
}
|
||||
|
||||
// Finalize archive by swapping the buffered archive into place
|
||||
err = os.Rename(dst.Name(), filepath.Join(rw.archivePath,
|
||||
compressionType.rollingArchiveTypeName(logFilename, true)))
|
||||
}()
|
||||
|
||||
// archive entry
|
||||
w := compressionType.archiver(dst, true)
|
||||
defer closeWithError(w)
|
||||
fi, err := src.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.NextFile(logFilename, fi); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(w, src)
|
||||
return err
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) archiveUnexplodedLogs(compressionType compressionType, rollsToDelete int, history []string) (err error) {
|
||||
closeWithError := func(c io.Closer) {
|
||||
if cerr := c.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer to a temporary file on the same partition
|
||||
// Note: archivePath is a path to a file when handling unexploded logs
|
||||
dst, err := rw.tempArchiveFile(filepath.Dir(rw.archivePath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
closeWithError(dst)
|
||||
if err != nil {
|
||||
os.Remove(dst.Name()) // Can't do anything when we fail to remove temp file
|
||||
return
|
||||
}
|
||||
|
||||
// Finalize archive by moving the buffered archive into place
|
||||
err = os.Rename(dst.Name(), rw.archivePath)
|
||||
}()
|
||||
|
||||
w := compressionType.archiver(dst, false)
|
||||
defer closeWithError(w)
|
||||
|
||||
src, err := os.Open(rw.archivePath)
|
||||
switch {
|
||||
// Archive exists
|
||||
case err == nil:
|
||||
defer src.Close() // Read-only
|
||||
|
||||
r, err := compressionType.unarchiver(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close() // Read-only
|
||||
|
||||
if err := archive.Copy(w, r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Failed to stat
|
||||
case !os.IsNotExist(err):
|
||||
return err
|
||||
}
|
||||
|
||||
// Add new files to the archive
|
||||
for i := 0; i < rollsToDelete; i++ {
|
||||
rollPath := filepath.Join(rw.currentDirPath, history[i])
|
||||
src, err := os.Open(rollPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close() // Read-only
|
||||
fi, err := src.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.NextFile(src.Name(), fi); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(w, src); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) deleteOldRolls(history []string) error {
|
||||
if rw.maxRolls <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
rollsToDelete := len(history) - rw.maxRolls
|
||||
if rollsToDelete <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rw.archiveType != rollingArchiveNone {
|
||||
if rw.archiveExploded {
|
||||
os.MkdirAll(rw.archivePath, defaultDirectoryPermissions)
|
||||
|
||||
// Archive logs
|
||||
for i := 0; i < rollsToDelete; i++ {
|
||||
rw.archiveExplodedLogs(history[i], compressionTypes[rw.archiveType])
|
||||
}
|
||||
} else {
|
||||
os.MkdirAll(filepath.Dir(rw.archivePath), defaultDirectoryPermissions)
|
||||
|
||||
rw.archiveUnexplodedLogs(compressionTypes[rw.archiveType], rollsToDelete, history)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
// In all cases (archive files or not) the files should be deleted.
|
||||
for i := 0; i < rollsToDelete; i++ {
|
||||
// Try best to delete files without breaking the loop.
|
||||
if err = tryRemoveFile(filepath.Join(rw.currentDirPath, history[i])); err != nil {
|
||||
reportInternalError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) getFileRollName(fileName string) string {
|
||||
switch rw.nameMode {
|
||||
case rollingNameModePostfix:
|
||||
return fileName[len(rw.fileName+rollingLogHistoryDelimiter):]
|
||||
case rollingNameModePrefix:
|
||||
return fileName[:len(fileName)-len(rw.fileName+rollingLogHistoryDelimiter)]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) roll() error {
|
||||
// First, close current file.
|
||||
err := rw.currentFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rw.currentFile = nil
|
||||
|
||||
// Current history of all previous log files.
|
||||
// For file roller it may be like this:
|
||||
// * ...
|
||||
// * file.log.4
|
||||
// * file.log.5
|
||||
// * file.log.6
|
||||
//
|
||||
// For date roller it may look like this:
|
||||
// * ...
|
||||
// * file.log.11.Aug.13
|
||||
// * file.log.15.Aug.13
|
||||
// * file.log.16.Aug.13
|
||||
// Sorted log history does NOT include current file.
|
||||
history, err := rw.getSortedLogHistory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Renames current file to create a new roll history entry
|
||||
// For file roller it may be like this:
|
||||
// * ...
|
||||
// * file.log.4
|
||||
// * file.log.5
|
||||
// * file.log.6
|
||||
// n file.log.7 <---- RENAMED (from file.log)
|
||||
newHistoryName := rw.createFullFileName(rw.fileName,
|
||||
rw.self.getNewHistoryRollFileName(history))
|
||||
|
||||
err = os.Rename(filepath.Join(rw.currentDirPath, rw.currentName), filepath.Join(rw.currentDirPath, newHistoryName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Finally, add the newly added history file to the history archive
|
||||
// and, if after that the archive exceeds the allowed max limit, older rolls
|
||||
// must the removed/archived.
|
||||
history = append(history, newHistoryName)
|
||||
if len(history) > rw.maxRolls {
|
||||
err = rw.deleteOldRolls(history)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) Write(bytes []byte) (n int, err error) {
|
||||
rw.rollLock.Lock()
|
||||
defer rw.rollLock.Unlock()
|
||||
|
||||
if rw.self.needsToRoll() {
|
||||
if err := rw.roll(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if rw.currentFile == nil {
|
||||
err := rw.createFileAndFolderIfNeeded(true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
n, err = rw.currentFile.Write(bytes)
|
||||
rw.currentFileSize += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) Close() error {
|
||||
if rw.currentFile != nil {
|
||||
e := rw.currentFile.Close()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
rw.currentFile = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *rollingFileWriter) tempArchiveFile(archiveDir string) (*os.File, error) {
|
||||
tmp := filepath.Join(archiveDir, ".seelog_tmp")
|
||||
if err := os.MkdirAll(tmp, defaultDirectoryPermissions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.TempFile(tmp, "archived_logs")
|
||||
}
|
||||
|
||||
// =============================================================================================
|
||||
// Different types of rolling writers
|
||||
// =============================================================================================
|
||||
|
||||
// --------------------------------------------------
|
||||
// Rolling writer by SIZE
|
||||
// --------------------------------------------------
|
||||
|
||||
// rollingFileWriterSize performs roll when file exceeds a specified limit.
|
||||
type rollingFileWriterSize struct {
|
||||
*rollingFileWriter
|
||||
maxFileSize int64
|
||||
}
|
||||
|
||||
func NewRollingFileWriterSize(fpath string, atype rollingArchiveType, apath string, maxSize int64, maxRolls int, namemode rollingNameMode, archiveExploded bool) (*rollingFileWriterSize, error) {
|
||||
rw, err := newRollingFileWriter(fpath, rollingTypeSize, atype, apath, maxRolls, namemode, archiveExploded, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rws := &rollingFileWriterSize{rw, maxSize}
|
||||
rws.self = rws
|
||||
return rws, nil
|
||||
}
|
||||
|
||||
func (rws *rollingFileWriterSize) needsToRoll() bool {
|
||||
return rws.currentFileSize >= rws.maxFileSize
|
||||
}
|
||||
|
||||
func (rws *rollingFileWriterSize) isFileRollNameValid(rname string) bool {
|
||||
if len(rname) == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := strconv.Atoi(rname)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
type rollSizeFileTailsSlice []string
|
||||
|
||||
func (p rollSizeFileTailsSlice) Len() int {
|
||||
return len(p)
|
||||
}
|
||||
func (p rollSizeFileTailsSlice) Less(i, j int) bool {
|
||||
v1, _ := strconv.Atoi(p[i])
|
||||
v2, _ := strconv.Atoi(p[j])
|
||||
return v1 < v2
|
||||
}
|
||||
func (p rollSizeFileTailsSlice) Swap(i, j int) {
|
||||
p[i], p[j] = p[j], p[i]
|
||||
}
|
||||
|
||||
func (rws *rollingFileWriterSize) sortFileRollNamesAsc(fs []string) ([]string, error) {
|
||||
ss := rollSizeFileTailsSlice(fs)
|
||||
sort.Sort(ss)
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
func (rws *rollingFileWriterSize) getNewHistoryRollFileName(otherLogFiles []string) string {
|
||||
v := 0
|
||||
if len(otherLogFiles) != 0 {
|
||||
latest := otherLogFiles[len(otherLogFiles)-1]
|
||||
v, _ = strconv.Atoi(rws.getFileRollName(latest))
|
||||
}
|
||||
return fmt.Sprintf("%d", v+1)
|
||||
}
|
||||
|
||||
func (rws *rollingFileWriterSize) getCurrentFileName() string {
|
||||
return rws.fileName
|
||||
}
|
||||
|
||||
func (rws *rollingFileWriterSize) String() string {
|
||||
return fmt.Sprintf("Rolling file writer (By SIZE): filename: %s, archive: %s, archivefile: %s, maxFileSize: %v, maxRolls: %v",
|
||||
rws.fileName,
|
||||
rollingArchiveTypesStringRepresentation[rws.archiveType],
|
||||
rws.archivePath,
|
||||
rws.maxFileSize,
|
||||
rws.maxRolls)
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Rolling writer by TIME
|
||||
// --------------------------------------------------
|
||||
|
||||
// rollingFileWriterTime performs roll when a specified time interval has passed.
|
||||
type rollingFileWriterTime struct {
|
||||
*rollingFileWriter
|
||||
timePattern string
|
||||
currentTimeFileName string
|
||||
}
|
||||
|
||||
func NewRollingFileWriterTime(fpath string, atype rollingArchiveType, apath string, maxr int,
|
||||
timePattern string, namemode rollingNameMode, archiveExploded bool, fullName bool) (*rollingFileWriterTime, error) {
|
||||
|
||||
rw, err := newRollingFileWriter(fpath, rollingTypeTime, atype, apath, maxr, namemode, archiveExploded, fullName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rws := &rollingFileWriterTime{rw, timePattern, ""}
|
||||
rws.self = rws
|
||||
return rws, nil
|
||||
}
|
||||
|
||||
func (rwt *rollingFileWriterTime) needsToRoll() bool {
|
||||
newName := time.Now().Format(rwt.timePattern)
|
||||
|
||||
if rwt.currentTimeFileName == "" {
|
||||
// first run; capture the current name
|
||||
rwt.currentTimeFileName = newName
|
||||
return false
|
||||
}
|
||||
|
||||
return newName != rwt.currentTimeFileName
|
||||
}
|
||||
|
||||
func (rwt *rollingFileWriterTime) isFileRollNameValid(rname string) bool {
|
||||
if len(rname) == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := time.ParseInLocation(rwt.timePattern, rname, time.Local)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
type rollTimeFileTailsSlice struct {
|
||||
data []string
|
||||
pattern string
|
||||
}
|
||||
|
||||
func (p rollTimeFileTailsSlice) Len() int {
|
||||
return len(p.data)
|
||||
}
|
||||
|
||||
func (p rollTimeFileTailsSlice) Less(i, j int) bool {
|
||||
t1, _ := time.ParseInLocation(p.pattern, p.data[i], time.Local)
|
||||
t2, _ := time.ParseInLocation(p.pattern, p.data[j], time.Local)
|
||||
return t1.Before(t2)
|
||||
}
|
||||
|
||||
func (p rollTimeFileTailsSlice) Swap(i, j int) {
|
||||
p.data[i], p.data[j] = p.data[j], p.data[i]
|
||||
}
|
||||
|
||||
func (rwt *rollingFileWriterTime) sortFileRollNamesAsc(fs []string) ([]string, error) {
|
||||
ss := rollTimeFileTailsSlice{data: fs, pattern: rwt.timePattern}
|
||||
sort.Sort(ss)
|
||||
return ss.data, nil
|
||||
}
|
||||
|
||||
func (rwt *rollingFileWriterTime) getNewHistoryRollFileName(_ []string) string {
|
||||
newFileName := rwt.currentTimeFileName
|
||||
rwt.currentTimeFileName = time.Now().Format(rwt.timePattern)
|
||||
return newFileName
|
||||
}
|
||||
|
||||
func (rwt *rollingFileWriterTime) getCurrentFileName() string {
|
||||
if rwt.fullName {
|
||||
return rwt.createFullFileName(rwt.fileName, time.Now().Format(rwt.timePattern))
|
||||
}
|
||||
return rwt.fileName
|
||||
}
|
||||
|
||||
func (rwt *rollingFileWriterTime) String() string {
|
||||
return fmt.Sprintf("Rolling file writer (By TIME): filename: %s, archive: %s, archivefile: %s, pattern: %s, maxRolls: %v",
|
||||
rwt.fileName,
|
||||
rollingArchiveTypesStringRepresentation[rwt.archiveType],
|
||||
rwt.archivePath,
|
||||
rwt.timePattern,
|
||||
rwt.maxRolls)
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package seelog
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/smtp"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default subject phrase for sending emails.
|
||||
DefaultSubjectPhrase = "Diagnostic message from server: "
|
||||
|
||||
// Message subject pattern composed according to RFC 5321.
|
||||
rfc5321SubjectPattern = "From: %s <%s>\nSubject: %s\n\n"
|
||||
)
|
||||
|
||||
// smtpWriter is used to send emails via given SMTP-server.
|
||||
type smtpWriter struct {
|
||||
auth smtp.Auth
|
||||
hostName string
|
||||
hostPort string
|
||||
hostNameWithPort string
|
||||
senderAddress string
|
||||
senderName string
|
||||
recipientAddresses []string
|
||||
caCertDirPaths []string
|
||||
mailHeaders []string
|
||||
subject string
|
||||
}
|
||||
|
||||
// NewSMTPWriter returns a new SMTP-writer.
|
||||
func NewSMTPWriter(sa, sn string, ras []string, hn, hp, un, pwd string, cacdps []string, subj string, headers []string) *smtpWriter {
|
||||
return &smtpWriter{
|
||||
auth: smtp.PlainAuth("", un, pwd, hn),
|
||||
hostName: hn,
|
||||
hostPort: hp,
|
||||
hostNameWithPort: fmt.Sprintf("%s:%s", hn, hp),
|
||||
senderAddress: sa,
|
||||
senderName: sn,
|
||||
recipientAddresses: ras,
|
||||
caCertDirPaths: cacdps,
|
||||
subject: subj,
|
||||
mailHeaders: headers,
|
||||
}
|
||||
}
|
||||
|
||||
func prepareMessage(senderAddr, senderName, subject string, body []byte, headers []string) []byte {
|
||||
headerLines := fmt.Sprintf(rfc5321SubjectPattern, senderName, senderAddr, subject)
|
||||
// Build header lines if configured.
|
||||
if headers != nil && len(headers) > 0 {
|
||||
headerLines += strings.Join(headers, "\n")
|
||||
headerLines += "\n"
|
||||
}
|
||||
return append([]byte(headerLines), body...)
|
||||
}
|
||||
|
||||
// getTLSConfig gets paths of PEM files with certificates,
|
||||
// host server name and tries to create an appropriate TLS.Config.
|
||||
func getTLSConfig(pemFileDirPaths []string, hostName string) (config *tls.Config, err error) {
|
||||
if pemFileDirPaths == nil || len(pemFileDirPaths) == 0 {
|
||||
err = errors.New("invalid PEM file paths")
|
||||
return
|
||||
}
|
||||
pemEncodedContent := []byte{}
|
||||
var (
|
||||
e error
|
||||
bytes []byte
|
||||
)
|
||||
// Create a file-filter-by-extension, set aside non-pem files.
|
||||
pemFilePathFilter := func(fp string) bool {
|
||||
if filepath.Ext(fp) == ".pem" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, pemFileDirPath := range pemFileDirPaths {
|
||||
pemFilePaths, err := getDirFilePaths(pemFileDirPath, pemFilePathFilter, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Put together all the PEM files to decode them as a whole byte slice.
|
||||
for _, pfp := range pemFilePaths {
|
||||
if bytes, e = ioutil.ReadFile(pfp); e == nil {
|
||||
pemEncodedContent = append(pemEncodedContent, bytes...)
|
||||
} else {
|
||||
return nil, fmt.Errorf("cannot read file: %s: %s", pfp, e.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
config = &tls.Config{RootCAs: x509.NewCertPool(), ServerName: hostName}
|
||||
isAppended := config.RootCAs.AppendCertsFromPEM(pemEncodedContent)
|
||||
if !isAppended {
|
||||
// Extract this into a separate error.
|
||||
err = errors.New("invalid PEM content")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SendMail accepts TLS configuration, connects to the server at addr,
|
||||
// switches to TLS if possible, authenticates with mechanism a if possible,
|
||||
// and then sends an email from address from, to addresses to, with message msg.
|
||||
func sendMailWithTLSConfig(config *tls.Config, addr string, a smtp.Auth, from string, to []string, msg []byte) error {
|
||||
c, err := smtp.Dial(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check if the server supports STARTTLS extension.
|
||||
if ok, _ := c.Extension("STARTTLS"); ok {
|
||||
if err = c.StartTLS(config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Check if the server supports AUTH extension and use given smtp.Auth.
|
||||
if a != nil {
|
||||
if isSupported, _ := c.Extension("AUTH"); isSupported {
|
||||
if err = c.Auth(a); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// Portion of code from the official smtp.SendMail function,
|
||||
// see http://golang.org/src/pkg/net/smtp/smtp.go.
|
||||
if err = c.Mail(from); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, addr := range to {
|
||||
if err = c.Rcpt(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Quit()
|
||||
}
|
||||
|
||||
// Write pushes a text message properly composed according to RFC 5321
|
||||
// to a post server, which sends it to the recipients.
|
||||
func (smtpw *smtpWriter) Write(data []byte) (int, error) {
|
||||
var err error
|
||||
|
||||
if smtpw.caCertDirPaths == nil {
|
||||
err = smtp.SendMail(
|
||||
smtpw.hostNameWithPort,
|
||||
smtpw.auth,
|
||||
smtpw.senderAddress,
|
||||
smtpw.recipientAddresses,
|
||||
prepareMessage(smtpw.senderAddress, smtpw.senderName, smtpw.subject, data, smtpw.mailHeaders),
|
||||
)
|
||||
} else {
|
||||
config, e := getTLSConfig(smtpw.caCertDirPaths, smtpw.hostName)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
err = sendMailWithTLSConfig(
|
||||
config,
|
||||
smtpw.hostNameWithPort,
|
||||
smtpw.auth,
|
||||
smtpw.senderAddress,
|
||||
smtpw.recipientAddresses,
|
||||
prepareMessage(smtpw.senderAddress, smtpw.senderName, smtpw.subject, data, smtpw.mailHeaders),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// Close closes down SMTP-connection.
|
||||
func (smtpw *smtpWriter) Close() error {
|
||||
// Do nothing as Write method opens and closes connection automatically.
|
||||
return nil
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
Copyright (c) 2012 Dave Grijalva
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
## Migration Guide from v2 -> v3
|
||||
|
||||
Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code.
|
||||
|
||||
### `Token.Claims` is now an interface type
|
||||
|
||||
The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`.
|
||||
|
||||
`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property.
|
||||
|
||||
The old example for parsing a token looked like this..
|
||||
|
||||
```go
|
||||
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
|
||||
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||
}
|
||||
```
|
||||
|
||||
is now directly mapped to...
|
||||
|
||||
```go
|
||||
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"])
|
||||
}
|
||||
```
|
||||
|
||||
`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type.
|
||||
|
||||
```go
|
||||
type MyCustomClaims struct {
|
||||
User string
|
||||
*StandardClaims
|
||||
}
|
||||
|
||||
if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil {
|
||||
claims := token.Claims.(*MyCustomClaims)
|
||||
fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt)
|
||||
}
|
||||
```
|
||||
|
||||
### `ParseFromRequest` has been moved
|
||||
|
||||
To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`.
|
||||
|
||||
`Extractors` do the work of picking the token string out of a request. The interface is simple and composable.
|
||||
|
||||
This simple parsing example:
|
||||
|
||||
```go
|
||||
if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil {
|
||||
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||
}
|
||||
```
|
||||
|
||||
is directly mapped to:
|
||||
|
||||
```go
|
||||
if token, err := request.ParseFromRequest(req, request.OAuth2Extractor, keyLookupFunc); err == nil {
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"])
|
||||
}
|
||||
```
|
||||
|
||||
There are several concrete `Extractor` types provided for your convenience:
|
||||
|
||||
* `HeaderExtractor` will search a list of headers until one contains content.
|
||||
* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content.
|
||||
* `MultiExtractor` will try a list of `Extractors` in order until one returns content.
|
||||
* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token.
|
||||
* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument
|
||||
* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header
|
||||
|
||||
|
||||
### RSA signing methods no longer accept `[]byte` keys
|
||||
|
||||
Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse.
|
||||
|
||||
To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types.
|
||||
|
||||
```go
|
||||
func keyLookupFunc(*Token) (interface{}, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
// Look up key
|
||||
key, err := lookupPublicKey(token.Header["kid"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Unpack key from PEM encoded PKCS8
|
||||
return jwt.ParseRSAPublicKeyFromPEM(key)
|
||||
}
|
||||
```
|
|
@ -1,100 +0,0 @@
|
|||
# jwt-go
|
||||
|
||||
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go)
|
||||
[![GoDoc](https://godoc.org/github.com/dgrijalva/jwt-go?status.svg)](https://godoc.org/github.com/dgrijalva/jwt-go)
|
||||
|
||||
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html)
|
||||
|
||||
**NEW VERSION COMING:** There have been a lot of improvements suggested since the version 3.0.0 released in 2016. I'm working now on cutting two different releases: 3.2.0 will contain any non-breaking changes or enhancements. 4.0.0 will follow shortly which will include breaking changes. See the 4.0.0 milestone to get an idea of what's coming. If you have other ideas, or would like to participate in 4.0.0, now's the time. If you depend on this library and don't want to be interrupted, I recommend you use your dependency mangement tool to pin to version 3.
|
||||
|
||||
**SECURITY NOTICE:** Some older versions of Go have a security issue in the cryotp/elliptic. Recommendation is to upgrade to at least 1.8.3. See issue #216 for more detail.
|
||||
|
||||
**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided.
|
||||
|
||||
## What the heck is a JWT?
|
||||
|
||||
JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens.
|
||||
|
||||
In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way.
|
||||
|
||||
The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used.
|
||||
|
||||
The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-jones-json-web-token.html) for information about reserved keys and the proper way to add your own.
|
||||
|
||||
## What's in the box?
|
||||
|
||||
This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own.
|
||||
|
||||
## Examples
|
||||
|
||||
See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage:
|
||||
|
||||
* [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac)
|
||||
* [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac)
|
||||
* [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples)
|
||||
|
||||
## Extensions
|
||||
|
||||
This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`.
|
||||
|
||||
Here's an example of an extension that integrates with the Google App Engine signing tools: https://github.com/someone1/gcp-jwt-go
|
||||
|
||||
## Compliance
|
||||
|
||||
This library was last reviewed to comply with [RTF 7519](http://www.rfc-editor.org/info/rfc7519) dated May 2015 with a few notable differences:
|
||||
|
||||
* In order to protect against accidental use of [Unsecured JWTs](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#UnsecuredJWT), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key.
|
||||
|
||||
## Project Status & Versioning
|
||||
|
||||
This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason).
|
||||
|
||||
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases).
|
||||
|
||||
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v3`. It will do the right thing WRT semantic versioning.
|
||||
|
||||
**BREAKING CHANGES:***
|
||||
* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
|
||||
|
||||
## Usage Tips
|
||||
|
||||
### Signing vs Encryption
|
||||
|
||||
A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data:
|
||||
|
||||
* The author of the token was in the possession of the signing secret
|
||||
* The data has not been modified since it was signed
|
||||
|
||||
It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library.
|
||||
|
||||
### Choosing a Signing Method
|
||||
|
||||
There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric.
|
||||
|
||||
Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation.
|
||||
|
||||
Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification.
|
||||
|
||||
### Signing Methods and Key Types
|
||||
|
||||
Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones:
|
||||
|
||||
* The [HMAC signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation
|
||||
* The [RSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation
|
||||
* The [ECDSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation
|
||||
|
||||
### JWT and OAuth
|
||||
|
||||
It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication.
|
||||
|
||||
Without going too far down the rabbit hole, here's a description of the interaction of these technologies:
|
||||
|
||||
* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth.
|
||||
* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token.
|
||||
* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL.
|
||||
|
||||
## More
|
||||
|
||||
Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
|
||||
|
||||
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation.
|
|
@ -1,118 +0,0 @@
|
|||
## `jwt-go` Version History
|
||||
|
||||
#### 3.2.0
|
||||
|
||||
* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation
|
||||
* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate
|
||||
* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before.
|
||||
* Deprecated `ParseFromRequestWithClaims` to simplify API in the future.
|
||||
|
||||
#### 3.1.0
|
||||
|
||||
* Improvements to `jwt` command line tool
|
||||
* Added `SkipClaimsValidation` option to `Parser`
|
||||
* Documentation updates
|
||||
|
||||
#### 3.0.0
|
||||
|
||||
* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code
|
||||
* Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods.
|
||||
* `ParseFromRequest` has been moved to `request` subpackage and usage has changed
|
||||
* The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims.
|
||||
* Other Additions and Changes
|
||||
* Added `Claims` interface type to allow users to decode the claims into a custom type
|
||||
* Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into.
|
||||
* Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage
|
||||
* Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims`
|
||||
* Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`.
|
||||
* Added several new, more specific, validation errors to error type bitmask
|
||||
* Moved examples from README to executable example files
|
||||
* Signing method registry is now thread safe
|
||||
* Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser)
|
||||
|
||||
#### 2.7.0
|
||||
|
||||
This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes.
|
||||
|
||||
* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying
|
||||
* Error text for expired tokens includes how long it's been expired
|
||||
* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM`
|
||||
* Documentation updates
|
||||
|
||||
#### 2.6.0
|
||||
|
||||
* Exposed inner error within ValidationError
|
||||
* Fixed validation errors when using UseJSONNumber flag
|
||||
* Added several unit tests
|
||||
|
||||
#### 2.5.0
|
||||
|
||||
* Added support for signing method none. You shouldn't use this. The API tries to make this clear.
|
||||
* Updated/fixed some documentation
|
||||
* Added more helpful error message when trying to parse tokens that begin with `BEARER `
|
||||
|
||||
#### 2.4.0
|
||||
|
||||
* Added new type, Parser, to allow for configuration of various parsing parameters
|
||||
* You can now specify a list of valid signing methods. Anything outside this set will be rejected.
|
||||
* You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON
|
||||
* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go)
|
||||
* Fixed some bugs with ECDSA parsing
|
||||
|
||||
#### 2.3.0
|
||||
|
||||
* Added support for ECDSA signing methods
|
||||
* Added support for RSA PSS signing methods (requires go v1.4)
|
||||
|
||||
#### 2.2.0
|
||||
|
||||
* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic.
|
||||
|
||||
#### 2.1.0
|
||||
|
||||
Backwards compatible API change that was missed in 2.0.0.
|
||||
|
||||
* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte`
|
||||
|
||||
#### 2.0.0
|
||||
|
||||
There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change.
|
||||
|
||||
The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`.
|
||||
|
||||
It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`.
|
||||
|
||||
* **Compatibility Breaking Changes**
|
||||
* `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct`
|
||||
* `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct`
|
||||
* `KeyFunc` now returns `interface{}` instead of `[]byte`
|
||||
* `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key
|
||||
* `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key
|
||||
* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type.
|
||||
* Added public package global `SigningMethodHS256`
|
||||
* Added public package global `SigningMethodHS384`
|
||||
* Added public package global `SigningMethodHS512`
|
||||
* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type.
|
||||
* Added public package global `SigningMethodRS256`
|
||||
* Added public package global `SigningMethodRS384`
|
||||
* Added public package global `SigningMethodRS512`
|
||||
* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged.
|
||||
* Refactored the RSA implementation to be easier to read
|
||||
* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM`
|
||||
|
||||
#### 1.0.2
|
||||
|
||||
* Fixed bug in parsing public keys from certificates
|
||||
* Added more tests around the parsing of keys for RS256
|
||||
* Code refactoring in RS256 implementation. No functional changes
|
||||
|
||||
#### 1.0.1
|
||||
|
||||
* Fixed panic if RS256 signing method was passed an invalid key
|
||||
|
||||
#### 1.0.0
|
||||
|
||||
* First versioned release
|
||||
* API stabilized
|
||||
* Supports creating, signing, parsing, and validating JWT tokens
|
||||
* Supports RS256 and HS256 signing methods
|
|
@ -1,134 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// For a type to be a Claims object, it must just have a Valid method that determines
|
||||
// if the token is invalid for any supported reason
|
||||
type Claims interface {
|
||||
Valid() error
|
||||
}
|
||||
|
||||
// Structured version of Claims Section, as referenced at
|
||||
// https://tools.ietf.org/html/rfc7519#section-4.1
|
||||
// See examples for how to use this with your own claim types
|
||||
type StandardClaims struct {
|
||||
Audience string `json:"aud,omitempty"`
|
||||
ExpiresAt int64 `json:"exp,omitempty"`
|
||||
Id string `json:"jti,omitempty"`
|
||||
IssuedAt int64 `json:"iat,omitempty"`
|
||||
Issuer string `json:"iss,omitempty"`
|
||||
NotBefore int64 `json:"nbf,omitempty"`
|
||||
Subject string `json:"sub,omitempty"`
|
||||
}
|
||||
|
||||
// Validates time based claims "exp, iat, nbf".
|
||||
// There is no accounting for clock skew.
|
||||
// As well, if any of the above claims are not in the token, it will still
|
||||
// be considered a valid claim.
|
||||
func (c StandardClaims) Valid() error {
|
||||
vErr := new(ValidationError)
|
||||
now := TimeFunc().Unix()
|
||||
|
||||
// The claims below are optional, by default, so if they are set to the
|
||||
// default value in Go, let's not fail the verification for them.
|
||||
if c.VerifyExpiresAt(now, false) == false {
|
||||
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
|
||||
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
|
||||
vErr.Errors |= ValidationErrorExpired
|
||||
}
|
||||
|
||||
if c.VerifyIssuedAt(now, false) == false {
|
||||
vErr.Inner = fmt.Errorf("Token used before issued")
|
||||
vErr.Errors |= ValidationErrorIssuedAt
|
||||
}
|
||||
|
||||
if c.VerifyNotBefore(now, false) == false {
|
||||
vErr.Inner = fmt.Errorf("token is not valid yet")
|
||||
vErr.Errors |= ValidationErrorNotValidYet
|
||||
}
|
||||
|
||||
if vErr.valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return vErr
|
||||
}
|
||||
|
||||
// Compares the aud claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
|
||||
return verifyAud(c.Audience, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the exp claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
|
||||
return verifyExp(c.ExpiresAt, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the iat claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
|
||||
return verifyIat(c.IssuedAt, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the iss claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
|
||||
return verifyIss(c.Issuer, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the nbf claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
|
||||
return verifyNbf(c.NotBefore, cmp, req)
|
||||
}
|
||||
|
||||
// ----- helpers
|
||||
|
||||
func verifyAud(aud string, cmp string, required bool) bool {
|
||||
if aud == "" {
|
||||
return !required
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func verifyExp(exp int64, now int64, required bool) bool {
|
||||
if exp == 0 {
|
||||
return !required
|
||||
}
|
||||
return now <= exp
|
||||
}
|
||||
|
||||
func verifyIat(iat int64, now int64, required bool) bool {
|
||||
if iat == 0 {
|
||||
return !required
|
||||
}
|
||||
return now >= iat
|
||||
}
|
||||
|
||||
func verifyIss(iss string, cmp string, required bool) bool {
|
||||
if iss == "" {
|
||||
return !required
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func verifyNbf(nbf int64, now int64, required bool) bool {
|
||||
if nbf == 0 {
|
||||
return !required
|
||||
}
|
||||
return now >= nbf
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html
|
||||
//
|
||||
// See README.md for more info.
|
||||
package jwt
|
|
@ -1,148 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var (
|
||||
// Sadly this is missing from crypto/ecdsa compared to crypto/rsa
|
||||
ErrECDSAVerification = errors.New("crypto/ecdsa: verification error")
|
||||
)
|
||||
|
||||
// Implements the ECDSA family of signing methods signing methods
|
||||
// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification
|
||||
type SigningMethodECDSA struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
KeySize int
|
||||
CurveBits int
|
||||
}
|
||||
|
||||
// Specific instances for EC256 and company
|
||||
var (
|
||||
SigningMethodES256 *SigningMethodECDSA
|
||||
SigningMethodES384 *SigningMethodECDSA
|
||||
SigningMethodES512 *SigningMethodECDSA
|
||||
)
|
||||
|
||||
func init() {
|
||||
// ES256
|
||||
SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256}
|
||||
RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod {
|
||||
return SigningMethodES256
|
||||
})
|
||||
|
||||
// ES384
|
||||
SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384}
|
||||
RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod {
|
||||
return SigningMethodES384
|
||||
})
|
||||
|
||||
// ES512
|
||||
SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521}
|
||||
RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod {
|
||||
return SigningMethodES512
|
||||
})
|
||||
}
|
||||
|
||||
func (m *SigningMethodECDSA) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Implements the Verify method from SigningMethod
|
||||
// For this verify method, key must be an ecdsa.PublicKey struct
|
||||
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error {
|
||||
var err error
|
||||
|
||||
// Decode the signature
|
||||
var sig []byte
|
||||
if sig, err = DecodeSegment(signature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the key
|
||||
var ecdsaKey *ecdsa.PublicKey
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return ErrInvalidKeyType
|
||||
}
|
||||
|
||||
if len(sig) != 2*m.KeySize {
|
||||
return ErrECDSAVerification
|
||||
}
|
||||
|
||||
r := big.NewInt(0).SetBytes(sig[:m.KeySize])
|
||||
s := big.NewInt(0).SetBytes(sig[m.KeySize:])
|
||||
|
||||
// Create hasher
|
||||
if !m.Hash.Available() {
|
||||
return ErrHashUnavailable
|
||||
}
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Verify the signature
|
||||
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
|
||||
return nil
|
||||
} else {
|
||||
return ErrECDSAVerification
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod
|
||||
// For this signing method, key must be an ecdsa.PrivateKey struct
|
||||
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) {
|
||||
// Get the key
|
||||
var ecdsaKey *ecdsa.PrivateKey
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return "", ErrInvalidKeyType
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
if !m.Hash.Available() {
|
||||
return "", ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Sign the string and return r, s
|
||||
if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil {
|
||||
curveBits := ecdsaKey.Curve.Params().BitSize
|
||||
|
||||
if m.CurveBits != curveBits {
|
||||
return "", ErrInvalidKey
|
||||
}
|
||||
|
||||
keyBytes := curveBits / 8
|
||||
if curveBits%8 > 0 {
|
||||
keyBytes += 1
|
||||
}
|
||||
|
||||
// We serialize the outpus (r and s) into big-endian byte arrays and pad
|
||||
// them with zeros on the left to make sure the sizes work out. Both arrays
|
||||
// must be keyBytes long, and the output must be 2*keyBytes long.
|
||||
rBytes := r.Bytes()
|
||||
rBytesPadded := make([]byte, keyBytes)
|
||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||
|
||||
sBytes := s.Bytes()
|
||||
sBytesPadded := make([]byte, keyBytes)
|
||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||
|
||||
out := append(rBytesPadded, sBytesPadded...)
|
||||
|
||||
return EncodeSegment(out), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key")
|
||||
ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key")
|
||||
)
|
||||
|
||||
// Parse PEM encoded Elliptic Curve Private Key Structure
|
||||
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pkey *ecdsa.PrivateKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
|
||||
return nil, ErrNotECPrivateKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 public key
|
||||
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||
parsedKey = cert.PublicKey
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *ecdsa.PublicKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok {
|
||||
return nil, ErrNotECPublicKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue