+32
-25
src/bin/main.ml
+32
-25
src/bin/main.ml
···
1
1
(* A shell... one day *)
2
2
3
-
module Shell = Merry.Eval.Make (Merry_posix.State) (Merry_posix.Exec)
3
+
module C = Merry.Eval.Make (Merry_posix.State) (Merry_posix.Exec)
4
+
module I = Merry.Interactive.Make (Merry_posix.State) (Merry_posix.Exec)
4
5
5
6
let sh ~command ~dump ~file env =
6
-
let ast =
7
-
match (file, command) with
8
-
| None, None ->
9
-
Fmt.pr "Expected either a file path or a command to run...";
10
-
exit 1
11
-
| Some file, None -> Merry.of_file Eio.Path.(env#fs / file)
12
-
| _, Some c -> Merry.of_string c
7
+
let executor = Merry_posix.Exec.{ mgr = env#process_mgr } in
8
+
let ctx =
9
+
C.
10
+
{
11
+
state = Merry_posix.State.make ~home:(Sys.getenv "HOME") ();
12
+
executor;
13
+
fs = env#fs;
14
+
options = Merry.Eval.Options.default;
15
+
}
13
16
in
14
-
if dump then Merry.Ast.Dump.pp Fmt.stdout ast
15
-
else
16
-
let _ctx, _ast =
17
-
let executor = Merry_posix.Exec.{ mgr = env#process_mgr } in
18
-
Shell.run
19
-
Shell.
20
-
{
21
-
state = Merry_posix.State.make ~home:(Sys.getenv "HOME") ();
22
-
executor;
23
-
fs = env#fs;
24
-
options = Merry.Eval.Options.default;
25
-
}
26
-
ast
27
-
in
28
-
()
17
+
match (file, command) with
18
+
| None, None -> I.run ctx
19
+
| _ ->
20
+
let ast =
21
+
match (file, command) with
22
+
| None, None -> assert false
23
+
| Some file, None -> Merry.Ast.of_file Eio.Path.(env#fs / file)
24
+
| _, Some c -> Merry.Ast.of_string c
25
+
in
26
+
if dump then Merry.Ast.Dump.pp Fmt.stdout ast
27
+
else
28
+
let _ctx, _ast = C.run ctx ast in
29
+
()
29
30
30
31
open Cmdliner
31
32
open Cmdliner.Term.Syntax
···
51
52
[
52
53
`S Manpage.s_description;
53
54
`P
54
-
"$(cmd) is first a foremost a shell written completely in the OCaml \
55
-
programming language";
55
+
"$(cmd) is first and foremost a shell written in the OCaml programming \
56
+
language. Like most shells, you can execute some series of commands \
57
+
either by specify a shell script file to run, by passing commands \
58
+
using the -c flag or by omitting both of these options and entering \
59
+
interactive mode.";
60
+
`P
61
+
"$(cmd) exists thanks to the Morbig static parser for the POSIX shell \
62
+
syntax.";
56
63
`S Manpage.s_bugs;
57
64
`P "Report bugs at https://tangled.org/patrick.sirref.org/merry/issues.";
58
65
]
+6
src/lib/ast.ml
+6
src/lib/ast.ml
···
732
732
let yjs = complete_commands_to_yojson v in
733
733
Yojson.Safe.pretty_print ppf yjs
734
734
end
735
+
736
+
let of_string s = Morbig.parse_string "-" s |> of_program
737
+
738
+
let of_file path =
739
+
let fname = Eio.Path.native_exn path in
740
+
Eio.Path.load path |> Morbig.parse_string fname |> of_program
+6
src/lib/ast.mli
+6
src/lib/ast.mli
···
9
9
val of_program : Morbig.CST.program -> t
10
10
(** An AST from a Morbig program *)
11
11
12
+
val of_string : string -> t
13
+
(** Construct an AST from a string *)
14
+
15
+
val of_file : _ Eio.Path.t -> t
16
+
(** Construct an AST from a file path. *)
17
+
12
18
(** {2 Utilities} *)
13
19
14
20
(* class map : Ppxlib_traverse_builtins.map *)
+1
-1
src/lib/dune
+1
-1
src/lib/dune
+17
src/lib/interactive.ml
+17
src/lib/interactive.ml
···
1
+
module Make (S : Types.State) (E : Types.Exec) = struct
2
+
module Eval = Eval.Make (S) (E)
3
+
4
+
let default_prompt _ = Fmt.str "%s >\t%!" (Unix.getcwd ())
5
+
6
+
let run ?(prompt = default_prompt) initial_ctx =
7
+
let rec loop (ctx : Eval.ctx) =
8
+
let p = prompt ctx.state in
9
+
match LNoise.linenoise p with
10
+
| None -> loop ctx
11
+
| Some c ->
12
+
let ast = Ast.of_string c in
13
+
let ctx', _ast = Eval.run ctx ast in
14
+
loop ctx'
15
+
in
16
+
loop initial_ctx
17
+
end
+1
-6
src/lib/merry.ml
+1
-6
src/lib/merry.ml
···
1
1
module Ast = Ast
2
2
module Types = Types
3
3
module Eval = Eval
4
-
5
-
let of_string s = Morbig.parse_string "-" s |> Ast.of_program
6
-
7
-
let of_file path =
8
-
let fname = Eio.Path.native_exn path in
9
-
Eio.Path.load path |> Morbig.parse_string fname |> Ast.of_program
4
+
module Interactive = Interactive
10
5
11
6
module Variable = struct
12
7
type t
+1
-7
src/lib/merry.mli
+1
-7
src/lib/merry.mli
···
1
1
module Ast = Ast
2
2
module Types = Types
3
-
4
-
val of_string : string -> Ast.t
5
-
(** Construct an AST from a string *)
6
-
7
-
val of_file : _ Eio.Path.t -> Ast.t
8
-
(** Construct an AST from a file path. *)
9
-
10
3
module Eval = Eval
4
+
module Interactive = Interactive
11
5
12
6
module Variable : sig
13
7
type t
+1
vendor/ocaml-linenoise/.github/CODEOWNERS
+1
vendor/ocaml-linenoise/.github/CODEOWNERS
···
1
+
* @c-cube
+49
vendor/ocaml-linenoise/.github/workflows/main.yml
+49
vendor/ocaml-linenoise/.github/workflows/main.yml
···
1
+
name: build
2
+
on:
3
+
push:
4
+
branches:
5
+
- main
6
+
pull_request:
7
+
jobs:
8
+
run:
9
+
name: build
10
+
strategy:
11
+
fail-fast: false
12
+
matrix:
13
+
os:
14
+
- ubuntu-latest
15
+
- macos-latest
16
+
- macos-13
17
+
setup-version:
18
+
- v2
19
+
- v3
20
+
ocaml-compiler:
21
+
- 4.06.x
22
+
- 4.14.x
23
+
- 5.1.x
24
+
exclude:
25
+
- os: ubuntu-latest
26
+
setup-version: v2
27
+
- os: macos-13
28
+
setup-version: v3
29
+
- os: macos-latest
30
+
setup-version: v2
31
+
- os: macos-latest
32
+
ocaml-compiler: 4.06.x
33
+
runs-on: ${{ matrix.os }}
34
+
steps:
35
+
- uses: actions/checkout@v4
36
+
- uses: ocaml/setup-ocaml@v2
37
+
if: matrix.setup-version == 'v2'
38
+
with:
39
+
ocaml-compiler: ${{ matrix.ocaml-compiler }}
40
+
allow-prerelease-opam: true
41
+
- uses: ocaml/setup-ocaml@v3
42
+
if: matrix.setup-version == 'v3'
43
+
with:
44
+
ocaml-compiler: ${{ matrix.ocaml-compiler }}
45
+
allow-prerelease-opam: true
46
+
- run: opam pin -n .
47
+
- run: opam install -t . --deps-only
48
+
- run: opam exec -- dune build --ignore-promoted-rules
49
+
- run: opam exec -- dune runtest --ignore-promoted-rules
+15
vendor/ocaml-linenoise/.gitignore
+15
vendor/ocaml-linenoise/.gitignore
+10
vendor/ocaml-linenoise/CHANGES.md
+10
vendor/ocaml-linenoise/CHANGES.md
+19
vendor/ocaml-linenoise/Makefile
+19
vendor/ocaml-linenoise/Makefile
+70
vendor/ocaml-linenoise/README.md
+70
vendor/ocaml-linenoise/README.md
···
1
+
Linenoise in OCaml
2
+
--------------------
3
+
4
+
[](https://github.com/ocaml-community/ocaml-linenoise/actions/workflows/main.yml)
5
+
6
+
# Benefits
7
+
1. BSD licensed.
8
+
2. No system dependencies, no need for `readline` on your machine.
9
+
3. Related to 2, these bindings are self-contained, the source for
10
+
`linenoise` is in this repo and compiled all together with the
11
+
`OCaml`.
12
+
4. Written in OCaml + C.
13
+
5. Pretty cool hints feature, see the gif.
14
+
6. Additional features compared to linenoise, such as history search
15
+
16
+
# Installation
17
+
18
+
It is easy with `opam`
19
+
20
+
```shell
21
+
$ opam install linenoise
22
+
```
23
+
24
+
See the pretty
25
+
documentation [here](https://ocaml-community.github.io/ocaml-linenoise/)
26
+
27
+
# Example code
28
+
This example is also included in the repo under examples:
29
+
30
+
<p align="center" style='min-width:100%'>
31
+
<img style='min-width:100%' src='example.gif'/>
32
+
</p>
33
+
34
+
35
+
```ocaml
36
+
let rec user_input prompt cb =
37
+
match LNoise.linenoise prompt with
38
+
| None -> ()
39
+
| Some v ->
40
+
cb v;
41
+
user_input prompt cb
42
+
43
+
let () =
44
+
(* LNoise.set_multiline true; *)
45
+
LNoise.set_hints_callback (fun line ->
46
+
if line <> "git remote add " then None
47
+
else Some (" <this is the remote name> <this is the remote URL>",
48
+
LNoise.Yellow,
49
+
true)
50
+
);
51
+
LNoise.history_load ~filename:"history.txt" |> ignore;
52
+
LNoise.history_set ~max_length:100 |> ignore;
53
+
LNoise.set_completion_callback begin fun line_so_far ln_completions ->
54
+
if line_so_far <> "" && line_so_far.[0] = 'h' then
55
+
["Hey"; "Howard"; "Hughes";"Hocus"]
56
+
|> List.iter (LNoise.add_completion ln_completions);
57
+
end;
58
+
["These are OCaml bindings to linenoise";
59
+
"get tab completion with <TAB>, type h then hit <TAB>";
60
+
"type quit to exit gracefully";
61
+
"By Edgar Aroutiounian\n"]
62
+
|> List.iter print_endline;
63
+
(fun from_user ->
64
+
if from_user = "quit" then exit 0;
65
+
LNoise.history_add from_user |> ignore;
66
+
LNoise.history_save ~filename:"history.txt" |> ignore;
67
+
Printf.sprintf "Got: %s" from_user |> print_endline
68
+
)
69
+
|> user_input "test_program> "
70
+
```
vendor/ocaml-linenoise/example.gif
vendor/ocaml-linenoise/example.gif
This is a binary file and will not be displayed.
+4
vendor/ocaml-linenoise/examples/dune
+4
vendor/ocaml-linenoise/examples/dune
+34
vendor/ocaml-linenoise/examples/show_off.ml
+34
vendor/ocaml-linenoise/examples/show_off.ml
···
1
+
let rec user_input prompt cb =
2
+
match LNoise.linenoise prompt with
3
+
| None -> ()
4
+
| Some v ->
5
+
cb v;
6
+
user_input prompt cb
7
+
8
+
let () =
9
+
(* LNoise.set_multiline true; *)
10
+
LNoise.set_hints_callback (fun line ->
11
+
if line <> "git remote add " then None
12
+
else Some (" <this is the remote name> <this is the remote URL>",
13
+
LNoise.Yellow,
14
+
true)
15
+
);
16
+
LNoise.history_load ~filename:"history.txt" |> ignore;
17
+
LNoise.history_set ~max_length:100 |> ignore;
18
+
LNoise.set_completion_callback begin fun line_so_far ln_completions ->
19
+
if line_so_far <> "" && line_so_far.[0] = 'h' then
20
+
["Hey"; "Howard"; "Hughes";"Hocus"]
21
+
|> List.iter (LNoise.add_completion ln_completions);
22
+
end;
23
+
["These are OCaml bindings to linenoise";
24
+
"get tab completion with <TAB>, type h then hit <TAB>";
25
+
"type quit to exit gracefully";
26
+
"By Edgar Aroutiounian\n"]
27
+
|> List.iter print_endline;
28
+
(fun from_user ->
29
+
if from_user = "quit" then exit 0;
30
+
LNoise.history_add from_user |> ignore;
31
+
LNoise.history_save ~filename:"history.txt" |> ignore;
32
+
Printf.sprintf "Got: %s" from_user |> print_endline
33
+
)
34
+
|> user_input "test_program> "
+20
vendor/ocaml-linenoise/linenoise.opam
+20
vendor/ocaml-linenoise/linenoise.opam
···
1
+
opam-version: "2.0"
2
+
name: "linenoise"
3
+
version: "1.5.1"
4
+
synopsis: "Lightweight readline alternative"
5
+
maintainer: "Simon Cruanes"
6
+
authors: [ "Edgar Aroutiounian <edgar.factorial@gmail.com>" "Simon Cruanes" ]
7
+
license: "BSD-3-clause"
8
+
homepage: "https://github.com/ocaml-community/ocaml-linenoise"
9
+
dev-repo: "git+https://github.com/ocaml-community/ocaml-linenoise.git"
10
+
bug-reports: "https://github.com/ocaml-community/ocaml-linenoise/issues"
11
+
build: [
12
+
["dune" "build" "@install" "-p" name "-j" jobs]
13
+
["dune" "runtest" "-p" name] {with-test}
14
+
["dune" "build" "@doc" "-p" name] {with-doc}
15
+
]
16
+
depends: [
17
+
"dune" { >= "1.1" }
18
+
"ocaml" { >= "4.06.0" }
19
+
"odoc" {with-doc}
20
+
]
+8
vendor/ocaml-linenoise/src/dune
+8
vendor/ocaml-linenoise/src/dune
+57
vendor/ocaml-linenoise/src/lNoise.ml
+57
vendor/ocaml-linenoise/src/lNoise.ml
···
1
+
2
+
type completions
3
+
4
+
external add_completion : completions -> string -> unit = "ml_add_completion"
5
+
6
+
external linenoise : string -> string option = "ml_linenoise"
7
+
8
+
external history_add_ : string -> int = "ml_history_add"
9
+
external history_set_ : max_length:int -> int = "ml_history_set_maxlen"
10
+
external history_save_ : filename:string -> int = "ml_history_save"
11
+
external history_load_ : filename:string -> int = "ml_history_load"
12
+
13
+
external catch_break : bool -> unit = "ml_catch_break"
14
+
15
+
external setup_bridges : unit -> unit = "ml_setup_bridges"
16
+
17
+
type hint_color = Red | Green | Yellow | Blue | Magenta | Cyan | White
18
+
19
+
let completion_cb = ref (fun _ _ -> ())
20
+
let hints_cb = ref (fun _ -> None)
21
+
22
+
let set_completion_callback (f:string->completions->unit) : unit =
23
+
completion_cb := f;
24
+
Callback.register "lnoise_completion_cb" f
25
+
26
+
let set_hints_callback (f:string -> (string*hint_color*bool) option) : unit =
27
+
hints_cb := f;
28
+
Callback.register "lnoise_hints_cb" f
29
+
30
+
(* initialization: register [Sys.Break] and enable catch-break *)
31
+
let () =
32
+
setup_bridges();
33
+
set_completion_callback !completion_cb;
34
+
set_hints_callback !hints_cb;
35
+
Callback.register_exception "sys_break" Sys.Break;
36
+
catch_break true
37
+
38
+
let history_add h =
39
+
if history_add_ h = 0 then Error "Couldn't add to history"
40
+
else Ok ()
41
+
42
+
let history_set ~max_length =
43
+
if history_set_ ~max_length = 0
44
+
then Error "Couldn't set the max length of history"
45
+
else Ok ()
46
+
47
+
let history_save ~filename =
48
+
if history_save_ ~filename = 0 then Ok ()
49
+
else Error "Couldn't save"
50
+
51
+
let history_load ~filename =
52
+
if history_load_ ~filename = 0 then Ok ()
53
+
else Error "Couldn't load the file"
54
+
55
+
external clear_screen : unit -> unit = "ml_clearscreen"
56
+
external set_multiline : bool -> unit = "ml_set_multiline"
57
+
external print_keycodes : unit -> unit = "ml_printkeycodes"
+65
vendor/ocaml-linenoise/src/lNoise.mli
+65
vendor/ocaml-linenoise/src/lNoise.mli
···
1
+
(** OCaml bindings to linenoise, functions that can fail use result
2
+
type *)
3
+
4
+
(** Abstract type of completions, given to your completion callback *)
5
+
type completions
6
+
7
+
(** This function is used by the callback function registered by the
8
+
user in order to add completion options given the input string
9
+
when the user typed <TAB>. *)
10
+
val add_completion : completions -> string -> unit
11
+
12
+
(** Register the callback function that is called for upon
13
+
tab-completion, aka when <TAB> is hit in the terminal *)
14
+
val set_completion_callback : (string -> completions -> unit) -> unit
15
+
16
+
(** The high level function that is the main API of the linenoise
17
+
library. This function checks if the terminal has basic
18
+
capabilities, just checking for a blacklist of stupid terminals,
19
+
and later either calls the line editing function or uses dummy
20
+
fgets() so that you will be able to type something even in the
21
+
most desperate of the conditions. *)
22
+
val linenoise : string -> string option
23
+
24
+
(** Add a string to the history *)
25
+
val history_add : string -> (unit, string) result
26
+
27
+
(** Set the maximum length for the history. This function can be
28
+
called even if there is already some history, the function will
29
+
make sure to retain just the latest 'len' elements if the new
30
+
history length value is smaller than the amount of items already
31
+
inside the history. *)
32
+
val history_set : max_length:int -> (unit, string) result
33
+
34
+
(** Save the history in the specified file *)
35
+
val history_save : filename:string -> (unit, string) result
36
+
37
+
(** Load the history from the specified file. *)
38
+
val history_load : filename:string -> (unit, string) result
39
+
40
+
(** Clear the screen; used to handle CTRL+L *)
41
+
val clear_screen : unit -> unit
42
+
43
+
(** If [true], [ctrl-c] during a call to {!linenoise}
44
+
will raise [Sys.Break] instead of returning an empty string.
45
+
@since 1.1 *)
46
+
val catch_break : bool -> unit
47
+
48
+
(** Set if to use or not use the multi line mode. *)
49
+
val set_multiline : bool -> unit
50
+
51
+
(** This special mode is used by linenoise in order to print scan
52
+
codes on screen for debugging / development purposes. *)
53
+
val print_keycodes : unit -> unit
54
+
55
+
(** What color you want the hints to be. *)
56
+
type hint_color = Red | Green | Yellow | Blue | Magenta | Cyan | White
57
+
58
+
(** Set a hints callback, callback gets a string, aka the line input,
59
+
and you get a chance to give a hint to the user. Example, imagine
60
+
if user types git remote add, then you can give a hint of <this is
61
+
where you add a remote name> <this is where you add the remote's
62
+
URL>, see animated gif in source repo for clear example. Returned
63
+
tuple represents the hint message, color, and whether it ought to
64
+
be bold. *)
65
+
val set_hints_callback : (string -> (string * hint_color * bool) option) -> unit
+1482
vendor/ocaml-linenoise/src/linenoise_src.c
+1482
vendor/ocaml-linenoise/src/linenoise_src.c
···
1
+
/* linenoise.c -- guerrilla line editing library against the idea that a
2
+
* line editing lib needs to be 20,000 lines of C code.
3
+
*
4
+
* You can find the latest source code at:
5
+
*
6
+
* http://github.com/antirez/linenoise
7
+
*
8
+
* Does a number of crazy assumptions that happen to be true in 99.9999% of
9
+
* the 2010 UNIX computers around.
10
+
*
11
+
* ------------------------------------------------------------------------
12
+
*
13
+
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
14
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
15
+
*
16
+
* All rights reserved.
17
+
*
18
+
* Redistribution and use in source and binary forms, with or without
19
+
* modification, are permitted provided that the following conditions are
20
+
* met:
21
+
*
22
+
* * Redistributions of source code must retain the above copyright
23
+
* notice, this list of conditions and the following disclaimer.
24
+
*
25
+
* * Redistributions in binary form must reproduce the above copyright
26
+
* notice, this list of conditions and the following disclaimer in the
27
+
* documentation and/or other materials provided with the distribution.
28
+
*
29
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
+
*
41
+
* ------------------------------------------------------------------------
42
+
*
43
+
* References:
44
+
* - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
45
+
* - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
46
+
*
47
+
* Todo list:
48
+
* - Filter bogus Ctrl+<char> combinations.
49
+
* - Win32 support
50
+
*
51
+
* Bloat:
52
+
* - History search like Ctrl+r in readline?
53
+
*
54
+
* List of escape sequences used by this program, we do everything just
55
+
* with three sequences. In order to be so cheap we may have some
56
+
* flickering effect with some slow terminal, but the lesser sequences
57
+
* the more compatible.
58
+
*
59
+
* EL (Erase Line)
60
+
* Sequence: ESC [ n K
61
+
* Effect: if n is 0 or missing, clear from cursor to end of line
62
+
* Effect: if n is 1, clear from beginning of line to cursor
63
+
* Effect: if n is 2, clear entire line
64
+
*
65
+
* CUF (CUrsor Forward)
66
+
* Sequence: ESC [ n C
67
+
* Effect: moves cursor forward n chars
68
+
*
69
+
* CUB (CUrsor Backward)
70
+
* Sequence: ESC [ n D
71
+
* Effect: moves cursor backward n chars
72
+
*
73
+
* The following is used to get the terminal width if getting
74
+
* the width with the TIOCGWINSZ ioctl fails
75
+
*
76
+
* DSR (Device Status Report)
77
+
* Sequence: ESC [ 6 n
78
+
* Effect: reports the current cusor position as ESC [ n ; m R
79
+
* where n is the row and m is the column
80
+
*
81
+
* When multi line mode is enabled, we also use an additional escape
82
+
* sequence. However multi line editing is disabled by default.
83
+
*
84
+
* CUU (Cursor Up)
85
+
* Sequence: ESC [ n A
86
+
* Effect: moves cursor up of n chars.
87
+
*
88
+
* CUD (Cursor Down)
89
+
* Sequence: ESC [ n B
90
+
* Effect: moves cursor down of n chars.
91
+
*
92
+
* When linenoiseClearScreen() is called, two additional escape sequences
93
+
* are used in order to clear the screen and position the cursor at home
94
+
* position.
95
+
*
96
+
* CUP (Cursor position)
97
+
* Sequence: ESC [ H
98
+
* Effect: moves the cursor to upper left corner
99
+
*
100
+
* ED (Erase display)
101
+
* Sequence: ESC [ 2 J
102
+
* Effect: clear the whole screen
103
+
*
104
+
*/
105
+
106
+
#include <termios.h>
107
+
#include <unistd.h>
108
+
#include <stdlib.h>
109
+
#include <stdio.h>
110
+
#include <errno.h>
111
+
#include <string.h>
112
+
#include <stdlib.h>
113
+
#include <ctype.h>
114
+
#include <sys/stat.h>
115
+
#include <sys/types.h>
116
+
#include <sys/ioctl.h>
117
+
#include <unistd.h>
118
+
#include "linenoise_src.h"
119
+
120
+
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
121
+
#define LINENOISE_MAX_LINE 4096
122
+
#define UNUSED(x) (void)(x)
123
+
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
124
+
static linenoiseCompletionCallback *completionCallback = NULL;
125
+
static linenoiseHintsCallback *hintsCallback = NULL;
126
+
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
127
+
static char *linenoiseNoTTY(void);
128
+
static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags);
129
+
static void refreshLineWithFlags(struct linenoiseState *l, int flags);
130
+
131
+
static struct termios orig_termios; /* In order to restore at exit.*/
132
+
static int maskmode = 0; /* Show "***" instead of input. For passwords. */
133
+
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
134
+
static int mlmode = 0; /* Multi line mode. Default is single line. */
135
+
static int atexit_registered = 0; /* Register atexit just 1 time. */
136
+
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
137
+
static int history_len = 0;
138
+
static char **history = NULL;
139
+
140
+
int linenoiseWasInterrupted = 0;
141
+
142
+
enum KEY_ACTION{
143
+
KEY_NULL = 0, /* NULL */
144
+
CTRL_A = 1, /* Ctrl+a */
145
+
CTRL_B = 2, /* Ctrl-b */
146
+
CTRL_C = 3, /* Ctrl-c */
147
+
CTRL_D = 4, /* Ctrl-d */
148
+
CTRL_E = 5, /* Ctrl-e */
149
+
CTRL_F = 6, /* Ctrl-f */
150
+
CTRL_H = 8, /* Ctrl-h */
151
+
TAB = 9, /* Tab */
152
+
CTRL_K = 11, /* Ctrl+k */
153
+
CTRL_L = 12, /* Ctrl+l */
154
+
ENTER = 13, /* Enter */
155
+
CTRL_N = 14, /* Ctrl-n */
156
+
CTRL_P = 16, /* Ctrl-p */
157
+
CTRL_T = 20, /* Ctrl-t */
158
+
CTRL_U = 21, /* Ctrl+u */
159
+
CTRL_W = 23, /* Ctrl+w */
160
+
ESC = 27, /* Escape */
161
+
BACKSPACE = 127 /* Backspace */
162
+
};
163
+
164
+
static void linenoiseAtExit(void);
165
+
int linenoiseHistoryAdd(const char *line);
166
+
#define REFRESH_CLEAN (1<<0) // Clean the old prompt from the screen
167
+
#define REFRESH_WRITE (1<<1) // Rewrite the prompt on the screen.
168
+
#define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both.
169
+
static void refreshLine(struct linenoiseState *l);
170
+
171
+
/* Debugging macro. */
172
+
#if 0
173
+
FILE *lndebug_fp = NULL;
174
+
#define lndebug(...) \
175
+
do { \
176
+
if (lndebug_fp == NULL) { \
177
+
lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
178
+
fprintf(lndebug_fp, \
179
+
"[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
180
+
(int)l->len,(int)l->pos,(int)l->oldcolpos,plen,rows,rpos, \
181
+
(int)l->oldrows,old_rows); \
182
+
} \
183
+
fprintf(lndebug_fp, ", " __VA_ARGS__); \
184
+
fflush(lndebug_fp); \
185
+
} while (0)
186
+
#else
187
+
#define lndebug(fmt, ...)
188
+
#endif
189
+
190
+
/* ========================== Encoding functions ============================= */
191
+
192
+
/* Get byte length and column length of the previous character */
193
+
static size_t defaultPrevCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) {
194
+
UNUSED(buf); UNUSED(buf_len); UNUSED(pos);
195
+
if (col_len != NULL) *col_len = 1;
196
+
return 1;
197
+
}
198
+
199
+
/* Get byte length and column length of the next character */
200
+
static size_t defaultNextCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) {
201
+
UNUSED(buf); UNUSED(buf_len); UNUSED(pos);
202
+
if (col_len != NULL) *col_len = 1;
203
+
return 1;
204
+
}
205
+
206
+
/* Read bytes of the next character */
207
+
static size_t defaultReadCode(int fd, char *buf, size_t buf_len, int* c) {
208
+
if (buf_len < 1) return -1;
209
+
int nread = read(fd,&buf[0],1);
210
+
if (nread == 1) *c = buf[0];
211
+
return nread;
212
+
}
213
+
214
+
/* Set default encoding functions */
215
+
static linenoisePrevCharLen *prevCharLen = defaultPrevCharLen;
216
+
static linenoiseNextCharLen *nextCharLen = defaultNextCharLen;
217
+
static linenoiseReadCode *readCode = defaultReadCode;
218
+
219
+
/* Set used defined encoding functions */
220
+
void linenoiseSetEncodingFunctions(
221
+
linenoisePrevCharLen *prevCharLenFunc,
222
+
linenoiseNextCharLen *nextCharLenFunc,
223
+
linenoiseReadCode *readCodeFunc) {
224
+
prevCharLen = prevCharLenFunc;
225
+
nextCharLen = nextCharLenFunc;
226
+
readCode = readCodeFunc;
227
+
}
228
+
229
+
/* Get column length from begining of buffer to current byte position */
230
+
static size_t columnPos(const char *buf, size_t buf_len, size_t pos) {
231
+
size_t ret = 0;
232
+
size_t off = 0;
233
+
while (off < pos) {
234
+
size_t col_len;
235
+
size_t len = nextCharLen(buf,buf_len,off,&col_len);
236
+
off += len;
237
+
ret += col_len;
238
+
}
239
+
return ret;
240
+
}
241
+
242
+
/* Get column length from begining of buffer to current byte position for multiline mode*/
243
+
static size_t columnPosForMultiLine(const char *buf, size_t buf_len, size_t pos, size_t cols, size_t ini_pos) {
244
+
size_t ret = 0;
245
+
size_t colwid = ini_pos;
246
+
247
+
size_t off = 0;
248
+
while (off < buf_len) {
249
+
size_t col_len;
250
+
size_t len = nextCharLen(buf,buf_len,off,&col_len);
251
+
252
+
int dif = (int)(colwid + col_len) - (int)cols;
253
+
if (dif > 0) {
254
+
ret += dif;
255
+
colwid = col_len;
256
+
} else if (dif == 0) {
257
+
colwid = 0;
258
+
} else {
259
+
colwid += col_len;
260
+
}
261
+
262
+
if (off >= pos) break;
263
+
off += len;
264
+
ret += col_len;
265
+
}
266
+
267
+
return ret;
268
+
}
269
+
270
+
/* ======================= Low level terminal handling ====================== */
271
+
272
+
/* Enable "mask mode". When it is enabled, instead of the input that
273
+
* the user is typing, the terminal will just display a corresponding
274
+
* number of asterisks, like "****". This is useful for passwords and other
275
+
* secrets that should not be displayed. */
276
+
void linenoiseMaskModeEnable(void) {
277
+
maskmode = 1;
278
+
}
279
+
280
+
/* Disable mask mode. */
281
+
void linenoiseMaskModeDisable(void) {
282
+
maskmode = 0;
283
+
}
284
+
285
+
/* Set if to use or not the multi line mode. */
286
+
void linenoiseSetMultiLine(int ml) {
287
+
mlmode = ml;
288
+
}
289
+
290
+
/* Return true if the terminal name is in the list of terminals we know are
291
+
* not able to understand basic escape sequences. */
292
+
static int isUnsupportedTerm(void) {
293
+
char *term = getenv("TERM");
294
+
int j;
295
+
296
+
if (term == NULL) return 0;
297
+
for (j = 0; unsupported_term[j]; j++)
298
+
if (!strcasecmp(term,unsupported_term[j])) return 1;
299
+
return 0;
300
+
}
301
+
302
+
/* Raw mode: 1960 magic shit. */
303
+
static int enableRawMode(int fd) {
304
+
struct termios raw;
305
+
306
+
if (!isatty(STDIN_FILENO)) goto fatal;
307
+
if (!atexit_registered) {
308
+
atexit(linenoiseAtExit);
309
+
atexit_registered = 1;
310
+
}
311
+
if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
312
+
313
+
raw = orig_termios; /* modify the original mode */
314
+
/* input modes: no break, no CR to NL, no parity check, no strip char,
315
+
* no start/stop output control. */
316
+
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
317
+
/* output modes - disable post processing */
318
+
raw.c_oflag &= ~(OPOST);
319
+
/* control modes - set 8 bit chars */
320
+
raw.c_cflag |= (CS8);
321
+
/* local modes - choing off, canonical off, no extended functions,
322
+
* no signal chars (^Z,^C) */
323
+
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
324
+
/* control chars - set return condition: min number of bytes and timer.
325
+
* We want read to return every single byte, without timeout. */
326
+
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
327
+
328
+
/* put terminal in raw mode after flushing */
329
+
if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
330
+
rawmode = 1;
331
+
return 0;
332
+
333
+
fatal:
334
+
errno = ENOTTY;
335
+
return -1;
336
+
}
337
+
338
+
static void disableRawMode(int fd) {
339
+
/* Don't even check the return value as it's too late. */
340
+
if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
341
+
rawmode = 0;
342
+
}
343
+
344
+
/* Use the ESC [6n escape sequence to query the horizontal cursor position
345
+
* and return it. On error -1 is returned, on success the position of the
346
+
* cursor. */
347
+
static int getCursorPosition(int ifd, int ofd) {
348
+
char buf[32];
349
+
int cols, rows;
350
+
unsigned int i = 0;
351
+
352
+
/* Report cursor location */
353
+
if (write(ofd, "\x1b[6n", 4) != 4) return -1;
354
+
355
+
/* Read the response: ESC [ rows ; cols R */
356
+
while (i < sizeof(buf)-1) {
357
+
if (read(ifd,buf+i,1) != 1) break;
358
+
if (buf[i] == 'R') break;
359
+
i++;
360
+
}
361
+
buf[i] = '\0';
362
+
363
+
/* Parse it. */
364
+
if (buf[0] != ESC || buf[1] != '[') return -1;
365
+
if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
366
+
return cols;
367
+
}
368
+
369
+
/* Try to get the number of columns in the current terminal, or assume 80
370
+
* if it fails. */
371
+
static int getColumns(int ifd, int ofd) {
372
+
struct winsize ws;
373
+
374
+
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
375
+
/* ioctl() failed. Try to query the terminal itself. */
376
+
int start, cols;
377
+
378
+
/* Get the initial position so we can restore it later. */
379
+
start = getCursorPosition(ifd,ofd);
380
+
if (start == -1) goto failed;
381
+
382
+
/* Go to right margin and get position. */
383
+
if (write(ofd,"\x1b[999C",6) != 6) goto failed;
384
+
cols = getCursorPosition(ifd,ofd);
385
+
if (cols == -1) goto failed;
386
+
387
+
/* Restore position. */
388
+
if (cols > start) {
389
+
char seq[32];
390
+
snprintf(seq,32,"\x1b[%dD",cols-start);
391
+
if (write(ofd,seq,strlen(seq)) == -1) {
392
+
/* Can't recover... */
393
+
}
394
+
}
395
+
return cols;
396
+
} else {
397
+
return ws.ws_col;
398
+
}
399
+
400
+
failed:
401
+
return 80;
402
+
}
403
+
404
+
/* Clear the screen. Used to handle ctrl+l */
405
+
void linenoiseClearScreen(void) {
406
+
if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
407
+
/* nothing to do, just to avoid warning. */
408
+
}
409
+
}
410
+
411
+
/* Beep, used for completion when there is nothing to complete or when all
412
+
* the choices were already shown. */
413
+
static void linenoiseBeep(void) {
414
+
fprintf(stderr, "\x7");
415
+
fflush(stderr);
416
+
}
417
+
418
+
/* ============================== Completion ================================ */
419
+
420
+
/* Free a list of completion option populated by linenoiseAddCompletion(). */
421
+
static void freeCompletions(linenoiseCompletions *lc) {
422
+
size_t i;
423
+
for (i = 0; i < lc->len; i++)
424
+
free(lc->cvec[i]);
425
+
if (lc->cvec != NULL)
426
+
free(lc->cvec);
427
+
}
428
+
429
+
/* Called by completeLine() and linenoiseShow() to render the current
430
+
* edited line with the proposed completion. If the current completion table
431
+
* is already available, it is passed as second argument, otherwise the
432
+
* function will use the callback to obtain it.
433
+
*
434
+
* Flags are the same as refreshLine*(), that is REFRESH_* macros. */
435
+
static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags) {
436
+
/* Obtain the table of completions if the caller didn't provide one. */
437
+
linenoiseCompletions ctable = { 0, NULL };
438
+
if (lc == NULL) {
439
+
completionCallback(ls->buf,&ctable);
440
+
lc = &ctable;
441
+
}
442
+
443
+
/* Show the edited line with completion if possible, or just refresh. */
444
+
if (ls->completion_idx < lc->len) {
445
+
struct linenoiseState saved = *ls;
446
+
ls->len = ls->pos = strlen(lc->cvec[ls->completion_idx]);
447
+
ls->buf = lc->cvec[ls->completion_idx];
448
+
refreshLineWithFlags(ls,flags);
449
+
ls->len = saved.len;
450
+
ls->pos = saved.pos;
451
+
ls->buf = saved.buf;
452
+
} else {
453
+
refreshLineWithFlags(ls,flags);
454
+
}
455
+
456
+
/* Free the completions table if needed. */
457
+
if (lc != &ctable) freeCompletions(&ctable);
458
+
}
459
+
460
+
/* This is an helper function for linenoiseEdit*() and is called when the
461
+
* user types the <tab> key in order to complete the string currently in the
462
+
* input.
463
+
*
464
+
* The state of the editing is encapsulated into the pointed linenoiseState
465
+
* structure as described in the structure definition.
466
+
*
467
+
* If the function returns non-zero, the caller should handle the
468
+
* returned value as a byte read from the standard input, and process
469
+
* it as usually: this basically means that the function may return a byte
470
+
* read from the termianl but not processed. Otherwise, if zero is returned,
471
+
* the input was consumed by the completeLine() function to navigate the
472
+
* possible completions, and the caller should read for the next characters
473
+
* from stdin. */
474
+
static int completeLine(struct linenoiseState *ls, int keypressed) {
475
+
linenoiseCompletions lc = { 0, NULL };
476
+
int nwritten;
477
+
char c = keypressed;
478
+
479
+
completionCallback(ls->buf,&lc);
480
+
if (lc.len == 0) {
481
+
linenoiseBeep();
482
+
ls->in_completion = 0;
483
+
} else {
484
+
switch(c) {
485
+
case 9: /* tab */
486
+
if (ls->in_completion == 0) {
487
+
ls->in_completion = 1;
488
+
ls->completion_idx = 0;
489
+
} else {
490
+
ls->completion_idx = (ls->completion_idx+1) % (lc.len+1);
491
+
if (ls->completion_idx == lc.len) linenoiseBeep();
492
+
}
493
+
c = 0;
494
+
break;
495
+
case 27: /* escape */
496
+
/* Re-show original buffer */
497
+
if (ls->completion_idx < lc.len) refreshLine(ls);
498
+
ls->in_completion = 0;
499
+
c = 0;
500
+
break;
501
+
default:
502
+
/* Update buffer and return */
503
+
if (ls->completion_idx < lc.len) {
504
+
nwritten = snprintf(ls->buf,ls->buflen,"%s",
505
+
lc.cvec[ls->completion_idx]);
506
+
ls->len = ls->pos = nwritten;
507
+
}
508
+
ls->in_completion = 0;
509
+
break;
510
+
}
511
+
512
+
/* Show completion or original buffer */
513
+
if (ls->in_completion && ls->completion_idx < lc.len) {
514
+
refreshLineWithCompletion(ls,&lc,REFRESH_ALL);
515
+
} else {
516
+
refreshLine(ls);
517
+
}
518
+
}
519
+
520
+
freeCompletions(&lc);
521
+
return c; /* Return last read character */
522
+
}
523
+
524
+
/* Register a callback function to be called for tab-completion. */
525
+
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
526
+
completionCallback = fn;
527
+
}
528
+
529
+
/* Register a hits function to be called to show hits to the user at the
530
+
* right of the prompt. */
531
+
void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
532
+
hintsCallback = fn;
533
+
}
534
+
535
+
/* Register a function to free the hints returned by the hints callback
536
+
* registered with linenoiseSetHintsCallback(). */
537
+
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
538
+
freeHintsCallback = fn;
539
+
}
540
+
541
+
/* This function is used by the callback function registered by the user
542
+
* in order to add completion options given the input string when the
543
+
* user typed <tab>. See the example.c source code for a very easy to
544
+
* understand example. */
545
+
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
546
+
size_t len = strlen(str);
547
+
char *copy, **cvec;
548
+
549
+
copy = malloc(len+1);
550
+
if (copy == NULL) return;
551
+
memcpy(copy,str,len+1);
552
+
cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
553
+
if (cvec == NULL) {
554
+
free(copy);
555
+
return;
556
+
}
557
+
lc->cvec = cvec;
558
+
lc->cvec[lc->len++] = copy;
559
+
}
560
+
561
+
/* =========================== Line editing ================================= */
562
+
563
+
/* We define a very simple "append buffer" structure, that is an heap
564
+
* allocated string where we can append to. This is useful in order to
565
+
* write all the escape sequences in a buffer and flush them to the standard
566
+
* output in a single call, to avoid flickering effects. */
567
+
struct abuf {
568
+
char *b;
569
+
int len;
570
+
};
571
+
572
+
static void abInit(struct abuf *ab) {
573
+
ab->b = NULL;
574
+
ab->len = 0;
575
+
}
576
+
577
+
static void abAppend(struct abuf *ab, const char *s, int len) {
578
+
char *new = realloc(ab->b,ab->len+len);
579
+
580
+
if (new == NULL) return;
581
+
memcpy(new+ab->len,s,len);
582
+
ab->b = new;
583
+
ab->len += len;
584
+
}
585
+
586
+
static void abFree(struct abuf *ab) {
587
+
free(ab->b);
588
+
}
589
+
590
+
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
591
+
* to the right of the prompt. */
592
+
void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int pcollen) {
593
+
char seq[64];
594
+
size_t collen = pcollen+columnPos(l->buf,l->len,l->len);
595
+
if (hintsCallback && collen < l->cols) {
596
+
int color = -1, bold = 0;
597
+
char *hint = hintsCallback(l->buf,&color,&bold);
598
+
if (hint) {
599
+
int hintlen = strlen(hint);
600
+
int hintmaxlen = l->cols-collen;
601
+
if (hintlen > hintmaxlen) hintlen = hintmaxlen;
602
+
if (bold == 1 && color == -1) color = 37;
603
+
if (color != -1 || bold != 0)
604
+
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
605
+
else
606
+
seq[0] = '\0';
607
+
abAppend(ab,seq,strlen(seq));
608
+
abAppend(ab,hint,hintlen);
609
+
if (color != -1 || bold != 0)
610
+
abAppend(ab,"\033[0m",4);
611
+
/* Call the function to free the hint returned. */
612
+
if (freeHintsCallback) freeHintsCallback(hint);
613
+
}
614
+
}
615
+
}
616
+
617
+
/* Check if text is an ANSI escape sequence
618
+
*/
619
+
static int isAnsiEscape(const char *buf, size_t buf_len, size_t* len) {
620
+
if (buf_len > 2 && !memcmp("\033[", buf, 2)) {
621
+
size_t off = 2;
622
+
while (off < buf_len) {
623
+
switch (buf[off++]) {
624
+
case 'A': case 'B': case 'C': case 'D': case 'E':
625
+
case 'F': case 'G': case 'H': case 'J': case 'K':
626
+
case 'S': case 'T': case 'f': case 'm':
627
+
*len = off;
628
+
return 1;
629
+
}
630
+
}
631
+
}
632
+
return 0;
633
+
}
634
+
635
+
/* Get column length of prompt text
636
+
*/
637
+
static size_t promptTextColumnLen(const char *prompt, size_t plen) {
638
+
char buf[LINENOISE_MAX_LINE];
639
+
size_t buf_len = 0;
640
+
size_t off = 0;
641
+
while (off < plen) {
642
+
size_t len;
643
+
if (isAnsiEscape(prompt + off, plen - off, &len)) {
644
+
off += len;
645
+
continue;
646
+
}
647
+
buf[buf_len++] = prompt[off++];
648
+
}
649
+
return columnPos(buf,buf_len,buf_len);
650
+
}
651
+
652
+
/* Single line low level line refresh.
653
+
*
654
+
* Rewrite the currently edited line accordingly to the buffer content,
655
+
* cursor position, and number of columns of the terminal.
656
+
*
657
+
* Flags is REFRESH_* macros. The function can just remove the old
658
+
* prompt, just write it, or both. */
659
+
static void refreshSingleLine(struct linenoiseState *l, int flags) {
660
+
char seq[64];
661
+
size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt));
662
+
int fd = l->ofd;
663
+
char *buf = l->buf;
664
+
size_t len = l->len;
665
+
size_t pos = l->pos;
666
+
struct abuf ab;
667
+
668
+
while((pcollen+columnPos(buf,len,pos)) >= l->cols) {
669
+
int chlen = nextCharLen(buf,len,0,NULL);
670
+
buf += chlen;
671
+
len -= chlen;
672
+
pos -= chlen;
673
+
}
674
+
while (pcollen+columnPos(buf,len,len) > l->cols) {
675
+
len -= prevCharLen(buf,len,len,NULL);
676
+
}
677
+
678
+
abInit(&ab);
679
+
/* Cursor to left edge */
680
+
snprintf(seq,sizeof(seq),"\r");
681
+
abAppend(&ab,seq,strlen(seq));
682
+
683
+
if (flags & REFRESH_WRITE) {
684
+
/* Write the prompt and the current buffer content */
685
+
abAppend(&ab,l->prompt,strlen(l->prompt));
686
+
if (maskmode == 1) {
687
+
while (len--) abAppend(&ab,"*",1);
688
+
} else {
689
+
abAppend(&ab,buf,len);
690
+
}
691
+
/* Show hits if any. */
692
+
refreshShowHints(&ab,l,pcollen);
693
+
}
694
+
695
+
/* Erase to right */
696
+
snprintf(seq,sizeof(seq),"\x1b[0K");
697
+
abAppend(&ab,seq,strlen(seq));
698
+
699
+
if (flags & REFRESH_WRITE) {
700
+
/* Move cursor to original position. */
701
+
snprintf(seq,sizeof(seq),"\r\x1b[%dC", (int)(columnPos(buf,len,pos)+pcollen));
702
+
abAppend(&ab,seq,strlen(seq));
703
+
}
704
+
705
+
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
706
+
abFree(&ab);
707
+
}
708
+
709
+
/* Multi line low level line refresh.
710
+
*
711
+
* Rewrite the currently edited line accordingly to the buffer content,
712
+
* cursor position, and number of columns of the terminal.
713
+
*
714
+
* Flags is REFRESH_* macros. The function can just remove the old
715
+
* prompt, just write it, or both. */
716
+
static void refreshMultiLine(struct linenoiseState *l, int flags) {
717
+
char seq[64];
718
+
size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt));
719
+
int colpos = columnPosForMultiLine(l->buf, l->len, l->len, l->cols, pcollen);
720
+
int colpos2; /* cursor column position. */
721
+
int rows = (pcollen+colpos+l->cols-1)/l->cols; /* rows used by current buf. */
722
+
int rpos = (pcollen+l->oldcolpos+l->cols)/l->cols; /* cursor relative row. */
723
+
int rpos2; /* rpos after refresh. */
724
+
int col; /* colum position, zero-based. */
725
+
int old_rows = l->oldrows;
726
+
int fd = l->ofd, j;
727
+
struct abuf ab;
728
+
729
+
l->oldrows = rows;
730
+
731
+
/* First step: clear all the lines used before. To do so start by
732
+
* going to the last row. */
733
+
abInit(&ab);
734
+
735
+
if (flags & REFRESH_CLEAN) {
736
+
if (old_rows-rpos > 0) {
737
+
lndebug("go down %d", old_rows-rpos);
738
+
snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
739
+
abAppend(&ab,seq,strlen(seq));
740
+
}
741
+
742
+
/* Now for every row clear it, go up. */
743
+
for (j = 0; j < old_rows-1; j++) {
744
+
lndebug("clear+up");
745
+
snprintf(seq,64,"\r\x1b[0K\x1b[1A");
746
+
abAppend(&ab,seq,strlen(seq));
747
+
}
748
+
}
749
+
750
+
if (flags & REFRESH_ALL) {
751
+
/* Clean the top line. */
752
+
lndebug("clear");
753
+
snprintf(seq,64,"\r\x1b[0K");
754
+
abAppend(&ab,seq,strlen(seq));
755
+
}
756
+
757
+
/* Get column length to cursor position */
758
+
colpos2 = columnPosForMultiLine(l->buf,l->len,l->pos,l->cols,pcollen);
759
+
760
+
if (flags & REFRESH_WRITE) {
761
+
/* Write the prompt and the current buffer content */
762
+
abAppend(&ab,l->prompt,strlen(l->prompt));
763
+
if (maskmode == 1) {
764
+
unsigned int i;
765
+
for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
766
+
} else {
767
+
abAppend(&ab,l->buf,l->len);
768
+
}
769
+
770
+
/* Show hits if any. */
771
+
refreshShowHints(&ab,l,pcollen);
772
+
773
+
/* If we are at the very end of the screen with our prompt, we need to
774
+
* emit a newline and move the prompt to the first column. */
775
+
if (l->pos &&
776
+
l->pos == l->len &&
777
+
(colpos2+pcollen) % l->cols == 0)
778
+
{
779
+
lndebug("<newline>");
780
+
abAppend(&ab,"\n",1);
781
+
snprintf(seq,64,"\r");
782
+
abAppend(&ab,seq,strlen(seq));
783
+
rows++;
784
+
if (rows > (int)l->oldrows) l->oldrows = rows;
785
+
}
786
+
787
+
/* Move cursor to right position. */
788
+
rpos2 = (pcollen+colpos2+l->cols)/l->cols; /* Current cursor relative row */
789
+
lndebug("rpos2 %d", rpos2);
790
+
791
+
/* Go up till we reach the expected positon. */
792
+
if (rows-rpos2 > 0) {
793
+
lndebug("go-up %d", rows-rpos2);
794
+
snprintf(seq,64,"\x1b[%dA", rows-rpos2);
795
+
abAppend(&ab,seq,strlen(seq));
796
+
}
797
+
798
+
/* Set column. */
799
+
col = (pcollen+colpos2) % l->cols;
800
+
lndebug("set col %d", 1+col);
801
+
if (col)
802
+
snprintf(seq,64,"\r\x1b[%dC", col);
803
+
else
804
+
snprintf(seq,64,"\r");
805
+
abAppend(&ab,seq,strlen(seq));
806
+
}
807
+
808
+
lndebug("\n");
809
+
l->oldcolpos = colpos2;
810
+
811
+
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
812
+
abFree(&ab);
813
+
}
814
+
815
+
/* Calls the two low level functions refreshSingleLine() or
816
+
* refreshMultiLine() according to the selected mode. */
817
+
static void refreshLineWithFlags(struct linenoiseState *l, int flags) {
818
+
if (mlmode)
819
+
refreshMultiLine(l,flags);
820
+
else
821
+
refreshSingleLine(l,flags);
822
+
}
823
+
824
+
/* Utility function to avoid specifying REFRESH_ALL all the times. */
825
+
static void refreshLine(struct linenoiseState *l) {
826
+
refreshLineWithFlags(l,REFRESH_ALL);
827
+
}
828
+
829
+
/* Hide the current line, when using the multiplexing API. */
830
+
void linenoiseHide(struct linenoiseState *l) {
831
+
if (mlmode)
832
+
refreshMultiLine(l,REFRESH_CLEAN);
833
+
else
834
+
refreshSingleLine(l,REFRESH_CLEAN);
835
+
}
836
+
837
+
/* Show the current line, when using the multiplexing API. */
838
+
void linenoiseShow(struct linenoiseState *l) {
839
+
if (l->in_completion) {
840
+
refreshLineWithCompletion(l,NULL,REFRESH_WRITE);
841
+
} else {
842
+
refreshLineWithFlags(l,REFRESH_WRITE);
843
+
}
844
+
}
845
+
846
+
/* Insert the character 'c' at cursor current position.
847
+
*
848
+
* On error writing to the terminal -1 is returned, otherwise 0. */
849
+
int linenoiseEditInsert(struct linenoiseState *l, const char *cbuf, int clen) {
850
+
if (l->len+clen <= l->buflen) {
851
+
if (l->len == l->pos) {
852
+
memcpy(&l->buf[l->pos],cbuf,clen);
853
+
l->pos+=clen;
854
+
l->len+=clen;;
855
+
l->buf[l->len] = '\0';
856
+
if ((!mlmode && promptTextColumnLen(l->prompt,l->plen)+columnPos(l->buf,l->len,l->len) < l->cols && !hintsCallback)) {
857
+
/* Avoid a full update of the line in the
858
+
* trivial case. */
859
+
if (maskmode == 1) {
860
+
static const char d = '*';
861
+
if (write(l->ofd,&d,1) == -1) return -1;
862
+
} else {
863
+
if (write(l->ofd,cbuf,clen) == -1) return -1;
864
+
}
865
+
} else {
866
+
refreshLine(l);
867
+
}
868
+
} else {
869
+
memmove(l->buf+l->pos+clen,l->buf+l->pos,l->len-l->pos);
870
+
memcpy(&l->buf[l->pos],cbuf,clen);
871
+
l->pos+=clen;
872
+
l->len+=clen;
873
+
l->buf[l->len] = '\0';
874
+
refreshLine(l);
875
+
}
876
+
}
877
+
return 0;
878
+
}
879
+
880
+
/* Move cursor on the left. */
881
+
void linenoiseEditMoveLeft(struct linenoiseState *l) {
882
+
if (l->pos > 0) {
883
+
l->pos -= prevCharLen(l->buf,l->len,l->pos,NULL);
884
+
refreshLine(l);
885
+
}
886
+
}
887
+
888
+
/* Move cursor on the right. */
889
+
void linenoiseEditMoveRight(struct linenoiseState *l) {
890
+
if (l->pos != l->len) {
891
+
l->pos += nextCharLen(l->buf,l->len,l->pos,NULL);
892
+
refreshLine(l);
893
+
}
894
+
}
895
+
896
+
/* Move cursor to the start of the line. */
897
+
void linenoiseEditMoveHome(struct linenoiseState *l) {
898
+
if (l->pos != 0) {
899
+
l->pos = 0;
900
+
refreshLine(l);
901
+
}
902
+
}
903
+
904
+
/* Move cursor to the end of the line. */
905
+
void linenoiseEditMoveEnd(struct linenoiseState *l) {
906
+
if (l->pos != l->len) {
907
+
l->pos = l->len;
908
+
refreshLine(l);
909
+
}
910
+
}
911
+
912
+
/* Substitute the currently edited line with the next or previous history
913
+
* entry as specified by 'dir'. */
914
+
#define LINENOISE_HISTORY_NEXT 0
915
+
#define LINENOISE_HISTORY_PREV 1
916
+
void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
917
+
if (history_len > 1) {
918
+
/* Update the current history entry before to
919
+
* overwrite it with the next one. */
920
+
free(history[history_len - 1 - l->history_index]);
921
+
history[history_len - 1 - l->history_index] = strdup(l->buf);
922
+
/* Show the new entry */
923
+
l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
924
+
if (l->history_index < 0) {
925
+
l->history_index = 0;
926
+
return;
927
+
} else if (l->history_index >= history_len) {
928
+
l->history_index = history_len-1;
929
+
return;
930
+
}
931
+
strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
932
+
l->buf[l->buflen-1] = '\0';
933
+
l->len = l->pos = strlen(l->buf);
934
+
refreshLine(l);
935
+
}
936
+
}
937
+
938
+
/* Delete the character at the right of the cursor without altering the cursor
939
+
* position. Basically this is what happens with the "Delete" keyboard key. */
940
+
void linenoiseEditDelete(struct linenoiseState *l) {
941
+
if (l->len > 0 && l->pos < l->len) {
942
+
int chlen = nextCharLen(l->buf,l->len,l->pos,NULL);
943
+
memmove(l->buf+l->pos,l->buf+l->pos+chlen,l->len-l->pos-chlen);
944
+
l->len-=chlen;
945
+
l->buf[l->len] = '\0';
946
+
refreshLine(l);
947
+
}
948
+
}
949
+
950
+
/* Backspace implementation. */
951
+
void linenoiseEditBackspace(struct linenoiseState *l) {
952
+
if (l->pos > 0 && l->len > 0) {
953
+
int chlen = prevCharLen(l->buf,l->len,l->pos,NULL);
954
+
memmove(l->buf+l->pos-chlen,l->buf+l->pos,l->len-l->pos);
955
+
l->pos-=chlen;
956
+
l->len-=chlen;
957
+
l->buf[l->len] = '\0';
958
+
refreshLine(l);
959
+
}
960
+
}
961
+
962
+
/* Delete the previosu word, maintaining the cursor at the start of the
963
+
* current word. */
964
+
void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
965
+
size_t old_pos = l->pos;
966
+
size_t diff;
967
+
968
+
while (l->pos > 0 && l->buf[l->pos-1] == ' ')
969
+
l->pos--;
970
+
while (l->pos > 0 && l->buf[l->pos-1] != ' ')
971
+
l->pos--;
972
+
diff = old_pos - l->pos;
973
+
memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
974
+
l->len -= diff;
975
+
refreshLine(l);
976
+
}
977
+
978
+
/* This function is part of the multiplexed API of Linenoise, that is used
979
+
* in order to implement the blocking variant of the API but can also be
980
+
* called by the user directly in an event driven program. It will:
981
+
*
982
+
* 1. Initialize the linenoise state passed by the user.
983
+
* 2. Put the terminal in RAW mode.
984
+
* 3. Show the prompt.
985
+
* 4. Return control to the user, that will have to call linenoiseEditFeed()
986
+
* each time there is some data arriving in the standard input.
987
+
*
988
+
* The user can also call linenoiseEditHide() and linenoiseEditShow() if it
989
+
* is required to show some input arriving asyncronously, without mixing
990
+
* it with the currently edited line.
991
+
*
992
+
* When linenoiseEditFeed() returns non-NULL, the user finished with the
993
+
* line editing session (pressed enter CTRL-D/C): in this case the caller
994
+
* needs to call linenoiseEditStop() to put back the terminal in normal
995
+
* mode. This will not destroy the buffer, as long as the linenoiseState
996
+
* is still valid in the context of the caller.
997
+
*
998
+
* The function returns 0 on success, or -1 if writing to standard output
999
+
* fails. If stdin_fd or stdout_fd are set to -1, the default is to use
1000
+
* STDIN_FILENO and STDOUT_FILENO.
1001
+
*/
1002
+
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) {
1003
+
/* Populate the linenoise state that we pass to functions implementing
1004
+
* specific editing functionalities. */
1005
+
l->in_completion = 0;
1006
+
l->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO;
1007
+
l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO;
1008
+
l->buf = buf;
1009
+
l->buflen = buflen;
1010
+
l->prompt = prompt;
1011
+
l->plen = strlen(prompt);
1012
+
l->oldcolpos = l->pos = 0;
1013
+
l->len = 0;
1014
+
1015
+
/* Enter raw mode. */
1016
+
if (enableRawMode(l->ifd) == -1) return -1;
1017
+
1018
+
l->cols = getColumns(stdin_fd, stdout_fd);
1019
+
l->oldrows = 0;
1020
+
l->history_index = 0;
1021
+
1022
+
/* Buffer starts empty. */
1023
+
l->buf[0] = '\0';
1024
+
l->buflen--; /* Make sure there is always space for the nulterm */
1025
+
1026
+
/* If stdin is not a tty, stop here with the initialization. We
1027
+
* will actually just read a line from standard input in blocking
1028
+
* mode later, in linenoiseEditFeed(). */
1029
+
if (!isatty(l->ifd)) return 0;
1030
+
1031
+
/* The latest history entry is always our current buffer, that
1032
+
* initially is just an empty string. */
1033
+
linenoiseHistoryAdd("");
1034
+
1035
+
if (write(l->ofd,prompt,l->plen) == -1) return -1;
1036
+
return 0;
1037
+
}
1038
+
1039
+
char *linenoiseEditMore = "If you see this, you are misusing the API: when linenoiseEditFeed() is called, if it returns linenoiseEditMore the user is yet editing the line. See the README file for more information.";
1040
+
1041
+
/* This function is part of the multiplexed API of linenoise, see the top
1042
+
* comment on linenoiseEditStart() for more information. Call this function
1043
+
* each time there is some data to read from the standard input file
1044
+
* descriptor. In the case of blocking operations, this function can just be
1045
+
* called in a loop, and block.
1046
+
*
1047
+
* The function returns linenoiseEditMore to signal that line editing is still
1048
+
* in progress, that is, the user didn't yet pressed enter / CTRL-D. Otherwise
1049
+
* the function returns the pointer to the heap-allocated buffer with the
1050
+
* edited line, that the user should free with linenoiseFree().
1051
+
*
1052
+
* On special conditions, NULL is returned and errno is populated:
1053
+
*
1054
+
* EAGAIN if the user pressed Ctrl-C
1055
+
* ENOENT if the user pressed Ctrl-D
1056
+
*
1057
+
* Some other errno: I/O error.
1058
+
*/
1059
+
char *linenoiseEditFeed(struct linenoiseState *l) {
1060
+
/* Not a TTY, pass control to line reading without character
1061
+
* count limits. */
1062
+
if (!isatty(l->ifd)) return linenoiseNoTTY();
1063
+
1064
+
int c;
1065
+
int nread;
1066
+
char cbuf[32]; // large enough for any encoding?
1067
+
char seq[3];
1068
+
1069
+
nread = readCode(l->ifd,cbuf,sizeof(cbuf),&c);
1070
+
if (nread <= 0) return NULL;
1071
+
1072
+
/* Only autocomplete when the callback is set. It returns < 0 when
1073
+
* there was an error reading from fd. Otherwise it will return the
1074
+
* character that should be handled next. */
1075
+
if ((l->in_completion || c == 9) && completionCallback != NULL) {
1076
+
c = completeLine(l,c);
1077
+
/* Return on errors */
1078
+
if (c < 0) return NULL;
1079
+
/* Read next character when 0 */
1080
+
if (c == 0) return linenoiseEditMore;
1081
+
}
1082
+
1083
+
switch(c) {
1084
+
case ENTER: /* enter */
1085
+
history_len--;
1086
+
free(history[history_len]);
1087
+
if (mlmode) linenoiseEditMoveEnd(l);
1088
+
if (hintsCallback) {
1089
+
/* Force a refresh without hints to leave the previous
1090
+
* line as the user typed it after a newline. */
1091
+
linenoiseHintsCallback *hc = hintsCallback;
1092
+
hintsCallback = NULL;
1093
+
refreshLine(l);
1094
+
hintsCallback = hc;
1095
+
}
1096
+
return strdup(l->buf);
1097
+
case CTRL_C: /* ctrl-c */
1098
+
errno = EAGAIN;
1099
+
linenoiseWasInterrupted = 1;
1100
+
return NULL;
1101
+
case BACKSPACE: /* backspace */
1102
+
case 8: /* ctrl-h */
1103
+
linenoiseEditBackspace(l);
1104
+
break;
1105
+
case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
1106
+
line is empty, act as end-of-file. */
1107
+
if (l->len > 0) {
1108
+
linenoiseEditDelete(l);
1109
+
} else {
1110
+
history_len--;
1111
+
free(history[history_len]);
1112
+
errno = ENOENT;
1113
+
return NULL;
1114
+
}
1115
+
break;
1116
+
case CTRL_T: /* ctrl-t, swaps current character with previous. */
1117
+
if (l->pos > 0 && l->pos < l->len) {
1118
+
int aux = l->buf[l->pos-1];
1119
+
l->buf[l->pos-1] = l->buf[l->pos];
1120
+
l->buf[l->pos] = aux;
1121
+
if (l->pos != l->len-1) l->pos++;
1122
+
refreshLine(l);
1123
+
}
1124
+
break;
1125
+
case CTRL_B: /* ctrl-b */
1126
+
linenoiseEditMoveLeft(l);
1127
+
break;
1128
+
case CTRL_F: /* ctrl-f */
1129
+
linenoiseEditMoveRight(l);
1130
+
break;
1131
+
case CTRL_P: /* ctrl-p */
1132
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
1133
+
break;
1134
+
case CTRL_N: /* ctrl-n */
1135
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
1136
+
break;
1137
+
case ESC: /* escape sequence */
1138
+
/* Read the next two bytes representing the escape sequence.
1139
+
* Use two calls to handle slow terminals returning the two
1140
+
* chars at different times. */
1141
+
if (read(l->ifd,seq,1) == -1) break;
1142
+
if (read(l->ifd,seq+1,1) == -1) break;
1143
+
1144
+
/* ESC [ sequences. */
1145
+
if (seq[0] == '[') {
1146
+
if (seq[1] >= '0' && seq[1] <= '9') {
1147
+
/* Extended escape, read additional byte. */
1148
+
if (read(l->ifd,seq+2,1) == -1) break;
1149
+
if (seq[2] == '~') {
1150
+
switch(seq[1]) {
1151
+
case '3': /* Delete key. */
1152
+
linenoiseEditDelete(l);
1153
+
break;
1154
+
}
1155
+
}
1156
+
} else {
1157
+
switch(seq[1]) {
1158
+
case 'A': /* Up */
1159
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
1160
+
break;
1161
+
case 'B': /* Down */
1162
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
1163
+
break;
1164
+
case 'C': /* Right */
1165
+
linenoiseEditMoveRight(l);
1166
+
break;
1167
+
case 'D': /* Left */
1168
+
linenoiseEditMoveLeft(l);
1169
+
break;
1170
+
case 'H': /* Home */
1171
+
linenoiseEditMoveHome(l);
1172
+
break;
1173
+
case 'F': /* End*/
1174
+
linenoiseEditMoveEnd(l);
1175
+
break;
1176
+
}
1177
+
}
1178
+
}
1179
+
1180
+
/* ESC O sequences. */
1181
+
else if (seq[0] == 'O') {
1182
+
switch(seq[1]) {
1183
+
case 'H': /* Home */
1184
+
linenoiseEditMoveHome(l);
1185
+
break;
1186
+
case 'F': /* End*/
1187
+
linenoiseEditMoveEnd(l);
1188
+
break;
1189
+
}
1190
+
}
1191
+
break;
1192
+
default:
1193
+
if (linenoiseEditInsert(l,cbuf,nread)) return NULL;
1194
+
break;
1195
+
case CTRL_U: /* Ctrl+u, delete the whole line. */
1196
+
l->buf[0] = '\0';
1197
+
l->pos = l->len = 0;
1198
+
refreshLine(l);
1199
+
break;
1200
+
case CTRL_K: /* Ctrl+k, delete from current to end of line. */
1201
+
l->buf[l->pos] = '\0';
1202
+
l->len = l->pos;
1203
+
refreshLine(l);
1204
+
break;
1205
+
case CTRL_A: /* Ctrl+a, go to the start of the line */
1206
+
linenoiseEditMoveHome(l);
1207
+
break;
1208
+
case CTRL_E: /* ctrl+e, go to the end of the line */
1209
+
linenoiseEditMoveEnd(l);
1210
+
break;
1211
+
case CTRL_L: /* ctrl+l, clear screen */
1212
+
linenoiseClearScreen();
1213
+
refreshLine(l);
1214
+
break;
1215
+
case CTRL_W: /* ctrl+w, delete previous word */
1216
+
linenoiseEditDeletePrevWord(l);
1217
+
break;
1218
+
}
1219
+
return linenoiseEditMore;
1220
+
}
1221
+
1222
+
/* This is part of the multiplexed linenoise API. See linenoiseEditStart()
1223
+
* for more information. This function is called when linenoiseEditFeed()
1224
+
* returns something different than NULL. At this point the user input
1225
+
* is in the buffer, and we can restore the terminal in normal mode. */
1226
+
void linenoiseEditStop(struct linenoiseState *l) {
1227
+
if (!isatty(l->ifd)) return;
1228
+
disableRawMode(l->ifd);
1229
+
printf("\n");
1230
+
}
1231
+
1232
+
/* This just implements a blocking loop for the multiplexed API.
1233
+
* In many applications that are not event-drivern, we can just call
1234
+
* the blocking linenoise API, wait for the user to complete the editing
1235
+
* and return the buffer. */
1236
+
static char *linenoiseBlockingEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
1237
+
{
1238
+
struct linenoiseState l;
1239
+
1240
+
/* Editing without a buffer is invalid. */
1241
+
if (buflen == 0) {
1242
+
errno = EINVAL;
1243
+
return NULL;
1244
+
}
1245
+
1246
+
linenoiseEditStart(&l,stdin_fd,stdout_fd,buf,buflen,prompt);
1247
+
char *res;
1248
+
while((res = linenoiseEditFeed(&l)) == linenoiseEditMore);
1249
+
linenoiseEditStop(&l);
1250
+
return res;
1251
+
}
1252
+
1253
+
/* This special mode is used by linenoise in order to print scan codes
1254
+
* on screen for debugging / development purposes. It is implemented
1255
+
* by the linenoise_example program using the --keycodes option. */
1256
+
void linenoisePrintKeyCodes(void) {
1257
+
char quit[4];
1258
+
1259
+
printf("Linenoise key codes debugging mode.\n"
1260
+
"Press keys to see scan codes. Type 'quit' at any time to exit.\n");
1261
+
if (enableRawMode(STDIN_FILENO) == -1) return;
1262
+
memset(quit,' ',4);
1263
+
while(1) {
1264
+
char c;
1265
+
int nread;
1266
+
1267
+
nread = read(STDIN_FILENO,&c,1);
1268
+
if (nread <= 0) continue;
1269
+
memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
1270
+
quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
1271
+
if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
1272
+
1273
+
printf("'%c' %02x (%d) (type quit to exit)\n",
1274
+
isprint((int)c) ? c : '?', (int)c, (int)c);
1275
+
printf("\r"); /* Go left edge manually, we are in raw mode. */
1276
+
fflush(stdout);
1277
+
}
1278
+
disableRawMode(STDIN_FILENO);
1279
+
}
1280
+
1281
+
/* This function is called when linenoise() is called with the standard
1282
+
* input file descriptor not attached to a TTY. So for example when the
1283
+
* program using linenoise is called in pipe or with a file redirected
1284
+
* to its standard input. In this case, we want to be able to return the
1285
+
* line regardless of its length (by default we are limited to 4k). */
1286
+
static char *linenoiseNoTTY(void) {
1287
+
char *line = NULL;
1288
+
size_t len = 0, maxlen = 0;
1289
+
1290
+
while(1) {
1291
+
if (len == maxlen) {
1292
+
if (maxlen == 0) maxlen = 16;
1293
+
maxlen *= 2;
1294
+
char *oldval = line;
1295
+
line = realloc(line,maxlen);
1296
+
if (line == NULL) {
1297
+
if (oldval) free(oldval);
1298
+
return NULL;
1299
+
}
1300
+
}
1301
+
int c = fgetc(stdin);
1302
+
if (c == EOF || c == '\n') {
1303
+
if (c == EOF && len == 0) {
1304
+
free(line);
1305
+
return NULL;
1306
+
} else {
1307
+
line[len] = '\0';
1308
+
return line;
1309
+
}
1310
+
} else {
1311
+
line[len] = c;
1312
+
len++;
1313
+
}
1314
+
}
1315
+
}
1316
+
1317
+
/* The high level function that is the main API of the linenoise library.
1318
+
* This function checks if the terminal has basic capabilities, just checking
1319
+
* for a blacklist of stupid terminals, and later either calls the line
1320
+
* editing function or uses dummy fgets() so that you will be able to type
1321
+
* something even in the most desperate of the conditions. */
1322
+
char *linenoise(const char *prompt) {
1323
+
char buf[LINENOISE_MAX_LINE];
1324
+
1325
+
if (!isatty(STDIN_FILENO)) {
1326
+
/* Not a tty: read from file / pipe. In this mode we don't want any
1327
+
* limit to the line size, so we call a function to handle that. */
1328
+
return linenoiseNoTTY();
1329
+
} else if (isUnsupportedTerm()) {
1330
+
size_t len;
1331
+
1332
+
printf("%s",prompt);
1333
+
fflush(stdout);
1334
+
if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
1335
+
len = strlen(buf);
1336
+
while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
1337
+
len--;
1338
+
buf[len] = '\0';
1339
+
}
1340
+
return strdup(buf);
1341
+
} else {
1342
+
char *retval = linenoiseBlockingEdit(STDIN_FILENO,STDOUT_FILENO,buf,LINENOISE_MAX_LINE,prompt);
1343
+
return retval;
1344
+
}
1345
+
}
1346
+
1347
+
/* This is just a wrapper the user may want to call in order to make sure
1348
+
* the linenoise returned buffer is freed with the same allocator it was
1349
+
* created with. Useful when the main program is using an alternative
1350
+
* allocator. */
1351
+
void linenoiseFree(void *ptr) {
1352
+
if (ptr == linenoiseEditMore) return; // Protect from API misuse.
1353
+
free(ptr);
1354
+
}
1355
+
1356
+
/* ================================ History ================================= */
1357
+
1358
+
/* Free the history, but does not reset it. Only used when we have to
1359
+
* exit() to avoid memory leaks are reported by valgrind & co. */
1360
+
static void freeHistory(void) {
1361
+
if (history) {
1362
+
int j;
1363
+
1364
+
for (j = 0; j < history_len; j++)
1365
+
free(history[j]);
1366
+
free(history);
1367
+
}
1368
+
}
1369
+
1370
+
/* At exit we'll try to fix the terminal to the initial conditions. */
1371
+
static void linenoiseAtExit(void) {
1372
+
disableRawMode(STDIN_FILENO);
1373
+
freeHistory();
1374
+
}
1375
+
1376
+
/* This is the API call to add a new entry in the linenoise history.
1377
+
* It uses a fixed array of char pointers that are shifted (memmoved)
1378
+
* when the history max length is reached in order to remove the older
1379
+
* entry and make room for the new one, so it is not exactly suitable for huge
1380
+
* histories, but will work well for a few hundred of entries.
1381
+
*
1382
+
* Using a circular buffer is smarter, but a bit more complex to handle. */
1383
+
int linenoiseHistoryAdd(const char *line) {
1384
+
char *linecopy;
1385
+
1386
+
if (history_max_len == 0) return 0;
1387
+
1388
+
/* Initialization on first call. */
1389
+
if (history == NULL) {
1390
+
history = malloc(sizeof(char*)*history_max_len);
1391
+
if (history == NULL) return 0;
1392
+
memset(history,0,(sizeof(char*)*history_max_len));
1393
+
}
1394
+
1395
+
/* Don't add duplicated lines. */
1396
+
if (history_len && !strcmp(history[history_len-1], line)) return 0;
1397
+
1398
+
/* Add an heap allocated copy of the line in the history.
1399
+
* If we reached the max length, remove the older line. */
1400
+
linecopy = strdup(line);
1401
+
if (!linecopy) return 0;
1402
+
if (history_len == history_max_len) {
1403
+
free(history[0]);
1404
+
memmove(history,history+1,sizeof(char*)*(history_max_len-1));
1405
+
history_len--;
1406
+
}
1407
+
history[history_len] = linecopy;
1408
+
history_len++;
1409
+
return 1;
1410
+
}
1411
+
1412
+
/* Set the maximum length for the history. This function can be called even
1413
+
* if there is already some history, the function will make sure to retain
1414
+
* just the latest 'len' elements if the new history length value is smaller
1415
+
* than the amount of items already inside the history. */
1416
+
int linenoiseHistorySetMaxLen(int len) {
1417
+
char **new;
1418
+
1419
+
if (len < 1) return 0;
1420
+
if (history) {
1421
+
int tocopy = history_len;
1422
+
1423
+
new = malloc(sizeof(char*)*len);
1424
+
if (new == NULL) return 0;
1425
+
1426
+
/* If we can't copy everything, free the elements we'll not use. */
1427
+
if (len < tocopy) {
1428
+
int j;
1429
+
1430
+
for (j = 0; j < tocopy-len; j++) free(history[j]);
1431
+
tocopy = len;
1432
+
}
1433
+
memset(new,0,sizeof(char*)*len);
1434
+
memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
1435
+
free(history);
1436
+
history = new;
1437
+
}
1438
+
history_max_len = len;
1439
+
if (history_len > history_max_len)
1440
+
history_len = history_max_len;
1441
+
return 1;
1442
+
}
1443
+
1444
+
/* Save the history in the specified file. On success 0 is returned
1445
+
* otherwise -1 is returned. */
1446
+
int linenoiseHistorySave(const char *filename) {
1447
+
mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
1448
+
FILE *fp;
1449
+
int j;
1450
+
1451
+
fp = fopen(filename,"w");
1452
+
umask(old_umask);
1453
+
if (fp == NULL) return -1;
1454
+
chmod(filename,S_IRUSR|S_IWUSR);
1455
+
for (j = 0; j < history_len; j++)
1456
+
fprintf(fp,"%s\n",history[j]);
1457
+
fclose(fp);
1458
+
return 0;
1459
+
}
1460
+
1461
+
/* Load the history from the specified file. If the file does not exist
1462
+
* zero is returned and no operation is performed.
1463
+
*
1464
+
* If the file exists and the operation succeeded 0 is returned, otherwise
1465
+
* on error -1 is returned. */
1466
+
int linenoiseHistoryLoad(const char *filename) {
1467
+
FILE *fp = fopen(filename,"r");
1468
+
char buf[LINENOISE_MAX_LINE];
1469
+
1470
+
if (fp == NULL) return -1;
1471
+
1472
+
while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
1473
+
char *p;
1474
+
1475
+
p = strchr(buf,'\r');
1476
+
if (!p) p = strchr(buf,'\n');
1477
+
if (p) *p = '\0';
1478
+
linenoiseHistoryAdd(buf);
1479
+
}
1480
+
fclose(fp);
1481
+
return 0;
1482
+
}
+123
vendor/ocaml-linenoise/src/linenoise_src.h
+123
vendor/ocaml-linenoise/src/linenoise_src.h
···
1
+
/* linenoise.h -- VERSION 1.0
2
+
*
3
+
* Guerrilla line editing library against the idea that a line editing lib
4
+
* needs to be 20,000 lines of C code.
5
+
*
6
+
* See linenoise.c for more information.
7
+
*
8
+
* ------------------------------------------------------------------------
9
+
*
10
+
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
11
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
12
+
*
13
+
* All rights reserved.
14
+
*
15
+
* Redistribution and use in source and binary forms, with or without
16
+
* modification, are permitted provided that the following conditions are
17
+
* met:
18
+
*
19
+
* * Redistributions of source code must retain the above copyright
20
+
* notice, this list of conditions and the following disclaimer.
21
+
*
22
+
* * Redistributions in binary form must reproduce the above copyright
23
+
* notice, this list of conditions and the following disclaimer in the
24
+
* documentation and/or other materials provided with the distribution.
25
+
*
26
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
*/
38
+
39
+
#ifndef __LINENOISE_H
40
+
#define __LINENOISE_H
41
+
42
+
#ifdef __cplusplus
43
+
extern "C" {
44
+
#endif
45
+
46
+
#include <stddef.h> /* For size_t. */
47
+
48
+
extern int linenoiseWasInterrupted; /* boolean signalling if last call was ctrl-c */
49
+
extern char *linenoiseEditMore;
50
+
51
+
/* The linenoiseState structure represents the state during line editing.
52
+
* We pass this state to functions implementing specific editing
53
+
* functionalities. */
54
+
struct linenoiseState {
55
+
int in_completion; /* The user pressed TAB and we are now in completion
56
+
* mode, so input is handled by completeLine(). */
57
+
size_t completion_idx; /* Index of next completion to propose. */
58
+
int ifd; /* Terminal stdin file descriptor. */
59
+
int ofd; /* Terminal stdout file descriptor. */
60
+
char *buf; /* Edited line buffer. */
61
+
size_t buflen; /* Edited line buffer size. */
62
+
const char *prompt; /* Prompt to display. */
63
+
size_t plen; /* Prompt length. */
64
+
size_t pos; /* Current cursor position. */
65
+
size_t oldcolpos; /* Previous refresh cursor column position. */
66
+
size_t len; /* Current edited line length. */
67
+
size_t cols; /* Number of columns in terminal. */
68
+
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
69
+
int history_index; /* The history index we are currently editing. */
70
+
};
71
+
72
+
typedef struct linenoiseCompletions {
73
+
size_t len;
74
+
char **cvec;
75
+
} linenoiseCompletions;
76
+
77
+
/* Non blocking API. */
78
+
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt);
79
+
char *linenoiseEditFeed(struct linenoiseState *l);
80
+
void linenoiseEditStop(struct linenoiseState *l);
81
+
void linenoiseHide(struct linenoiseState *l);
82
+
void linenoiseShow(struct linenoiseState *l);
83
+
84
+
/* Blocking API. */
85
+
char *linenoise(const char *prompt);
86
+
void linenoiseFree(void *ptr);
87
+
88
+
/* Completion API. */
89
+
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
90
+
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
91
+
typedef void(linenoiseFreeHintsCallback)(void *);
92
+
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
93
+
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
94
+
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
95
+
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
96
+
97
+
/* History API. */
98
+
int linenoiseHistoryAdd(const char *line);
99
+
int linenoiseHistorySetMaxLen(int len);
100
+
int linenoiseHistorySave(const char *filename);
101
+
int linenoiseHistoryLoad(const char *filename);
102
+
103
+
/* Other utilities. */
104
+
void linenoiseClearScreen(void);
105
+
void linenoiseSetMultiLine(int ml);
106
+
void linenoisePrintKeyCodes(void);
107
+
void linenoiseMaskModeEnable(void);
108
+
void linenoiseMaskModeDisable(void);
109
+
110
+
typedef size_t (linenoisePrevCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len);
111
+
typedef size_t (linenoiseNextCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len);
112
+
typedef size_t (linenoiseReadCode)(int fd, char *buf, size_t buf_len, int* c);
113
+
114
+
void linenoiseSetEncodingFunctions(
115
+
linenoisePrevCharLen *prevCharLenFunc,
116
+
linenoiseNextCharLen *nextCharLenFunc,
117
+
linenoiseReadCode *readCodeFunc);
118
+
119
+
#ifdef __cplusplus
120
+
}
121
+
#endif
122
+
123
+
#endif /* __LINENOISE_H */
+183
vendor/ocaml-linenoise/src/linenoise_stubs.c
+183
vendor/ocaml-linenoise/src/linenoise_stubs.c
···
1
+
// OCaml declarations
2
+
#include <caml/alloc.h>
3
+
#include <caml/memory.h>
4
+
#include <caml/callback.h>
5
+
#include <caml/fail.h>
6
+
#include <caml/threads.h>
7
+
8
+
#include <errno.h>
9
+
#include <assert.h>
10
+
11
+
#include "linenoise_src.h"
12
+
13
+
// Ripped from ctypes
14
+
#define Val_none Val_int(0)
15
+
#define Some_val(v) Field(v, 0)
16
+
17
+
static value Val_some(value v)
18
+
{
19
+
CAMLparam1(v);
20
+
CAMLlocal1(some);
21
+
some = caml_alloc(1, 0);
22
+
Store_field(some, 0, v);
23
+
CAMLreturn(some);
24
+
}
25
+
26
+
/* if true, raise Sys.Break on ctrl-c */
27
+
static int raise_sys_break = 0;
28
+
29
+
CAMLprim value ml_catch_break(value flag)
30
+
{
31
+
CAMLparam1(flag);
32
+
raise_sys_break = Bool_val(flag);
33
+
CAMLreturn(Val_unit);
34
+
}
35
+
36
+
CAMLprim value ml_add_completion(value completions, value new_completion)
37
+
{
38
+
CAMLparam2(completions, new_completion);
39
+
char* c_new_completion = caml_stat_strdup(String_val(new_completion));
40
+
linenoiseAddCompletion((linenoiseCompletions *)completions, c_new_completion);
41
+
caml_stat_free(c_new_completion);
42
+
CAMLreturn(Val_unit);
43
+
}
44
+
45
+
// this bridge runs with the runtime lock acquired
46
+
static void completion_bridge_inner(const char *buf, linenoiseCompletions *lc)
47
+
{
48
+
CAMLparam0();
49
+
CAMLlocal1(str_copy);
50
+
str_copy = caml_copy_string(buf);
51
+
caml_callback2(*caml_named_value("lnoise_completion_cb"), str_copy, (value)lc);
52
+
CAMLreturn0;
53
+
}
54
+
55
+
static void completion_bridge(const char *buf, linenoiseCompletions *lc)
56
+
{
57
+
caml_acquire_runtime_system();
58
+
completion_bridge_inner(buf, lc);
59
+
caml_release_runtime_system();
60
+
}
61
+
62
+
static char *hints_bridge_inner(const char *buf, int *color, int *bold)
63
+
{
64
+
CAMLparam0();
65
+
CAMLlocal2(str_copy, cb_result);
66
+
67
+
str_copy = caml_copy_string(buf);
68
+
69
+
cb_result = caml_callback(*caml_named_value("lnoise_hints_cb"), str_copy);
70
+
if (cb_result == Val_none) {
71
+
CAMLreturnT(char *,NULL);
72
+
} else {
73
+
char* msg = caml_stat_strdup(String_val(Field(Field(cb_result, 0), 0)));
74
+
*color = Int_val(Field(Field(cb_result, 0), 1)) + 31;
75
+
*bold = Bool_val(Field(Field(cb_result, 0), 2));
76
+
CAMLreturnT(char *,msg);
77
+
}
78
+
}
79
+
80
+
static char *hints_bridge(const char *buf, int *color, int *bold)
81
+
{
82
+
caml_acquire_runtime_system();
83
+
char* res = hints_bridge_inner(buf, color, bold);
84
+
caml_release_runtime_system();
85
+
return res;
86
+
}
87
+
88
+
89
+
static void free_hints_bridge(void* data) {
90
+
caml_acquire_runtime_system();
91
+
caml_stat_free(data);
92
+
caml_release_runtime_system();
93
+
}
94
+
95
+
__attribute__((constructor))
96
+
void set_free_hints(void) { linenoiseSetFreeHintsCallback(free); }
97
+
98
+
CAMLprim value ml_setup_bridges(value unit) {
99
+
CAMLparam1(unit);
100
+
linenoiseSetCompletionCallback(completion_bridge);
101
+
linenoiseSetHintsCallback(hints_bridge);
102
+
linenoiseSetFreeHintsCallback(free_hints_bridge);
103
+
CAMLreturn(Val_unit);
104
+
}
105
+
106
+
CAMLprim value ml_linenoise(value prompt)
107
+
{
108
+
CAMLparam1(prompt);
109
+
CAMLlocal1(lnoise_result);
110
+
111
+
linenoiseWasInterrupted = 0; // reset
112
+
char* c_prompt = caml_stat_strdup(String_val(prompt));
113
+
114
+
caml_release_runtime_system();
115
+
const char *result = linenoise(c_prompt);
116
+
caml_acquire_runtime_system();
117
+
118
+
caml_stat_free(c_prompt);
119
+
if (!result) {
120
+
if (linenoiseWasInterrupted && raise_sys_break) {
121
+
caml_raise_constant(*caml_named_value("sys_break"));
122
+
} else {
123
+
CAMLreturn(Val_none);
124
+
}
125
+
}
126
+
lnoise_result = caml_copy_string(result);
127
+
linenoiseFree((void*)result);
128
+
CAMLreturn(Val_some(lnoise_result));
129
+
}
130
+
131
+
CAMLprim value ml_history_add(value line)
132
+
{
133
+
CAMLparam1(line);
134
+
char* c_line = caml_stat_strdup(String_val(line));
135
+
int res = linenoiseHistoryAdd(c_line);
136
+
caml_stat_free(c_line);
137
+
CAMLreturn(Val_int(res));
138
+
}
139
+
140
+
CAMLprim value ml_history_set_maxlen(value max)
141
+
{
142
+
CAMLparam1(max);
143
+
CAMLreturn(Val_int(linenoiseHistorySetMaxLen(Int_val(max))));
144
+
}
145
+
146
+
CAMLprim value ml_history_save(value filename)
147
+
{
148
+
CAMLparam1(filename);
149
+
char* c_filename = caml_stat_strdup(String_val(filename));
150
+
int res = linenoiseHistorySave(c_filename);
151
+
caml_stat_free(c_filename);
152
+
CAMLreturn(Val_int(res));
153
+
}
154
+
155
+
CAMLprim value ml_history_load(value filename)
156
+
{
157
+
CAMLparam1(filename);
158
+
char* c_filename= caml_stat_strdup(String_val(filename));
159
+
int res = linenoiseHistoryLoad(c_filename);
160
+
caml_stat_free(c_filename);
161
+
CAMLreturn(Val_int(res));
162
+
}
163
+
164
+
CAMLprim value ml_clearscreen(__attribute__((unused))value unit)
165
+
{
166
+
CAMLparam0();
167
+
linenoiseClearScreen();
168
+
CAMLreturn(Val_unit);
169
+
}
170
+
171
+
CAMLprim value ml_set_multiline(value use_multiline)
172
+
{
173
+
CAMLparam1(use_multiline);
174
+
linenoiseSetMultiLine(Bool_val(use_multiline));
175
+
CAMLreturn(Val_unit);
176
+
}
177
+
178
+
CAMLprim value ml_printkeycodes(void)
179
+
{
180
+
CAMLparam0();
181
+
linenoisePrintKeyCodes();
182
+
CAMLreturn(Val_unit);
183
+
}
+473
vendor/ocaml-linenoise/src/utf8.c
+473
vendor/ocaml-linenoise/src/utf8.c
···
1
+
/* encoding/utf8.c -- VERSION 1.0
2
+
*
3
+
* Guerrilla line editing library against the idea that a line editing lib
4
+
* needs to be 20,000 lines of C code.
5
+
*
6
+
* You can find the latest source code at:
7
+
*
8
+
* http://github.com/antirez/linenoise
9
+
*
10
+
* Does a number of crazy assumptions that happen to be true in 99.9999% of
11
+
* the 2010 UNIX computers around.
12
+
*
13
+
* ------------------------------------------------------------------------
14
+
*
15
+
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
16
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
17
+
*
18
+
* All rights reserved.
19
+
*
20
+
* Redistribution and use in source and binary forms, with or without
21
+
* modification, are permitted provided that the following conditions are
22
+
* met:
23
+
*
24
+
* * Redistributions of source code must retain the above copyright
25
+
* notice, this list of conditions and the following disclaimer.
26
+
*
27
+
* * Redistributions in binary form must reproduce the above copyright
28
+
* notice, this list of conditions and the following disclaimer in the
29
+
* documentation and/or other materials provided with the distribution.
30
+
*
31
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42
+
*/
43
+
44
+
#include <unistd.h>
45
+
#include <stdio.h>
46
+
47
+
#define UNUSED(x) (void)(x)
48
+
49
+
/* ============================ UTF8 utilities ============================== */
50
+
51
+
static unsigned long wideCharTable[][2] = {
52
+
{ 0x1100, 0x115F }, { 0x231A, 0x231B }, { 0x2329, 0x232A }, { 0x23E9, 0x23EC },
53
+
{ 0x23F0, 0x23F0 }, { 0x23F3, 0x23F3 }, { 0x25FD, 0x25FE }, { 0x2614, 0x2615 },
54
+
{ 0x2630, 0x2637 }, { 0x2648, 0x2653 }, { 0x267F, 0x267F }, { 0x268A, 0x268F },
55
+
{ 0x2693, 0x2693 }, { 0x26A1, 0x26A1 }, { 0x26AA, 0x26AB }, { 0x26BD, 0x26BE },
56
+
{ 0x26C4, 0x26C5 }, { 0x26CE, 0x26CE }, { 0x26D4, 0x26D4 }, { 0x26EA, 0x26EA },
57
+
{ 0x26F2, 0x26F3 }, { 0x26F5, 0x26F5 }, { 0x26FA, 0x26FA }, { 0x26FD, 0x26FD },
58
+
{ 0x2705, 0x2705 }, { 0x270A, 0x270B }, { 0x2728, 0x2728 }, { 0x274C, 0x274C },
59
+
{ 0x274E, 0x274E }, { 0x2753, 0x2755 }, { 0x2757, 0x2757 }, { 0x2795, 0x2797 },
60
+
{ 0x27B0, 0x27B0 }, { 0x27BF, 0x27BF }, { 0x2B1B, 0x2B1C }, { 0x2B50, 0x2B50 },
61
+
{ 0x2B55, 0x2B55 }, { 0x2E80, 0x2E99 }, { 0x2E9B, 0x2EF3 }, { 0x2F00, 0x2FD5 },
62
+
{ 0x2FF0, 0x303E }, { 0x3041, 0x3096 }, { 0x3099, 0x30FF }, { 0x3105, 0x312F },
63
+
{ 0x3131, 0x318E }, { 0x3190, 0x31E5 }, { 0x31EF, 0x321E }, { 0x3220, 0x3247 },
64
+
{ 0x3250, 0xA48C }, { 0xA490, 0xA4C6 }, { 0xA960, 0xA97C }, { 0xAC00, 0xD7A3 },
65
+
{ 0xF900, 0xFAFF }, { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE52 }, { 0xFE54, 0xFE66 },
66
+
{ 0xFE68, 0xFE6B }, { 0xFF01, 0xFF60 }, { 0xFFE0, 0xFFE6 }, { 0x16FE0, 0x16FE4 },
67
+
{ 0x16FF0, 0x16FF1 }, { 0x17000, 0x187F7 }, { 0x18800, 0x18CD5 }, { 0x18CFF, 0x18D08 },
68
+
{ 0x1AFF0, 0x1AFF3 }, { 0x1AFF5, 0x1AFFB }, { 0x1AFFD, 0x1AFFE }, { 0x1B000, 0x1B122 },
69
+
{ 0x1B132, 0x1B132 }, { 0x1B150, 0x1B152 }, { 0x1B155, 0x1B155 }, { 0x1B164, 0x1B167 },
70
+
{ 0x1B170, 0x1B2FB }, { 0x1D300, 0x1D356 }, { 0x1D360, 0x1D376 }, { 0x1F004, 0x1F004 },
71
+
{ 0x1F0CF, 0x1F0CF }, { 0x1F18E, 0x1F18E }, { 0x1F191, 0x1F19A }, { 0x1F200, 0x1F202 },
72
+
{ 0x1F210, 0x1F23B }, { 0x1F240, 0x1F248 }, { 0x1F250, 0x1F251 }, { 0x1F260, 0x1F265 },
73
+
{ 0x1F300, 0x1F320 }, { 0x1F32D, 0x1F335 }, { 0x1F337, 0x1F37C }, { 0x1F37E, 0x1F393 },
74
+
{ 0x1F3A0, 0x1F3CA }, { 0x1F3CF, 0x1F3D3 }, { 0x1F3E0, 0x1F3F0 }, { 0x1F3F4, 0x1F3F4 },
75
+
{ 0x1F3F8, 0x1F43E }, { 0x1F440, 0x1F440 }, { 0x1F442, 0x1F4FC }, { 0x1F4FF, 0x1F53D },
76
+
{ 0x1F54B, 0x1F54E }, { 0x1F550, 0x1F567 }, { 0x1F57A, 0x1F57A }, { 0x1F595, 0x1F596 },
77
+
{ 0x1F5A4, 0x1F5A4 }, { 0x1F5FB, 0x1F64F }, { 0x1F680, 0x1F6C5 }, { 0x1F6CC, 0x1F6CC },
78
+
{ 0x1F6D0, 0x1F6D2 }, { 0x1F6D5, 0x1F6D7 }, { 0x1F6DC, 0x1F6DF }, { 0x1F6EB, 0x1F6EC },
79
+
{ 0x1F6F4, 0x1F6FC }, { 0x1F7E0, 0x1F7EB }, { 0x1F7F0, 0x1F7F0 }, { 0x1F90C, 0x1F93A },
80
+
{ 0x1F93C, 0x1F945 }, { 0x1F947, 0x1F9FF }, { 0x1FA70, 0x1FA7C }, { 0x1FA80, 0x1FA89 },
81
+
{ 0x1FA8F, 0x1FAC6 }, { 0x1FACE, 0x1FADC }, { 0x1FADF, 0x1FAE9 }, { 0x1FAF0, 0x1FAF8 },
82
+
{ 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD },
83
+
};
84
+
85
+
static size_t wideCharTableSize = sizeof(wideCharTable) / sizeof(wideCharTable[0]);
86
+
87
+
static unsigned long combiningCharTable[] = {
88
+
0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
89
+
0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
90
+
0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
91
+
0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
92
+
0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
93
+
0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
94
+
0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
95
+
0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
96
+
0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
97
+
0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
98
+
0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
99
+
0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
100
+
0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
101
+
0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
102
+
0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0591, 0x0592, 0x0593,
103
+
0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B,
104
+
0x059C, 0x059D, 0x059E, 0x059F, 0x05A0, 0x05A1, 0x05A2, 0x05A3,
105
+
0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB,
106
+
0x05AC, 0x05AD, 0x05AE, 0x05AF, 0x05B0, 0x05B1, 0x05B2, 0x05B3,
107
+
0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB,
108
+
0x05BC, 0x05BD, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7,
109
+
0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617,
110
+
0x0618, 0x0619, 0x061A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F,
111
+
0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657,
112
+
0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F,
113
+
0x0670, 0x06D6, 0x06D7, 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC,
114
+
0x06DF, 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E7, 0x06E8,
115
+
0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x0711, 0x0730, 0x0731, 0x0732,
116
+
0x0733, 0x0734, 0x0735, 0x0736, 0x0737, 0x0738, 0x0739, 0x073A,
117
+
0x073B, 0x073C, 0x073D, 0x073E, 0x073F, 0x0740, 0x0741, 0x0742,
118
+
0x0743, 0x0744, 0x0745, 0x0746, 0x0747, 0x0748, 0x0749, 0x074A,
119
+
0x07A6, 0x07A7, 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD,
120
+
0x07AE, 0x07AF, 0x07B0, 0x07EB, 0x07EC, 0x07ED, 0x07EE, 0x07EF,
121
+
0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07FD, 0x0816, 0x0817, 0x0818,
122
+
0x0819, 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, 0x0820, 0x0821,
123
+
0x0822, 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, 0x082A, 0x082B,
124
+
0x082C, 0x082D, 0x0859, 0x085A, 0x085B, 0x0897, 0x0898, 0x0899,
125
+
0x089A, 0x089B, 0x089C, 0x089D, 0x089E, 0x089F, 0x08CA, 0x08CB,
126
+
0x08CC, 0x08CD, 0x08CE, 0x08CF, 0x08D0, 0x08D1, 0x08D2, 0x08D3,
127
+
0x08D4, 0x08D5, 0x08D6, 0x08D7, 0x08D8, 0x08D9, 0x08DA, 0x08DB,
128
+
0x08DC, 0x08DD, 0x08DE, 0x08DF, 0x08E0, 0x08E1, 0x08E3, 0x08E4,
129
+
0x08E5, 0x08E6, 0x08E7, 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC,
130
+
0x08ED, 0x08EE, 0x08EF, 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4,
131
+
0x08F5, 0x08F6, 0x08F7, 0x08F8, 0x08F9, 0x08FA, 0x08FB, 0x08FC,
132
+
0x08FD, 0x08FE, 0x08FF, 0x0900, 0x0901, 0x0902, 0x093A, 0x093C,
133
+
0x0941, 0x0942, 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, 0x0948,
134
+
0x094D, 0x0951, 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957,
135
+
0x0962, 0x0963, 0x0981, 0x09BC, 0x09C1, 0x09C2, 0x09C3, 0x09C4,
136
+
0x09CD, 0x09E2, 0x09E3, 0x09FE, 0x0A01, 0x0A02, 0x0A3C, 0x0A41,
137
+
0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A51, 0x0A70,
138
+
0x0A71, 0x0A75, 0x0A81, 0x0A82, 0x0ABC, 0x0AC1, 0x0AC2, 0x0AC3,
139
+
0x0AC4, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0AE2, 0x0AE3, 0x0AFA,
140
+
0x0AFB, 0x0AFC, 0x0AFD, 0x0AFE, 0x0AFF, 0x0B01, 0x0B3C, 0x0B3F,
141
+
0x0B41, 0x0B42, 0x0B43, 0x0B44, 0x0B4D, 0x0B55, 0x0B56, 0x0B62,
142
+
0x0B63, 0x0B82, 0x0BC0, 0x0BCD, 0x0C00, 0x0C04, 0x0C3C, 0x0C3E,
143
+
0x0C3F, 0x0C40, 0x0C46, 0x0C47, 0x0C48, 0x0C4A, 0x0C4B, 0x0C4C,
144
+
0x0C4D, 0x0C55, 0x0C56, 0x0C62, 0x0C63, 0x0C81, 0x0CBC, 0x0CBF,
145
+
0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D00, 0x0D01, 0x0D3B,
146
+
0x0D3C, 0x0D41, 0x0D42, 0x0D43, 0x0D44, 0x0D4D, 0x0D62, 0x0D63,
147
+
0x0D81, 0x0DCA, 0x0DD2, 0x0DD3, 0x0DD4, 0x0DD6, 0x0E31, 0x0E34,
148
+
0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0E47, 0x0E48,
149
+
0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0EB1, 0x0EB4,
150
+
0x0EB5, 0x0EB6, 0x0EB7, 0x0EB8, 0x0EB9, 0x0EBA, 0x0EBB, 0x0EBC,
151
+
0x0EC8, 0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, 0x0ECD, 0x0ECE, 0x0F18,
152
+
0x0F19, 0x0F35, 0x0F37, 0x0F39, 0x0F71, 0x0F72, 0x0F73, 0x0F74,
153
+
0x0F75, 0x0F76, 0x0F77, 0x0F78, 0x0F79, 0x0F7A, 0x0F7B, 0x0F7C,
154
+
0x0F7D, 0x0F7E, 0x0F80, 0x0F81, 0x0F82, 0x0F83, 0x0F84, 0x0F86,
155
+
0x0F87, 0x0F8D, 0x0F8E, 0x0F8F, 0x0F90, 0x0F91, 0x0F92, 0x0F93,
156
+
0x0F94, 0x0F95, 0x0F96, 0x0F97, 0x0F99, 0x0F9A, 0x0F9B, 0x0F9C,
157
+
0x0F9D, 0x0F9E, 0x0F9F, 0x0FA0, 0x0FA1, 0x0FA2, 0x0FA3, 0x0FA4,
158
+
0x0FA5, 0x0FA6, 0x0FA7, 0x0FA8, 0x0FA9, 0x0FAA, 0x0FAB, 0x0FAC,
159
+
0x0FAD, 0x0FAE, 0x0FAF, 0x0FB0, 0x0FB1, 0x0FB2, 0x0FB3, 0x0FB4,
160
+
0x0FB5, 0x0FB6, 0x0FB7, 0x0FB8, 0x0FB9, 0x0FBA, 0x0FBB, 0x0FBC,
161
+
0x0FC6, 0x102D, 0x102E, 0x102F, 0x1030, 0x1032, 0x1033, 0x1034,
162
+
0x1035, 0x1036, 0x1037, 0x1039, 0x103A, 0x103D, 0x103E, 0x1058,
163
+
0x1059, 0x105E, 0x105F, 0x1060, 0x1071, 0x1072, 0x1073, 0x1074,
164
+
0x1082, 0x1085, 0x1086, 0x108D, 0x109D, 0x135D, 0x135E, 0x135F,
165
+
0x1712, 0x1713, 0x1714, 0x1732, 0x1733, 0x1752, 0x1753, 0x1772,
166
+
0x1773, 0x17B4, 0x17B5, 0x17B7, 0x17B8, 0x17B9, 0x17BA, 0x17BB,
167
+
0x17BC, 0x17BD, 0x17C6, 0x17C9, 0x17CA, 0x17CB, 0x17CC, 0x17CD,
168
+
0x17CE, 0x17CF, 0x17D0, 0x17D1, 0x17D2, 0x17D3, 0x17DD, 0x180B,
169
+
0x180C, 0x180D, 0x180F, 0x1885, 0x1886, 0x18A9, 0x1920, 0x1921,
170
+
0x1922, 0x1927, 0x1928, 0x1932, 0x1939, 0x193A, 0x193B, 0x1A17,
171
+
0x1A18, 0x1A1B, 0x1A56, 0x1A58, 0x1A59, 0x1A5A, 0x1A5B, 0x1A5C,
172
+
0x1A5D, 0x1A5E, 0x1A60, 0x1A62, 0x1A65, 0x1A66, 0x1A67, 0x1A68,
173
+
0x1A69, 0x1A6A, 0x1A6B, 0x1A6C, 0x1A73, 0x1A74, 0x1A75, 0x1A76,
174
+
0x1A77, 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, 0x1A7F, 0x1AB0,
175
+
0x1AB1, 0x1AB2, 0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, 0x1AB7, 0x1AB8,
176
+
0x1AB9, 0x1ABA, 0x1ABB, 0x1ABC, 0x1ABD, 0x1ABF, 0x1AC0, 0x1AC1,
177
+
0x1AC2, 0x1AC3, 0x1AC4, 0x1AC5, 0x1AC6, 0x1AC7, 0x1AC8, 0x1AC9,
178
+
0x1ACA, 0x1ACB, 0x1ACC, 0x1ACD, 0x1ACE, 0x1B00, 0x1B01, 0x1B02,
179
+
0x1B03, 0x1B34, 0x1B36, 0x1B37, 0x1B38, 0x1B39, 0x1B3A, 0x1B3C,
180
+
0x1B42, 0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71,
181
+
0x1B72, 0x1B73, 0x1B80, 0x1B81, 0x1BA2, 0x1BA3, 0x1BA4, 0x1BA5,
182
+
0x1BA8, 0x1BA9, 0x1BAB, 0x1BAC, 0x1BAD, 0x1BE6, 0x1BE8, 0x1BE9,
183
+
0x1BED, 0x1BEF, 0x1BF0, 0x1BF1, 0x1C2C, 0x1C2D, 0x1C2E, 0x1C2F,
184
+
0x1C30, 0x1C31, 0x1C32, 0x1C33, 0x1C36, 0x1C37, 0x1CD0, 0x1CD1,
185
+
0x1CD2, 0x1CD4, 0x1CD5, 0x1CD6, 0x1CD7, 0x1CD8, 0x1CD9, 0x1CDA,
186
+
0x1CDB, 0x1CDC, 0x1CDD, 0x1CDE, 0x1CDF, 0x1CE0, 0x1CE2, 0x1CE3,
187
+
0x1CE4, 0x1CE5, 0x1CE6, 0x1CE7, 0x1CE8, 0x1CED, 0x1CF4, 0x1CF8,
188
+
0x1CF9, 0x1DC0, 0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, 0x1DC5, 0x1DC6,
189
+
0x1DC7, 0x1DC8, 0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, 0x1DCD, 0x1DCE,
190
+
0x1DCF, 0x1DD0, 0x1DD1, 0x1DD2, 0x1DD3, 0x1DD4, 0x1DD5, 0x1DD6,
191
+
0x1DD7, 0x1DD8, 0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, 0x1DDD, 0x1DDE,
192
+
0x1DDF, 0x1DE0, 0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, 0x1DE5, 0x1DE6,
193
+
0x1DE7, 0x1DE8, 0x1DE9, 0x1DEA, 0x1DEB, 0x1DEC, 0x1DED, 0x1DEE,
194
+
0x1DEF, 0x1DF0, 0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, 0x1DF5, 0x1DF6,
195
+
0x1DF7, 0x1DF8, 0x1DF9, 0x1DFA, 0x1DFB, 0x1DFC, 0x1DFD, 0x1DFE,
196
+
0x1DFF, 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6,
197
+
0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20E1, 0x20E5,
198
+
0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED,
199
+
0x20EE, 0x20EF, 0x20F0, 0x2CEF, 0x2CF0, 0x2CF1, 0x2D7F, 0x2DE0,
200
+
0x2DE1, 0x2DE2, 0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6, 0x2DE7, 0x2DE8,
201
+
0x2DE9, 0x2DEA, 0x2DEB, 0x2DEC, 0x2DED, 0x2DEE, 0x2DEF, 0x2DF0,
202
+
0x2DF1, 0x2DF2, 0x2DF3, 0x2DF4, 0x2DF5, 0x2DF6, 0x2DF7, 0x2DF8,
203
+
0x2DF9, 0x2DFA, 0x2DFB, 0x2DFC, 0x2DFD, 0x2DFE, 0x2DFF, 0x302A,
204
+
0x302B, 0x302C, 0x302D, 0x3099, 0x309A, 0xA66F, 0xA674, 0xA675,
205
+
0xA676, 0xA677, 0xA678, 0xA679, 0xA67A, 0xA67B, 0xA67C, 0xA67D,
206
+
0xA69E, 0xA69F, 0xA6F0, 0xA6F1, 0xA802, 0xA806, 0xA80B, 0xA825,
207
+
0xA826, 0xA82C, 0xA8C4, 0xA8C5, 0xA8E0, 0xA8E1, 0xA8E2, 0xA8E3,
208
+
0xA8E4, 0xA8E5, 0xA8E6, 0xA8E7, 0xA8E8, 0xA8E9, 0xA8EA, 0xA8EB,
209
+
0xA8EC, 0xA8ED, 0xA8EE, 0xA8EF, 0xA8F0, 0xA8F1, 0xA8FF, 0xA926,
210
+
0xA927, 0xA928, 0xA929, 0xA92A, 0xA92B, 0xA92C, 0xA92D, 0xA947,
211
+
0xA948, 0xA949, 0xA94A, 0xA94B, 0xA94C, 0xA94D, 0xA94E, 0xA94F,
212
+
0xA950, 0xA951, 0xA980, 0xA981, 0xA982, 0xA9B3, 0xA9B6, 0xA9B7,
213
+
0xA9B8, 0xA9B9, 0xA9BC, 0xA9BD, 0xA9E5, 0xAA29, 0xAA2A, 0xAA2B,
214
+
0xAA2C, 0xAA2D, 0xAA2E, 0xAA31, 0xAA32, 0xAA35, 0xAA36, 0xAA43,
215
+
0xAA4C, 0xAA7C, 0xAAB0, 0xAAB2, 0xAAB3, 0xAAB4, 0xAAB7, 0xAAB8,
216
+
0xAABE, 0xAABF, 0xAAC1, 0xAAEC, 0xAAED, 0xAAF6, 0xABE5, 0xABE8,
217
+
0xABED, 0xFB1E, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05,
218
+
0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D,
219
+
0xFE0E, 0xFE0F, 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25,
220
+
0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D,
221
+
0xFE2E, 0xFE2F, 0x101FD, 0x102E0, 0x10376, 0x10377, 0x10378, 0x10379,
222
+
0x1037A, 0x10A01, 0x10A02, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A0D,
223
+
0x10A0E, 0x10A0F, 0x10A38, 0x10A39, 0x10A3A, 0x10A3F, 0x10AE5, 0x10AE6,
224
+
0x10D24, 0x10D25, 0x10D26, 0x10D27, 0x10D69, 0x10D6A, 0x10D6B, 0x10D6C,
225
+
0x10D6D, 0x10EAB, 0x10EAC, 0x10EFC, 0x10EFD, 0x10EFE, 0x10EFF, 0x10F46,
226
+
0x10F47, 0x10F48, 0x10F49, 0x10F4A, 0x10F4B, 0x10F4C, 0x10F4D, 0x10F4E,
227
+
0x10F4F, 0x10F50, 0x10F82, 0x10F83, 0x10F84, 0x10F85, 0x11001, 0x11038,
228
+
0x11039, 0x1103A, 0x1103B, 0x1103C, 0x1103D, 0x1103E, 0x1103F, 0x11040,
229
+
0x11041, 0x11042, 0x11043, 0x11044, 0x11045, 0x11046, 0x11070, 0x11073,
230
+
0x11074, 0x1107F, 0x11080, 0x11081, 0x110B3, 0x110B4, 0x110B5, 0x110B6,
231
+
0x110B9, 0x110BA, 0x110C2, 0x11100, 0x11101, 0x11102, 0x11127, 0x11128,
232
+
0x11129, 0x1112A, 0x1112B, 0x1112D, 0x1112E, 0x1112F, 0x11130, 0x11131,
233
+
0x11132, 0x11133, 0x11134, 0x11173, 0x11180, 0x11181, 0x111B6, 0x111B7,
234
+
0x111B8, 0x111B9, 0x111BA, 0x111BB, 0x111BC, 0x111BD, 0x111BE, 0x111C9,
235
+
0x111CA, 0x111CB, 0x111CC, 0x111CF, 0x1122F, 0x11230, 0x11231, 0x11234,
236
+
0x11236, 0x11237, 0x1123E, 0x11241, 0x112DF, 0x112E3, 0x112E4, 0x112E5,
237
+
0x112E6, 0x112E7, 0x112E8, 0x112E9, 0x112EA, 0x11300, 0x11301, 0x1133B,
238
+
0x1133C, 0x11340, 0x11366, 0x11367, 0x11368, 0x11369, 0x1136A, 0x1136B,
239
+
0x1136C, 0x11370, 0x11371, 0x11372, 0x11373, 0x11374, 0x113BB, 0x113BC,
240
+
0x113BD, 0x113BE, 0x113BF, 0x113C0, 0x113CE, 0x113D0, 0x113D2, 0x113E1,
241
+
0x113E2, 0x11438, 0x11439, 0x1143A, 0x1143B, 0x1143C, 0x1143D, 0x1143E,
242
+
0x1143F, 0x11442, 0x11443, 0x11444, 0x11446, 0x1145E, 0x114B3, 0x114B4,
243
+
0x114B5, 0x114B6, 0x114B7, 0x114B8, 0x114BA, 0x114BF, 0x114C0, 0x114C2,
244
+
0x114C3, 0x115B2, 0x115B3, 0x115B4, 0x115B5, 0x115BC, 0x115BD, 0x115BF,
245
+
0x115C0, 0x115DC, 0x115DD, 0x11633, 0x11634, 0x11635, 0x11636, 0x11637,
246
+
0x11638, 0x11639, 0x1163A, 0x1163D, 0x1163F, 0x11640, 0x116AB, 0x116AD,
247
+
0x116B0, 0x116B1, 0x116B2, 0x116B3, 0x116B4, 0x116B5, 0x116B7, 0x1171D,
248
+
0x1171F, 0x11722, 0x11723, 0x11724, 0x11725, 0x11727, 0x11728, 0x11729,
249
+
0x1172A, 0x1172B, 0x1182F, 0x11830, 0x11831, 0x11832, 0x11833, 0x11834,
250
+
0x11835, 0x11836, 0x11837, 0x11839, 0x1183A, 0x1193B, 0x1193C, 0x1193E,
251
+
0x11943, 0x119D4, 0x119D5, 0x119D6, 0x119D7, 0x119DA, 0x119DB, 0x119E0,
252
+
0x11A01, 0x11A02, 0x11A03, 0x11A04, 0x11A05, 0x11A06, 0x11A07, 0x11A08,
253
+
0x11A09, 0x11A0A, 0x11A33, 0x11A34, 0x11A35, 0x11A36, 0x11A37, 0x11A38,
254
+
0x11A3B, 0x11A3C, 0x11A3D, 0x11A3E, 0x11A47, 0x11A51, 0x11A52, 0x11A53,
255
+
0x11A54, 0x11A55, 0x11A56, 0x11A59, 0x11A5A, 0x11A5B, 0x11A8A, 0x11A8B,
256
+
0x11A8C, 0x11A8D, 0x11A8E, 0x11A8F, 0x11A90, 0x11A91, 0x11A92, 0x11A93,
257
+
0x11A94, 0x11A95, 0x11A96, 0x11A98, 0x11A99, 0x11C30, 0x11C31, 0x11C32,
258
+
0x11C33, 0x11C34, 0x11C35, 0x11C36, 0x11C38, 0x11C39, 0x11C3A, 0x11C3B,
259
+
0x11C3C, 0x11C3D, 0x11C3F, 0x11C92, 0x11C93, 0x11C94, 0x11C95, 0x11C96,
260
+
0x11C97, 0x11C98, 0x11C99, 0x11C9A, 0x11C9B, 0x11C9C, 0x11C9D, 0x11C9E,
261
+
0x11C9F, 0x11CA0, 0x11CA1, 0x11CA2, 0x11CA3, 0x11CA4, 0x11CA5, 0x11CA6,
262
+
0x11CA7, 0x11CAA, 0x11CAB, 0x11CAC, 0x11CAD, 0x11CAE, 0x11CAF, 0x11CB0,
263
+
0x11CB2, 0x11CB3, 0x11CB5, 0x11CB6, 0x11D31, 0x11D32, 0x11D33, 0x11D34,
264
+
0x11D35, 0x11D36, 0x11D3A, 0x11D3C, 0x11D3D, 0x11D3F, 0x11D40, 0x11D41,
265
+
0x11D42, 0x11D43, 0x11D44, 0x11D45, 0x11D47, 0x11D90, 0x11D91, 0x11D95,
266
+
0x11D97, 0x11EF3, 0x11EF4, 0x11F00, 0x11F01, 0x11F36, 0x11F37, 0x11F38,
267
+
0x11F39, 0x11F3A, 0x11F40, 0x11F42, 0x11F5A, 0x13440, 0x13447, 0x13448,
268
+
0x13449, 0x1344A, 0x1344B, 0x1344C, 0x1344D, 0x1344E, 0x1344F, 0x13450,
269
+
0x13451, 0x13452, 0x13453, 0x13454, 0x13455, 0x1611E, 0x1611F, 0x16120,
270
+
0x16121, 0x16122, 0x16123, 0x16124, 0x16125, 0x16126, 0x16127, 0x16128,
271
+
0x16129, 0x1612D, 0x1612E, 0x1612F, 0x16AF0, 0x16AF1, 0x16AF2, 0x16AF3,
272
+
0x16AF4, 0x16B30, 0x16B31, 0x16B32, 0x16B33, 0x16B34, 0x16B35, 0x16B36,
273
+
0x16F4F, 0x16F8F, 0x16F90, 0x16F91, 0x16F92, 0x16FE4, 0x1BC9D, 0x1BC9E,
274
+
0x1CF00, 0x1CF01, 0x1CF02, 0x1CF03, 0x1CF04, 0x1CF05, 0x1CF06, 0x1CF07,
275
+
0x1CF08, 0x1CF09, 0x1CF0A, 0x1CF0B, 0x1CF0C, 0x1CF0D, 0x1CF0E, 0x1CF0F,
276
+
0x1CF10, 0x1CF11, 0x1CF12, 0x1CF13, 0x1CF14, 0x1CF15, 0x1CF16, 0x1CF17,
277
+
0x1CF18, 0x1CF19, 0x1CF1A, 0x1CF1B, 0x1CF1C, 0x1CF1D, 0x1CF1E, 0x1CF1F,
278
+
0x1CF20, 0x1CF21, 0x1CF22, 0x1CF23, 0x1CF24, 0x1CF25, 0x1CF26, 0x1CF27,
279
+
0x1CF28, 0x1CF29, 0x1CF2A, 0x1CF2B, 0x1CF2C, 0x1CF2D, 0x1CF30, 0x1CF31,
280
+
0x1CF32, 0x1CF33, 0x1CF34, 0x1CF35, 0x1CF36, 0x1CF37, 0x1CF38, 0x1CF39,
281
+
0x1CF3A, 0x1CF3B, 0x1CF3C, 0x1CF3D, 0x1CF3E, 0x1CF3F, 0x1CF40, 0x1CF41,
282
+
0x1CF42, 0x1CF43, 0x1CF44, 0x1CF45, 0x1CF46, 0x1D167, 0x1D168, 0x1D169,
283
+
0x1D17B, 0x1D17C, 0x1D17D, 0x1D17E, 0x1D17F, 0x1D180, 0x1D181, 0x1D182,
284
+
0x1D185, 0x1D186, 0x1D187, 0x1D188, 0x1D189, 0x1D18A, 0x1D18B, 0x1D1AA,
285
+
0x1D1AB, 0x1D1AC, 0x1D1AD, 0x1D242, 0x1D243, 0x1D244, 0x1DA00, 0x1DA01,
286
+
0x1DA02, 0x1DA03, 0x1DA04, 0x1DA05, 0x1DA06, 0x1DA07, 0x1DA08, 0x1DA09,
287
+
0x1DA0A, 0x1DA0B, 0x1DA0C, 0x1DA0D, 0x1DA0E, 0x1DA0F, 0x1DA10, 0x1DA11,
288
+
0x1DA12, 0x1DA13, 0x1DA14, 0x1DA15, 0x1DA16, 0x1DA17, 0x1DA18, 0x1DA19,
289
+
0x1DA1A, 0x1DA1B, 0x1DA1C, 0x1DA1D, 0x1DA1E, 0x1DA1F, 0x1DA20, 0x1DA21,
290
+
0x1DA22, 0x1DA23, 0x1DA24, 0x1DA25, 0x1DA26, 0x1DA27, 0x1DA28, 0x1DA29,
291
+
0x1DA2A, 0x1DA2B, 0x1DA2C, 0x1DA2D, 0x1DA2E, 0x1DA2F, 0x1DA30, 0x1DA31,
292
+
0x1DA32, 0x1DA33, 0x1DA34, 0x1DA35, 0x1DA36, 0x1DA3B, 0x1DA3C, 0x1DA3D,
293
+
0x1DA3E, 0x1DA3F, 0x1DA40, 0x1DA41, 0x1DA42, 0x1DA43, 0x1DA44, 0x1DA45,
294
+
0x1DA46, 0x1DA47, 0x1DA48, 0x1DA49, 0x1DA4A, 0x1DA4B, 0x1DA4C, 0x1DA4D,
295
+
0x1DA4E, 0x1DA4F, 0x1DA50, 0x1DA51, 0x1DA52, 0x1DA53, 0x1DA54, 0x1DA55,
296
+
0x1DA56, 0x1DA57, 0x1DA58, 0x1DA59, 0x1DA5A, 0x1DA5B, 0x1DA5C, 0x1DA5D,
297
+
0x1DA5E, 0x1DA5F, 0x1DA60, 0x1DA61, 0x1DA62, 0x1DA63, 0x1DA64, 0x1DA65,
298
+
0x1DA66, 0x1DA67, 0x1DA68, 0x1DA69, 0x1DA6A, 0x1DA6B, 0x1DA6C, 0x1DA75,
299
+
0x1DA84, 0x1DA9B, 0x1DA9C, 0x1DA9D, 0x1DA9E, 0x1DA9F, 0x1DAA1, 0x1DAA2,
300
+
0x1DAA3, 0x1DAA4, 0x1DAA5, 0x1DAA6, 0x1DAA7, 0x1DAA8, 0x1DAA9, 0x1DAAA,
301
+
0x1DAAB, 0x1DAAC, 0x1DAAD, 0x1DAAE, 0x1DAAF, 0x1E000, 0x1E001, 0x1E002,
302
+
0x1E003, 0x1E004, 0x1E005, 0x1E006, 0x1E008, 0x1E009, 0x1E00A, 0x1E00B,
303
+
0x1E00C, 0x1E00D, 0x1E00E, 0x1E00F, 0x1E010, 0x1E011, 0x1E012, 0x1E013,
304
+
0x1E014, 0x1E015, 0x1E016, 0x1E017, 0x1E018, 0x1E01B, 0x1E01C, 0x1E01D,
305
+
0x1E01E, 0x1E01F, 0x1E020, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E027,
306
+
0x1E028, 0x1E029, 0x1E02A, 0x1E08F, 0x1E130, 0x1E131, 0x1E132, 0x1E133,
307
+
0x1E134, 0x1E135, 0x1E136, 0x1E2AE, 0x1E2EC, 0x1E2ED, 0x1E2EE, 0x1E2EF,
308
+
0x1E4EC, 0x1E4ED, 0x1E4EE, 0x1E4EF, 0x1E5EE, 0x1E5EF, 0x1E8D0, 0x1E8D1,
309
+
0x1E8D2, 0x1E8D3, 0x1E8D4, 0x1E8D5, 0x1E8D6, 0x1E944, 0x1E945, 0x1E946,
310
+
0x1E947, 0x1E948, 0x1E949, 0x1E94A, 0xE0100, 0xE0101, 0xE0102, 0xE0103,
311
+
0xE0104, 0xE0105, 0xE0106, 0xE0107, 0xE0108, 0xE0109, 0xE010A, 0xE010B,
312
+
0xE010C, 0xE010D, 0xE010E, 0xE010F, 0xE0110, 0xE0111, 0xE0112, 0xE0113,
313
+
0xE0114, 0xE0115, 0xE0116, 0xE0117, 0xE0118, 0xE0119, 0xE011A, 0xE011B,
314
+
0xE011C, 0xE011D, 0xE011E, 0xE011F, 0xE0120, 0xE0121, 0xE0122, 0xE0123,
315
+
0xE0124, 0xE0125, 0xE0126, 0xE0127, 0xE0128, 0xE0129, 0xE012A, 0xE012B,
316
+
0xE012C, 0xE012D, 0xE012E, 0xE012F, 0xE0130, 0xE0131, 0xE0132, 0xE0133,
317
+
0xE0134, 0xE0135, 0xE0136, 0xE0137, 0xE0138, 0xE0139, 0xE013A, 0xE013B,
318
+
0xE013C, 0xE013D, 0xE013E, 0xE013F, 0xE0140, 0xE0141, 0xE0142, 0xE0143,
319
+
0xE0144, 0xE0145, 0xE0146, 0xE0147, 0xE0148, 0xE0149, 0xE014A, 0xE014B,
320
+
0xE014C, 0xE014D, 0xE014E, 0xE014F, 0xE0150, 0xE0151, 0xE0152, 0xE0153,
321
+
0xE0154, 0xE0155, 0xE0156, 0xE0157, 0xE0158, 0xE0159, 0xE015A, 0xE015B,
322
+
0xE015C, 0xE015D, 0xE015E, 0xE015F, 0xE0160, 0xE0161, 0xE0162, 0xE0163,
323
+
0xE0164, 0xE0165, 0xE0166, 0xE0167, 0xE0168, 0xE0169, 0xE016A, 0xE016B,
324
+
0xE016C, 0xE016D, 0xE016E, 0xE016F, 0xE0170, 0xE0171, 0xE0172, 0xE0173,
325
+
0xE0174, 0xE0175, 0xE0176, 0xE0177, 0xE0178, 0xE0179, 0xE017A, 0xE017B,
326
+
0xE017C, 0xE017D, 0xE017E, 0xE017F, 0xE0180, 0xE0181, 0xE0182, 0xE0183,
327
+
0xE0184, 0xE0185, 0xE0186, 0xE0187, 0xE0188, 0xE0189, 0xE018A, 0xE018B,
328
+
0xE018C, 0xE018D, 0xE018E, 0xE018F, 0xE0190, 0xE0191, 0xE0192, 0xE0193,
329
+
0xE0194, 0xE0195, 0xE0196, 0xE0197, 0xE0198, 0xE0199, 0xE019A, 0xE019B,
330
+
0xE019C, 0xE019D, 0xE019E, 0xE019F, 0xE01A0, 0xE01A1, 0xE01A2, 0xE01A3,
331
+
0xE01A4, 0xE01A5, 0xE01A6, 0xE01A7, 0xE01A8, 0xE01A9, 0xE01AA, 0xE01AB,
332
+
0xE01AC, 0xE01AD, 0xE01AE, 0xE01AF, 0xE01B0, 0xE01B1, 0xE01B2, 0xE01B3,
333
+
0xE01B4, 0xE01B5, 0xE01B6, 0xE01B7, 0xE01B8, 0xE01B9, 0xE01BA, 0xE01BB,
334
+
0xE01BC, 0xE01BD, 0xE01BE, 0xE01BF, 0xE01C0, 0xE01C1, 0xE01C2, 0xE01C3,
335
+
0xE01C4, 0xE01C5, 0xE01C6, 0xE01C7, 0xE01C8, 0xE01C9, 0xE01CA, 0xE01CB,
336
+
0xE01CC, 0xE01CD, 0xE01CE, 0xE01CF, 0xE01D0, 0xE01D1, 0xE01D2, 0xE01D3,
337
+
0xE01D4, 0xE01D5, 0xE01D6, 0xE01D7, 0xE01D8, 0xE01D9, 0xE01DA, 0xE01DB,
338
+
0xE01DC, 0xE01DD, 0xE01DE, 0xE01DF, 0xE01E0, 0xE01E1, 0xE01E2, 0xE01E3,
339
+
0xE01E4, 0xE01E5, 0xE01E6, 0xE01E7, 0xE01E8, 0xE01E9, 0xE01EA, 0xE01EB,
340
+
0xE01EC, 0xE01ED, 0xE01EE, 0xE01EF,
341
+
};
342
+
343
+
static unsigned long combiningCharTableSize = sizeof(combiningCharTable) / sizeof(combiningCharTable[0]);
344
+
345
+
/* Check if the code is a wide character
346
+
*/
347
+
static int isWideChar(unsigned long cp) {
348
+
size_t i;
349
+
for (i = 0; i < wideCharTableSize; i++)
350
+
if (wideCharTable[i][0] <= cp && cp <= wideCharTable[i][1]) return 1;
351
+
return 0;
352
+
}
353
+
354
+
/* Check if the code is a combining character
355
+
*/
356
+
static int isCombiningChar(unsigned long cp) {
357
+
size_t i;
358
+
for (i = 0; i < combiningCharTableSize; i++)
359
+
if (combiningCharTable[i] == cp) return 1;
360
+
return 0;
361
+
}
362
+
363
+
/* Get length of previous UTF8 character
364
+
*/
365
+
static size_t prevUtf8CharLen(const char* buf, int pos) {
366
+
int end = pos--;
367
+
while (pos >= 0 && ((unsigned char)buf[pos] & 0xC0) == 0x80)
368
+
pos--;
369
+
return end - pos;
370
+
}
371
+
372
+
/* Convert UTF8 to Unicode code point
373
+
*/
374
+
static size_t utf8BytesToCodePoint(const char* buf, size_t len, int* cp) {
375
+
if (len) {
376
+
unsigned char byte = buf[0];
377
+
if ((byte & 0x80) == 0) {
378
+
*cp = byte;
379
+
return 1;
380
+
} else if ((byte & 0xE0) == 0xC0) {
381
+
if (len >= 2) {
382
+
*cp = (((unsigned long)(buf[0] & 0x1F)) << 6) |
383
+
((unsigned long)(buf[1] & 0x3F));
384
+
return 2;
385
+
}
386
+
} else if ((byte & 0xF0) == 0xE0) {
387
+
if (len >= 3) {
388
+
*cp = (((unsigned long)(buf[0] & 0x0F)) << 12) |
389
+
(((unsigned long)(buf[1] & 0x3F)) << 6) |
390
+
((unsigned long)(buf[2] & 0x3F));
391
+
return 3;
392
+
}
393
+
} else if ((byte & 0xF8) == 0xF0) {
394
+
if (len >= 4) {
395
+
*cp = (((unsigned long)(buf[0] & 0x07)) << 18) |
396
+
(((unsigned long)(buf[1] & 0x3F)) << 12) |
397
+
(((unsigned long)(buf[2] & 0x3F)) << 6) |
398
+
((unsigned long)(buf[3] & 0x3F));
399
+
return 4;
400
+
}
401
+
}
402
+
}
403
+
return 0;
404
+
}
405
+
406
+
/* Get length of next grapheme
407
+
*/
408
+
size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) {
409
+
size_t beg = pos;
410
+
int cp;
411
+
size_t len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp);
412
+
if (isCombiningChar(cp)) {
413
+
/* NOTREACHED */
414
+
return 0;
415
+
}
416
+
if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1;
417
+
pos += len;
418
+
while (pos < buf_len) {
419
+
int cp;
420
+
len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp);
421
+
if (!isCombiningChar(cp)) return pos - beg;
422
+
pos += len;
423
+
}
424
+
return pos - beg;
425
+
}
426
+
427
+
/* Get length of previous grapheme
428
+
*/
429
+
size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) {
430
+
UNUSED(buf_len);
431
+
size_t end = pos;
432
+
while (pos > 0) {
433
+
size_t len = prevUtf8CharLen(buf, pos);
434
+
pos -= len;
435
+
int cp;
436
+
utf8BytesToCodePoint(buf + pos, len, &cp);
437
+
if (!isCombiningChar(cp)) {
438
+
if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1;
439
+
return end - pos;
440
+
}
441
+
}
442
+
/* NOTREACHED */
443
+
return 0;
444
+
}
445
+
446
+
/* Read a Unicode from file.
447
+
*/
448
+
size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp) {
449
+
if (buf_len < 1) return -1;
450
+
size_t nread = read(fd,&buf[0],1);
451
+
if (nread <= 0) return nread;
452
+
453
+
unsigned char byte = buf[0];
454
+
if ((byte & 0x80) == 0) {
455
+
;
456
+
} else if ((byte & 0xE0) == 0xC0) {
457
+
if (buf_len < 2) return -1;
458
+
nread = read(fd,&buf[1],1);
459
+
if (nread <= 0) return nread;
460
+
} else if ((byte & 0xF0) == 0xE0) {
461
+
if (buf_len < 3) return -1;
462
+
nread = read(fd,&buf[1],2);
463
+
if (nread <= 0) return nread;
464
+
} else if ((byte & 0xF8) == 0xF0) {
465
+
if (buf_len < 3) return -1;
466
+
nread = read(fd,&buf[1],3);
467
+
if (nread <= 0) return nread;
468
+
} else {
469
+
return -1;
470
+
}
471
+
472
+
return utf8BytesToCodePoint(buf, buf_len, cp);
473
+
}
+55
vendor/ocaml-linenoise/src/utf8.h
+55
vendor/ocaml-linenoise/src/utf8.h
···
1
+
/* encodings/utf8.h -- VERSION 1.0
2
+
*
3
+
* Guerrilla line editing library against the idea that a line editing lib
4
+
* needs to be 20,000 lines of C code.
5
+
*
6
+
* See linenoise.c for more information.
7
+
*
8
+
* ------------------------------------------------------------------------
9
+
*
10
+
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
11
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
12
+
*
13
+
* All rights reserved.
14
+
*
15
+
* Redistribution and use in source and binary forms, with or without
16
+
* modification, are permitted provided that the following conditions are
17
+
* met:
18
+
*
19
+
* * Redistributions of source code must retain the above copyright
20
+
* notice, this list of conditions and the following disclaimer.
21
+
*
22
+
* * Redistributions in binary form must reproduce the above copyright
23
+
* notice, this list of conditions and the following disclaimer in the
24
+
* documentation and/or other materials provided with the distribution.
25
+
*
26
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
*/
38
+
39
+
#ifndef __LINENOISE_ENCODINGS_UTF8_H
40
+
#define __LINENOISE_ENCODINGS_UTF8_H
41
+
42
+
#ifdef __cplusplus
43
+
extern "C" {
44
+
#endif
45
+
46
+
size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len);
47
+
size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len);
48
+
size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp);
49
+
50
+
#ifdef __cplusplus
51
+
}
52
+
#endif
53
+
54
+
#endif /* __LINENOISE_ENCODINGS_UTF8_H */
55
+