This is an automated email from the git hooks/post-receive script.

glondu pushed a commit to branch master
in repository sexplib310.

commit 3155175bbde3d42d93d0e91d193b3d2835cbc89b
Author: Stephane Glondu <st...@glondu.net>
Date:   Sat Jul 23 10:40:01 2016 +0200

    Imported Upstream version 113.33.00
---
 CHANGES.md           |  40 +++++
 README.md            | 478 ---------------------------------------------------
 README.org           | 105 +++++++++++
 _oasis               |   2 +-
 src/macro.ml         |  31 +++-
 src/pre_sexp.ml      |  48 +++++-
 test/test_macros.mli |   1 -
 7 files changed, 215 insertions(+), 490 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index fcfa49f..11e81fa 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,43 @@
+## 113.33.00
+
+- Changes `Sexp.to_string` to escape all non-ASCII characters.
+
+  Previously chars >= 127 are escaped or not depending on:
+  1. other character in the string
+  2. the system
+  3. environment variable settings
+
+  (2) and (3) are because `String.escaped` from the stdlib uses the C
+  function `isprint` which is locale and OS dependent.
+
+  This can cause invalid UTF-8 sequence to be printed by sexplib, which
+  is annoying:
+
+    https://github.com/janestreet/sexplib/issues/18
+
+  Starting with this release, sexplib:
+  1. copies the `String.escaped` function of OCaml 4.03 which escapes
+     all non-ascii characters
+  2. make sure we escape the string when it contains characters >= 127
+
+- Clean up the documentation for sexplib, modernizing it to include
+  `ppx_sexp_conv`, and breaking up the documentation between sexplib and
+  `ppx_sexp_conv`.  Also changed the formatting to use org-mode, so it
+  will render properly on github.  Markdown doesn't render well by
+  default, unless you use quite different conventions about linebeaks.
+
+- In sexp macro library, avoid returning success when there is any error
+  reading a sexp. In particular, this prevents
+
+    sexp resolve <(echo '(:use x)')
+
+  from silently succeeding.
+
+  Also, now we no longer read an included file multiple times.
+  This lets even crazy stuff like this to work:
+
+    $ echo 'hi ' | sexp resolve <(echo '((:include /dev/stdin) (:include 
/dev/stdin))')
+
 ## 113.24.00
 
 - Switch code in `lib` subdir to ppx-style.
diff --git a/README.md b/README.md
deleted file mode 100644
index 381f669..0000000
--- a/README.md
+++ /dev/null
@@ -1,478 +0,0 @@
-Sexplib - S-Expressions with Type Converters for OCaml
-======================================================
-
----------------------------------------------------------------------------
-
-What is Sexplib?
-----------------
-
-This [OCaml](http://www.ocaml.org) library contains functionality for
-parsing and pretty-printing S-expressions.  In addition to that it contains a
-preprocessing module for Camlp4 which can be used to automatically generate
-code from type definitions for efficiently converting OCaml-values to
-S-expressions and vice versa.
-
-In combination with the parsing and pretty-printing functionality this
-frees users from having to write their own I/O-routines for data structures
-they define.  The tight integration with the OCaml type system also allows
-for automatically verifying complex semantic properties when converting from
-S-expressions to OCaml values.  Possible errors during automatic conversions
-from S-expressions to OCaml-values are reported in human-readable ways
-with exact location information.  The library also offers functionality for
-extracting and replacing sub-expressions in S-expressions.
-
-Usage
------
-
-Make sure you have installed the required `type_conv` package on your
-system, too.  It should be obtainable at the same site as `sexplib`.
-
-The API (`.mli`-files) in the `sexplib` library directory (`lib`)
-is fully documented, and HTML-documentation can be built from it on
-installation.  The documentation for the latest release can also be found
-[online](http://mmottl.bitbucket.org/projects/sexplib/api/).
-
-Module `Sexp` contains all I/O-functions for S-expressions, module
-`Conv` helper functions for converting OCaml-values of standard types
-to S-expressions.  Module `Path` supports sub-expression extraction and
-substitution.
-
-Module `syntax/pa_sexp_conv.ml` contains the extensions for the
-Camlp4-preprocessor.  It adds the following three new constructs to the
-language:
-
-    :::ocaml
-    with sexp
-    with sexp_of
-    with of_sexp
-
-The first one implies the last two statements.  When using these constructs
-right after a type definition, function definitions will be automatically
-generated which perform S-expression conversions.  For example, consider
-the following type definition:
-
-    :::ocaml
-    type t = A | B with sexp
-
-The above will generate the functions `sexp_of_t` and `t_of_sexp`.
-The preprocessor also supports automatic addition of conversion functions
-to signatures.  Just add "`with sexp`" to the type in a signature, and the
-appropriate function signatures will be generated.
-
-Converters for standard types (`int`, `list`, `Hashtbl.t`, etc.) become
-visible to the macro-generated code by opening the standard module before
-their first use in a type definition.  Users will therefore usually want to
-place the following at the top of their files:
-
-    :::ocaml
-    open Sexplib.Std
-
-See the file `lib_test/conv_test.ml` for an example application.  It also
-demonstrates how to extract and substitute sub-expressions.
-
-### Compiling and linking
-
-To compile a file you will have to add preprocessing flags to the compiler
-invocation.  For example for file `foo.ml`:
-
-    :::sh
-    ocamlc -pp "camlp4o -I {path to type_conv} \
-      -I {path to sexplib} pa_type_conv.cmo pa_sexp_conv.cmo" \
-      -I {path to sexplib} foo.ml
-
-If you are using [OCamlMakefile](http://bitbucket.org/mmottl/ocaml-makefile),
-just put the following line at the top of the file, assuming you have installed
-both `type_conv` and `sexplib` with ocamlfind.  The comment must start at
-the beginning of the line, and you must not break lines (here broken for
-readability only):
-
-    :::ocaml
-    (*pp camlp4o -I `ocamlfind query type_conv` \
-      -I `ocamlfind query sexplib` \
-      pa_type_conv.cmo pa_sexp_conv.cmo *)
-
-In the linking stage you will only have to link with `sexplib`.  E.g. when
-using `OCamlMakefile`, just add `sexplib` to the `PACKS`-variable.
-
-Users of the OCaml tool
-[findlib](http://projects.camlcity.org/projects/findlib.html) for compiling
-and linking OCaml files have an easier time: they just need to add `sexplib`
-to the list of packages to make S-expression functionality available.
-Adding `sexplib.syntax`, too, will make sure that files that use the type
-conversion feature will be preprocessed correctly.
-
-You may choose to place the macro `TYPE_CONV_PATH`, which takes a string
-argument, at the top of files to be preprocessed if you want to force a
-particular module path for error messages generated by `sexplib`.  This may
-become necessary if modules are packed into a library at a later stage and
-if error messages generated by Sexplib need to refer to this location to
-help pinpoint the associated type.
-
-Syntax Specification of S-expressions
--------------------------------------
-
-### Lexical conventions of S-expression
-
-Whitespace, which consists of space, newline, horizontal tab, and form feed,
-is ignored unless within an OCaml-string, where it is treated according
-to OCaml-conventions.  The left parenthesis opens a new list, the right one
-closes it again.  Lists can be empty.  The double quote denotes the beginning
-and end of a string following the lexical conventions of OCaml (see the
-[OCaml-manual](http://www.ocaml.org/pub/docs/manual-ocaml) for details).
-All characters other than double quotes, left- and right parentheses,
-whitespace, carriage return, and comment-introducing characters or sequences
-(see next paragraph) are considered part of a contiguous string.
-
-A line comment is introduced using a semicolon, which comments out all
-text up to the end of the next newline character.  The sequence "`#;`"
-introduces an S-expression comment.  This means that the next S-expression,
-which must be syntactically correct and may be an atom (quoted or unquoted)
-or list, following this two-character sequence will be ignored.  Whitespace
-or other comments between this sequence and the subsequent S-expression
-are ignored.  Block comments are opened with "`#|`" and closed with "`|#`".
-They can be nested and require that double-quotes within the block balance
-and contain syntactically correct OCaml-strings, similar to quoted atoms.
-These OCaml-strings may contain comment characters without causing parsing
-problems.
-
-### Grammar of S-expressions
-
-S-expressions are either strings (= atoms) or lists.  The lists can
-recursively contain further S-expressions or be empty, and must be balanced,
-i.e. parentheses must match.
-
-### Examples
-
-    :::scheme
-    this_is_an_atom_123'&^%!  ; this is a comment
-    "another atom in an OCaml-string \"string in a string\" \123"
-
-    ; empty list follows below
-    ()
-
-    ; a more complex example
-    (
-      (
-        list in a list  ; comment within a list
-        (list in a list in a list)
-        42 is the answer to all questions
-        #; (this S-expression
-             (has been commented out)
-           )
-        #| Block comments #| can be "nested" |# |#
-      )
-    )
-
-### Conversion of basic OCaml-values
-
-Basic OCaml-values like the unit-value, integers (in all representations),
-floats, strings, and booleans are represented in S-expression syntax the
-same way as in OCaml.  Strings may also appear without quotes if this does
-not clash with the lexical conventions for S-expressions.
-
-### Conversion of OCaml-tuples
-
-OCaml-tuples are simple lists of values in the same order as in the tuple.
-E.g. (OCaml representation followed by S-expression after arrow):
-
-    :::ocaml
-    (3.14, "foo", "bar bla", 27)  <===>  (3.14 foo "bar bla" 27)
-
-### Conversion of OCaml-records
-
-OCaml-records are represented as lists of pairs in S-expression syntax.
-Each pair consists of the name of the record field (first element), and its
-value (second element).  E.g.:
-
-    :::ocaml
-    {
-      foo = 3;
-      bar = "some string";
-    }
-    <===>
-    (
-      (foo 3)
-      (bar "some string")
-    )
-
-Type specifications of records allow the use of a special type `sexp_option`
-which indicates that a record field should be optional.  E.g.:
-
-    :::ocaml
-    type t =
-      {
-        x : int option;
-        y : int sexp_option;
-      } with sexp
-
-The type `sexp_option` is equivalent to ordinary options, but is treated
-specially by the code generator.  The above would lead to the following
-equivalences of values and S-expressions:
-
-    :::ocaml
-    {
-      x = Some 1;
-      y = Some 2;
-    }
-    <===>
-    (
-      (x (some 1))
-      (y 2)
-    )
-
-And:
-
-    :::ocaml
-    {
-      x = None;
-      y = None;
-    }
-    <===>
-    (
-      (x none)
-    )
->>
-
-  Note how `sexp_option` allows you to leave away record fields that should
-default to `None`.  It is also unnecessary (and actually wrong) now to write
-down such a value as an option, i.e. the `some`-tag must be dropped if the
-field should be defined.
-
-The types `sexp_list`, `sexp_array`, and `sexp_bool` can be used in ways
-similar to the type `sexp_option`.  They assume the empty list, empty array,
-and false value respectively as default values.
-
-More complex default values can be specified explicitly using several
-constructs, e.g.:
-
-    :::ocaml
-    let z_test v = v > 42
-
-    type t =
-      {
-        x : int with default(42);
-        y : int with default(3), sexp_drop_default;
-        z : int with default(3), sexp_drop_if(z_test);
-      } with sexp
-
-The `default` record field extension above is supported by the underlying
-preprocessor library `type_conv` and specifies the intended default value for
-a record field in its argument.  Sexplib will use this information to generate
-code which will set this record field to the default value if an S-expression
-omits this field.  If a record is converted to an S-expression, record fields
-with default values will be emitted as usual.  Specifying `sexp_drop_default`
-will add a test for polymorphic equality to the generated code such that a
-record field containing its default value will be suppressed in the resulting
-S-expression.  This option requires the presence of a default value.
-
-`sexp_drop_if` on the other hand does not require a default.  Its argument
-must be a function, which will receive the current record value.  If the
-result of this function is `true`, then the record field will be suppressed
-in the resulting S-expression.
-
-The above extensions can be quite creatively used together with manifest
-types, functors, and first-class modules to make the emission of record
-fields or the definition of their default values configurable at runtime.
-
-### Conversion of sum types
-
-Constant constructors in sum types are represented as strings.  Constructors
-with arguments are represented as lists, the first element being the
-constructor name, the rest being its arguments.  Constructors may also
-be started in lowercase in S-expressions, but will always be converted to
-uppercase when converting from OCaml-values.
-
-For example:
-
-    :::ocaml
-    type t = A | B of int * float * t with sexp
-
-    B (42, 3.14, B (-1, 2.72, A))  <===>  (B 42 3.14 (B -1 2.72 A))
-
-The above example also demonstrates recursion in data structures.
-
-### Conversion of variant types
-
-The conversion of polymorphic variants is almost the same as with sum types.
-The notable difference is that variant constructors must always start with
-an either lower- or uppercase character, matching the way it was specified
-in the type definition.  This is because OCaml also distinguishes between
-upper- and lowercase variant constructors.  Note that type specifications
-containing unions of variant types are also supported by the S-expression
-converter, for example as in:
-
-    :::ocaml
-    type ab = [ `A | `B ] with sexp
-    type cd = [ `C | `D ] with sexp
-    type abcd = [ ab | cd ] with sexp
-
-### Conversion of OCaml-lists and arrays
-
-OCaml-lists and arrays are straightforwardly represented as S-expression lists.
-
-### Conversion of option types
-
-The option type is converted like ordinary polymorphic sum types, i.e.:
-
-    :::ocaml
-    None        <===>  none
-    Some value  <===>  (some value)
-
-There is a deprecated version of the syntax in which values of option type
-are represented as lists in S-expressions:
-
-    :::ocaml
-    None        <===>  ()
-    Some value  <===>  (value)
-
-Reading of the old-style S-expression syntax for option values is
-only supported if the reference `Conv.read_old_option_format` is set
-to `true` (currently the default, which may change).  A conversion
-exception is raised otherwise.  The old format will be written only if
-`Conv.write_old_option_format` is true (also currently the default).
-Reading of the new format is always supported.
-
-### Conversion of polymorphic values
-
-There is nothing special about polymorphic values as long as there are
-conversion functions for the type parameters.  E.g.:
-
-    :::ocaml
-    type 'a t = A | B of 'a with sexp
-    type foo = int t with sexp
-
-In the above case the conversion functions will behave as if `foo` had been
-defined as a monomorphic version of `t` with `'a` replaced by `int` on the
-right hand side.
-
-If a data structure is indeed polymorphic and you want to convert it, you will
-have to supply the conversion functions for the type parameters at runtime.
-If you wanted to convert a value of type `'a t` as in the above example,
-you would have to write something like this:
-
-    :::ocaml
-    sexp_of_t sexp_of_a v
-
-where `sexp_of_a`, which may also be named differently in this particular
-case, is a function that converts values of type `'a` to an S-expression.
-Types with more than one parameter require passing conversion functions for
-those parameters in the order of their appearance on the left hand side of
-the type definition.
-
-
-### Conversion of abstract data types
-
-If you want to convert an abstract data type to an S-expression, you will
-have to roll your own conversion functions, which should produce or accept
-values of type `Sexp.t`.  If you want to make use of your abstract type
-within definitions of other types, make sure that you call your conversion
-function appropriately: it should be in the same scope as the typename,
-and must be named `sexp_of_{typename}`.
-
-It is possible to make use of internal representations, too, of course.
-In that case you may need to shadow the generated `*_of_sexp` function with a
-version that calls the generated one, but performs required semantic checks
-on the resulting value to guarantee that it does not violate properties of
-the abstract data type.  For example:
-
-    :::ocaml
-    type pos_int = int with sexp
-
-    let pos_int_of_sexp sexp =
-      let n = pos_int_of_sexp sexp in
-      if n >= 0 then n
-      else raise (Of_sexp_error (Failure "pos_int: number not positive", sexp))
-
-A nice perk of `sexplib` is that using the `Of_sexp_error`-exception
-will allow you to accurately pinpoint type errors in large S-expressions.
-The file loading functions described further below will exploit this feature.
-
-### Conversion of hash tables
-
-Hash tables, which are abstract values in OCaml, are represented as association
-lists, i.e. lists of key-value pairs, e.g.:
-
-    :::scheme
-    ((foo 42) (bar 3))
-
-Reading in the above S-expression as hash table mapping strings to integers
-(`(string, int) Hashtbl.t`) will map `foo` to `42` and `bar` to `3`.
-
-Note that the order of elements in the list may matter, because the
-OCaml-implementation of hash tables keeps duplicates.  Bindings will be
-inserted into the hash table in the order of appearance.  Therefore, the
-last binding of a key will be the "visible" one, the others are "hidden".
-See the OCaml-documentation on hash tables for details.
-
-Note, too, that polymorphic equality may not hold between conversions.
-You will have to use a function implementing logical equality for that purpose.
-
-### Conversion of opaque values
-
-Opaque values are ones for which we do not want to perform conversions.
-This may be, because we do not have S-expression converters for them, or
-because we do not want to apply them in a particular type context. e.g. to
-hide large, unimportant parts of configurations.  To prevent the preprocessor
-from generating calls to converters, simply apply the qualifier `sexp_opaque`
-as if it were a type constructor, e.g.:
-
-    :::ocaml
-    type foo = int * stuff sexp_opaque with sexp
-
-Thus, there is no need to specify converters for type `stuff`, and if there
-are any, they will not be used in this particular context.  Needless to say,
-it is not possible to convert such an S-expression back to the original value.
-Here is an example conversion:
-
-    :::ocaml
-    (42, some_stuff)  ===>  (42 <opaque>)
-
-### Conversion of exceptions
-
-S-expression converters for exceptions can be automatically registered using
-the "`with sexp`" macro, e.g.:
-
-    :::ocaml
-    module M = struct
-      exception Foo of int with sexp
-    end
-
-Such exceptions will be translated in a similar way as sum types, but their
-constructor will be prefixed with the fully qualified module path (here:
-`M.Foo`) so as to be able to discriminate between them without problems.
-
-The user can then easily convert an exception matching the above one to
-an S-expression using `sexp_of_exn`.  User-defined conversion functions
-can be registered, too, by calling `add_exn_converter`.  This should make
-it very convenient for users to catch arbitrary exceptions escaping their
-program and pretty-printing them, including all arguments, as S-expressions.
-The library already contains mappings for all known exceptions that can
-escape functions in the OCaml standard library.
-
-I/O and Type Conversions
-------------------------
-
-There are multiple ways of performing I/O with S-expressions.  If exact
-error locations are required when type conversions fail, S-expressions need
-to be parsed with location annotations.  The associated parser is slower,
-however, and needs more memory.  In most cases users may therefore want to
-use functions like `load_sexp_conv` or `load_sexp_conv_exn`, which load
-S-expressions from files and convert them.  They initially read the file
-without location annotations for performance reasons.  Only if conversions
-fail, the file will be reparsed with location annotations.  Type errors
-can then be reported accurately with file name, line number, column, and
-file position.
-
----------------------------------------------------------------------------
-
-Contact Information and Contributing
-------------------------------------
-
-In the case of bugs, feature requests, contributions and similar, please
-contact the maintainers:
-
-  * Jane Street Capital, LLC <opensou...@janestreet.com>
-
-Up-to-date information should be available at:
-* <https://github.com/janestreet/sexplib>
-* <https://bitbucket.org/janestreet/sexplib>
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..a1ca2a3
--- /dev/null
+++ b/README.org
@@ -0,0 +1,105 @@
+* Sexplib - S-Expressions for OCaml
+
+=sexplib= contains functionality for parsing and pretty-printing
+s-expressions.  S-expressions are defined by the following type:
+
+#+begin_src ocaml
+type sexp = Atom of string | List of sexp list
+#+end_src
+
+and are rendered as parenthesized lists of strings, /e.g./ =(This (is
+an) (s expression))=.
+
+This library is often used in conjunction with =ppx_sexp_conv=, a
+syntax extension which generates code from type definitions for
+efficiently converting OCaml-values to s-expressions and vice versa.
+
+Together, these two libraries make it easy and convenient to convert
+your OCaml values to and from a human-readable serializable form,
+without the tedium of having to write your own converters.
+
+The library also offers functionality for extracting and replacing
+sub-expressions in s-expressions.  Here, we'll only document =sexplib=
+proper.  If you want to know more about the way in which OCaml types
+are mapped on to s-expressions, you should look at the documentation
+for =ppx_sexp_conv=.
+
+** Lexical conventions of s-expression
+
+Whitespace, which consists of space, newline, horizontal tab, and form
+feed, is ignored unless within an OCaml-string, where it is treated
+according to OCaml-conventions.  The left parenthesis opens a new
+list, the right one closes it again.  Lists can be empty.  The double
+quote denotes the beginning and end of a string following the lexical
+conventions of OCaml (see the 
[[http://www.ocaml.org/pub/docs/manual-ocaml][OCaml-manual]] for details). All
+characters other than double quotes, left- and right parentheses,
+whitespace, carriage return, and comment-introducing characters or
+sequences (see next paragraph) are considered part of a contiguous
+string.
+
+** Comments
+
+There are three kinds of comments:
+
+- _line comments_ are introduced with =;=, and end at the newline.
+- _sexp comments_ are introduced with =#;=, and end at the end of the
+  following s-expression
+- _block comments_ are introduced with =#|= and end with =|#=.  These
+  can be nested, and double-quotes within them must be balanced and be
+  lexically correct OCaml strings.
+
+** Grammar of s-expressions
+
+s-expressions are either strings (= atoms) or lists.  The lists can
+recursively contain further s-expressions or be empty, and must be
+balanced, /i.e./ parentheses must match.
+
+** Examples
+
+#+begin_src scheme
+  this_is_an_atom_123'&^%!  ; this is a comment
+  "another atom in an OCaml-string \"string in a string\" \123"
+  
+  ; empty list follows below
+  ()
+  
+  ; a more complex example
+  (
+    (
+      list in a list  ; comment within a list
+      (list in a list in a list)
+      42 is the answer to all questions
+      #; (this S-expression
+           (has been commented out)
+         )
+      #| Block comments #| can be "nested" |# |#
+    )
+  )
+#+end_src
+
+** I/O and Type Conversions
+
+There are multiple ways of performing I/O with s-expressions.  If
+exact error locations are required when type conversions fail,
+s-expressions need to be parsed with location annotations.  The
+associated parser is slower, however, and needs more memory.  In most
+cases users may therefore want to use functions like =load_sexp_conv=
+or =load_sexp_conv_exn=, which load s-expressions from files and
+convert them.  They initially read the file without location
+annotations for performance reasons.  Only if conversions fail, the
+file will be reparsed with location annotations.  Type errors can then
+be reported accurately with file name, line number, column, and file
+position.
+
+** Custom converters
+
+In addition to the converters provided automatically by
+=ppx_sexp_conv=, it's possible to write one's own sexp-converter.  For
+such converters to be available by other automatically generated
+converters, it should follow the convention of being defined in the
+same scope as the type, and should be named =sexp_of_[type]= and
+=[type_of_sexp]=.
+
+You must report failures by raising the =Of_sexp_error=-exception so
+that then =sexplib='s tools for pinpointing the location of type
+errors within an s-expression file will work properly.
diff --git a/_oasis b/_oasis
index 7024a6c..683ca82 100644
--- a/_oasis
+++ b/_oasis
@@ -2,7 +2,7 @@ OASISFormat:      0.4
 OCamlVersion:     >= 4.02.3
 FindlibVersion:   >= 1.3.2
 Name:             sexplib
-Version:          113.24.00
+Version:          113.33.00
 Synopsis:         Library for serializing OCaml values to and from 
S-expressions
 Authors:          Jane Street Group, LLC <opensou...@janestreet.com>
 Copyrights:       (C) 2005-2016 Jane Street Group LLC 
<opensou...@janestreet.com>
diff --git a/src/macro.ml b/src/macro.ml
index 7058730..e7dcaee 100644
--- a/src/macro.ml
+++ b/src/macro.ml
@@ -59,6 +59,8 @@ module List = struct
       | Some x -> Some x
       | None -> find_map ~f xs
 
+  let exists ~f xs = List.exists f xs
+
   let rec find_a_dup = function
     | [] -> None
     | x :: xs ->
@@ -254,10 +256,14 @@ module Loader (S : Sexp_loader) = struct
     let rec load visited file =
       if List.mem file visited
       then raise (Include_loop_detected file);
-      S.load_sexps file
-      >>= fun ts ->
-      file_contents := (file, ts) :: !file_contents;
-      M.List.iter ts ~f:(load_includes (file :: visited) file)
+      if List.mem file (List.map ~f:fst !file_contents)
+      then M.return ()
+      else begin
+        S.load_sexps file
+        >>= fun ts ->
+        file_contents := (file, ts) :: !file_contents;
+        M.List.iter ts ~f:(load_includes (file :: visited) file)
+      end
     and load_includes visited file = function
       | Sexp.List [Sexp.Atom ":include"; Sexp.Atom include_file] ->
         let include_file = make_absolute_path ~with_respect_to:file 
include_file in
@@ -365,12 +371,23 @@ module Loader (S : Sexp_loader) = struct
     load_all_includes file
     >>= fun file_contents ->
     try M.return (expand_and_convert ~multiple (`Fast file_contents) file f)
-    with Of_sexp_error _ ->
+    with Of_sexp_error _ as original_exn ->
       begin
         load_all_annotated_includes file_contents
         >>= fun annotated_file_contents ->
-        M.return (expand_and_convert
-                    ~multiple (`Find_error annotated_file_contents) file f);
+        let result =
+          (expand_and_convert
+             ~multiple (`Find_error annotated_file_contents) file f)
+        in
+        if List.exists result ~f:(function
+          | `Result _ -> false
+          | `Error _ -> true)
+        then
+          M.return result
+        else
+          (* Avoid returning success in the case there was an error.
+             This can be bad e.g. when reading the input from a pipe. *)
+          raise original_exn
       end
 
   let load_sexps_conv file f = load ~multiple:true file f
diff --git a/src/pre_sexp.ml b/src/pre_sexp.ml
index d8751fe..4ee6dd6 100644
--- a/src/pre_sexp.ml
+++ b/src/pre_sexp.ml
@@ -27,12 +27,54 @@ let must_escape str =
       | '"' | '(' | ')' | ';' | '\\' -> true
       | '|' -> ix > 0 && let next = ix - 1 in str.[next] = '#' || loop next
       | '#' -> ix > 0 && let next = ix - 1 in str.[next] = '|' || loop next
-      | c -> c <= ' ' || ix > 0 && loop (ix - 1)
+      | '\000' .. '\032' | '\127' .. '\255' -> true
+      | _ -> ix > 0 && loop (ix - 1)
     in
     loop (len - 1)
 
+let escaped s =
+  let open Bytes in
+  let n = ref 0 in
+  for i = 0 to length s - 1 do
+    n := !n +
+      (match unsafe_get s i with
+       | '\"' | '\\' | '\n' | '\t' | '\r' | '\b' -> 2
+       | ' ' .. '~' -> 1
+       | _ -> 4)
+  done;
+  if !n = length s then copy s else begin
+    let s' = create !n in
+    n := 0;
+    for i = 0 to length s - 1 do
+      begin match unsafe_get s i with
+      | ('\"' | '\\') as c ->
+          unsafe_set s' !n '\\'; incr n; unsafe_set s' !n c
+      | '\n' ->
+          unsafe_set s' !n '\\'; incr n; unsafe_set s' !n 'n'
+      | '\t' ->
+          unsafe_set s' !n '\\'; incr n; unsafe_set s' !n 't'
+      | '\r' ->
+          unsafe_set s' !n '\\'; incr n; unsafe_set s' !n 'r'
+      | '\b' ->
+          unsafe_set s' !n '\\'; incr n; unsafe_set s' !n 'b'
+      | (' ' .. '~') as c -> unsafe_set s' !n c
+      | c ->
+          let a = Char.code c in
+          unsafe_set s' !n '\\';
+          incr n;
+          unsafe_set s' !n (Char.chr (48 + a / 100));
+          incr n;
+          unsafe_set s' !n (Char.chr (48 + (a / 10) mod 10));
+          incr n;
+          unsafe_set s' !n (Char.chr (48 + a mod 10));
+      end;
+      incr n
+    done;
+    s'
+  end
+
 let esc_str str =
-  let estr = String.escaped str in
+  let estr = escaped str in
   let elen = String.length estr in
   let res = String.create (elen + 2) in
   String.blit estr 0 res 1 elen;
@@ -66,7 +108,7 @@ let pp_hum_maybe_esc_str ppf str =
     let rec loop index =
       let next_newline = index_of_newline str index in
       let next_line = get_substring str index next_newline in
-      pp_print_string ppf (String.escaped next_line);
+      pp_print_string ppf (escaped next_line);
       match next_newline with
       | None ->  ()
       | Some newline_index ->
diff --git a/test/test_macros.mli b/test/test_macros.mli
index 7a68989..31d3b71 100644
--- a/test/test_macros.mli
+++ b/test/test_macros.mli
@@ -1,5 +1,4 @@
 open Sexplib
-open Sexplib.Conv
 
 module type Load = sig
   val load_sexp_conv_exn : string -> (Sexp.t -> 'a) -> 'a

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-ocaml-maint/packages/sexplib310.git

_______________________________________________
Pkg-ocaml-maint-commits mailing list
Pkg-ocaml-maint-commits@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-ocaml-maint-commits

Reply via email to