on master help sign in
Merge branch '90-safe-intl-filename' of https://github.com/Blaisorblade/yaml
7112a07Michael Snoyman
ChangeLog.md+4
@@ -1,3 +1,7 @@
1+## 0.8.18.2
2+
3+* Handle non-ASCII filenames correctly on Windows [#91](https://github.com/snoyberg/yaml/pull/91)
4+
1## 0.8.18.1
5## 0.8.18.1
2
6
3* Improve prettyPrintParseException when context is empty [#89](https://github.com/snoyberg/yaml/pull/89)
7* Improve prettyPrintParseException when context is empty [#89](https://github.com/snoyberg/yaml/pull/89)
Text/Libyaml.hs+26−12
@@ -29,6 +29,7 @@ module Text.Libyaml
29
29
30import Prelude hiding (pi)
30import Prelude hiding (pi)
31
31
32+import Data.Bits ((.|.))
32import Foreign.C
33import Foreign.C
33import Foreign.Ptr
34import Foreign.Ptr
34import Foreign.ForeignPtr
35import Foreign.ForeignPtr
@@ -36,6 +37,7 @@ import Foreign.ForeignPtr
36import Foreign.ForeignPtr.Unsafe
37import Foreign.ForeignPtr.Unsafe
37#endif
38#endif
38import Foreign.Marshal.Alloc
39import Foreign.Marshal.Alloc
40+import qualified System.Posix.Internals as Posix
39
41
40#if !MIN_VERSION_base(4,8,0)
42#if !MIN_VERSION_base(4,8,0)
41import Control.Applicative
43import Control.Applicative
@@ -147,11 +149,10 @@ foreign import ccall unsafe "yaml_parser_set_input_file"
147data FileStruct
149data FileStruct
148type File = Ptr FileStruct
150type File = Ptr FileStruct
149
151
150−foreign import ccall unsafe "fopen"
152+foreign import ccall unsafe "fdopen"
151− c_fopen :: Ptr CChar
153+ c_fdopen :: CInt
152− -> Ptr CChar
154+ -> Ptr CChar
153− -> IO File
155+ -> IO File
154−
155foreign import ccall unsafe "fclose"
156foreign import ccall unsafe "fclose"
156 c_fclose :: File
157 c_fclose :: File
157 -> IO ()
158 -> IO ()
@@ -503,6 +504,24 @@ decode bs =
503 c_yaml_parser_delete ptr
504 c_yaml_parser_delete ptr
504 free ptr
505 free ptr
505
506
507+-- XXX copied from GHC.IO.FD
508+std_flags, read_flags, output_flags, write_flags :: CInt
509+std_flags = Posix.o_NOCTTY
510+output_flags = std_flags .|. Posix.o_CREAT
511+read_flags = std_flags .|. Posix.o_RDONLY
512+write_flags = output_flags .|. Posix.o_WRONLY
513+
514+-- | Open a C FILE* from a file path, using internal GHC API to work correctly
515+-- on all platforms, even on non-ASCII filenames. The opening mode must be
516+-- indicated via both 'rawOpenFlags' and 'openMode'.
517+openFile :: FilePath -> CInt -> String -> IO File
518+openFile file rawOpenFlags openMode = do
519+ fd <- liftIO $ Posix.withFilePath file $ \file' ->
520+ Posix.c_open file' rawOpenFlags 0o666
521+ if fd /= (-1)
522+ then withCString openMode $ \openMode' -> c_fdopen fd openMode'
523+ else return nullPtr
524+
506decodeFile :: MonadResource m => FilePath
525decodeFile :: MonadResource m => FilePath
507#if MIN_VERSION_conduit(1, 0, 0)
526#if MIN_VERSION_conduit(1, 0, 0)
508 -> Producer m Event
527 -> Producer m Event
@@ -521,12 +540,9 @@ decodeFile file =
521 free ptr
540 free ptr
522 throwIO $ YamlException "Yaml out of memory"
541 throwIO $ YamlException "Yaml out of memory"
523 else do
542 else do
524− file' <- liftIO
543+ file' <- openFile file read_flags "r"
525− $ withCString file $ \file' -> withCString "r" $ \r' ->
526− c_fopen file' r'
527 if file' == nullPtr
544 if file' == nullPtr
528 then do
545 then do
529− c_fclose_helper file'
530 c_yaml_parser_delete ptr
546 c_yaml_parser_delete ptr
531 free ptr
547 free ptr
532 throwIO $ YamlException
548 throwIO $ YamlException
@@ -599,9 +615,7 @@ encodeFile filePath =
599 bracketP getFile c_fclose $ \file -> runEmitter (alloc file) (\u _ -> return u)
615 bracketP getFile c_fclose $ \file -> runEmitter (alloc file) (\u _ -> return u)
600 where
616 where
601 getFile = do
617 getFile = do
602− file <- withCString filePath $
618+ file <- openFile filePath write_flags "w"
603− \filePath' -> withCString "w" $
604− \w' -> c_fopen filePath' w'
605 if file == nullPtr
619 if file == nullPtr
606 then throwIO $ YamlException $ "could not open file for write: " ++ filePath
620 then throwIO $ YamlException $ "could not open file for write: " ++ filePath
607 else return file
621 else return file
examples/Config.hs+1
@@ -9,6 +9,7 @@ import Data.Yaml (FromJSON(..), (.:))
9import Text.RawString.QQ
9import Text.RawString.QQ
10import Data.ByteString (ByteString)
10import Data.ByteString (ByteString)
11import Control.Applicative
11import Control.Applicative
12+import Prelude -- Ensure Applicative is in scope and we have no warnings, before/after AMP.
12
13
13configYaml :: ByteString
14configYaml :: ByteString
14configYaml = [r|
15configYaml = [r|
test/Data/YamlSpec.hs+13
@@ -19,6 +19,7 @@ import Control.Monad
19import Control.Exception (try, SomeException)
19import Control.Exception (try, SomeException)
20import Test.Hspec
20import Test.Hspec
21import Data.Either.Compat
21import Data.Either.Compat
22+import System.Directory (createDirectory)
22import Test.Mockery.Directory
23import Test.Mockery.Directory
23
24
24import qualified Data.Yaml as D
25import qualified Data.Yaml as D
@@ -63,6 +64,7 @@ spec = do
63 describe "Data.Yaml" $ do
64 describe "Data.Yaml" $ do
64 it "encode/decode" caseEncodeDecodeData
65 it "encode/decode" caseEncodeDecodeData
65 it "encode/decode file" caseEncodeDecodeFileData
66 it "encode/decode file" caseEncodeDecodeFileData
67+ it "encode/decode files with non-ASCII names" caseEncodeDecodeNonAsciiFileData
66 it "encode/decode strings" caseEncodeDecodeStrings
68 it "encode/decode strings" caseEncodeDecodeStrings
67 it "decode invalid file" caseDecodeInvalid
69 it "decode invalid file" caseDecodeInvalid
68 it "processes datatypes" caseDataTypes
70 it "processes datatypes" caseDataTypes
@@ -317,6 +319,17 @@ caseEncodeDecodeFileData = withFile "" $ \fp -> do
317 out <- D.decodeFile fp
319 out <- D.decodeFile fp
318 out @?= Just sample
320 out @?= Just sample
319
321
322+caseEncodeDecodeNonAsciiFileData :: Assertion
323+caseEncodeDecodeNonAsciiFileData = do
324+ let mySample = (object ["foo" .= True])
325+ inTempDirectory $ do
326+ createDirectory "accenté"
327+ D.encodeFile "accenté/bar.yaml" mySample
328+ out1 <- D.decodeFile "accenté/bar.yaml"
329+ out1 @?= Just mySample
330+ out2 <- D.decodeFile "test/resources/accenté/foo.yaml"
331+ out2 @?= Just mySample
332+
320caseEncodeDecodeStrings :: Assertion
333caseEncodeDecodeStrings :: Assertion
321caseEncodeDecodeStrings = do
334caseEncodeDecodeStrings = do
322 let out = D.decode $ D.encode sample
335 let out = D.decode $ D.encode sample
test/resources/accenté/foo.yamlnew file+1
@@ -0,0 +1 @@
1+foo: true
yaml.cabal+3−1
@@ -1,5 +1,5 @@
1name: yaml
1name: yaml
2−version: 0.8.18.1
2+version: 0.8.18.2
3license: BSD3
3license: BSD3
4license-file: LICENSE
4license-file: LICENSE
5author: Michael Snoyman <michael@snoyman.com>, Anton Ageev <antage@gmail.com>,Kirill Simonov
5author: Michael Snoyman <michael@snoyman.com>, Anton Ageev <antage@gmail.com>,Kirill Simonov
@@ -20,6 +20,7 @@ extra-source-files: c/helper.h
20 test/resources/foo.yaml
20 test/resources/foo.yaml
21 test/resources/bar.yaml
21 test/resources/bar.yaml
22 test/resources/baz.yaml
22 test/resources/baz.yaml
23+ test/resources/accenté/foo.yaml
23 test/resources/loop/foo.yaml
24 test/resources/loop/foo.yaml
24 test/resources/loop/bar.yaml
25 test/resources/loop/bar.yaml
25 README.md
26 README.md
@@ -130,6 +131,7 @@ test-suite spec
130 , text
131 , text
131 , aeson >= 0.7
132 , aeson >= 0.7
132 , unordered-containers
133 , unordered-containers
134+ , directory
133 , vector
135 , vector
134 , resourcet
136 , resourcet
135 , aeson-qq
137 , aeson-qq