this repo has no description

Initial commit

Patrick Ferris f8371416

+1
.gitignore
···
+
_build
+1
.ocamlformat
···
+
version=0.27.0
+4
README.md
···
+
shelter
+
-------
+
+
+31
cshell.opam
···
+
# This file is generated by dune, edit dune-project instead
+
opam-version: "2.0"
+
synopsis: "A short synopsis"
+
description: "A longer description"
+
maintainer: ["Maintainer Name <maintainer@example.com>"]
+
authors: ["Author Name <author@example.com>"]
+
license: "LICENSE"
+
tags: ["add topics" "to describe" "your" "project"]
+
homepage: "https://github.com/username/reponame"
+
doc: "https://url/to/documentation"
+
bug-reports: "https://github.com/username/reponame/issues"
+
depends: [
+
"dune" {>= "3.17"}
+
"ocaml"
+
"odoc" {with-doc}
+
]
+
build: [
+
["dune" "subst"] {dev}
+
[
+
"dune"
+
"build"
+
"-p"
+
name
+
"-j"
+
jobs
+
"@install"
+
"@runtest" {with-test}
+
"@doc" {with-doc}
+
]
+
]
+
dev-repo: "git+https://github.com/username/reponame.git"
+26
dune-project
···
+
(lang dune 3.17)
+
+
(name cshell)
+
+
(generate_opam_files true)
+
+
(source
+
(github username/reponame))
+
+
(authors "Author Name <author@example.com>")
+
+
(maintainers "Maintainer Name <maintainer@example.com>")
+
+
(license LICENSE)
+
+
(documentation https://url/to/documentation)
+
+
(package
+
(name cshell)
+
(synopsis "A short synopsis")
+
(description "A longer description")
+
(depends ocaml)
+
(tags
+
("add topics" "to describe" your project)))
+
+
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html
+5
src/bin/dune
···
+
(executable
+
(public_name cshell)
+
(package cshell)
+
(name main)
+
(libraries cshell fmt.tty cshell.shelter cshell.passthrough eio void))
+27
src/bin/main.ml
···
+
module History = struct
+
type t = string
+
+
let t = Repr.string
+
let merge = Irmin.Merge.default (Repr.option t)
+
end
+
+
module Pass = Cshell.Make (History) (Cshell_passthrough)
+
module Shelter = Cshell.Make (Shelter.History) (Shelter)
+
+
let home = Unix.getenv "HOME"
+
+
let state_dir fs type' =
+
let path = Eio.Path.(fs / home / ".cache/cshell" / type') in
+
Eio.Path.mkdirs ~exists_ok:true ~perm:0o755 path;
+
path
+
+
let () =
+
Eio_posix.run @@ fun env ->
+
Fmt_tty.setup_std_outputs ();
+
match Sys.argv.(1) with
+
| "shelter" ->
+
let dir = state_dir env#fs "shelter" in
+
Shelter.main env#clock env#process_mgr dir
+
| _ | (exception Invalid_argument _) ->
+
let dir = state_dir env#fs "passthrough" in
+
Pass.main env#clock env#process_mgr dir
+33
src/lib/cshell.ml
···
+
module History = History
+
module Engine = Engine
+
+
module Make (H : History.S) (Engine : Engine.S with type entry = H.t) = struct
+
module Store = Irmin_fs_unix.KV.Make (H)
+
+
let run clock proc store =
+
let store = History.Store ((module Store), store) in
+
Engine.init store;
+
let rec loop store exit_code =
+
let prompt = Engine.prompt exit_code store in
+
match LNoise.linenoise prompt with
+
| None -> ()
+
| Some input -> (
+
let action = Engine.action_of_command input in
+
match Engine.run clock proc store action with
+
| Error (Eio.Process.Child_error exit_code) ->
+
Fmt.epr "%a\n%!" Eio.Process.pp_status exit_code;
+
loop store exit_code
+
| Error (Eio.Process.Executable_not_found m) ->
+
Fmt.epr "cshell: excutable not found %s\n%!" m;
+
loop store (`Exited 127)
+
| Ok store -> loop store (`Exited 0))
+
in
+
loop store (`Exited 0)
+
+
let main clock proc directory =
+
Irmin_fs.run directory @@ fun () ->
+
let conf = Irmin_fs.config (Eio.Path.native_exn directory) in
+
let repo = Store.Repo.v conf in
+
let store = Store.main repo in
+
run clock proc store
+
end
+4
src/lib/dune
···
+
(library
+
(name cshell)
+
(public_name cshell)
+
(libraries irmin-fs.unix eio.unix eio linenoise void repr))
+26
src/lib/engine.ml
···
+
module type S = sig
+
type action
+
(** An action to run *)
+
+
val action : action Repr.t
+
val action_of_command : string -> action
+
+
type entry
+
+
val init : entry History.t -> unit
+
(** [init store] will be called before entering the shell loop. You may wish
+
to setup history completions etc. with LNoise. *)
+
+
val run :
+
_ Eio.Time.clock ->
+
Eio_unix.Process.mgr_ty Eio_unix.Process.mgr ->
+
entry History.t ->
+
action ->
+
(entry History.t, Eio.Process.error) result
+
(** [run history action] runs the action in [history]. Return a new [history]
+
that can be persisted *)
+
+
val prompt : Eio.Process.exit_status -> entry History.t -> string
+
(** [prompt previous_exit_code history] generates a prompt from the current
+
[history] *)
+
end
+17
src/lib/history.ml
···
+
module type S = sig
+
type t
+
(** A single history entry *)
+
+
include Irmin.Contents.S with type t := t
+
end
+
+
type 'entry t =
+
| Store :
+
((module Irmin.S
+
with type t = 'a
+
and type Schema.Branch.t = string
+
and type Schema.Contents.t = 'entry
+
and type Schema.Path.t = string list
+
and type Schema.Path.step = string)
+
* 'a)
+
-> 'entry t
+52
src/lib/passthrough/cshell_passthrough.ml
···
+
open Eio
+
+
type action = Exec of string [@@deriving repr]
+
+
let action = action_t
+
let action_of_command cmd = Exec cmd
+
+
type entry = string [@@derviving repr]
+
+
let () = Fmt.set_style_renderer Format.str_formatter `Ansi_tty
+
+
let prompt _ _ =
+
Fmt.(styled (`Fg `Red) string) Format.str_formatter "cshell> ";
+
Format.flush_str_formatter ()
+
+
let history_key = [ "history" ]
+
let key () = history_key @ [ string_of_float @@ Unix.gettimeofday () ]
+
+
let init (Cshell.History.Store ((module S), store) : entry Cshell.History.t) =
+
match S.list store history_key with
+
| [] -> ()
+
| xs ->
+
let rec loop acc = function
+
| `Contents (v, _meta) :: next -> loop (v :: acc) next
+
| _ :: next -> loop acc next
+
| [] -> List.rev acc
+
in
+
let entries =
+
loop [] (List.map (fun (_, tree) -> S.Tree.to_concrete tree) xs)
+
in
+
List.iter (fun v -> LNoise.history_add v |> ignore) entries
+
+
let run clock proc
+
((Cshell.History.Store ((module S), store) : entry Cshell.History.t) as
+
full_store) (Exec command) =
+
let info () =
+
S.Info.v ~message:"cshell" (Eio.Time.now clock |> Int64.of_float)
+
in
+
let cmd =
+
String.split_on_char ' ' command
+
|> List.filter (fun v -> not (String.equal "" v))
+
in
+
Switch.run @@ fun sw ->
+
try
+
let proc = Eio.Process.spawn ~sw proc cmd in
+
let res = Eio.Process.await proc in
+
if res = `Exited 0 then (
+
S.set_exn ~info store (key ()) command;
+
let _ : (unit, string) result = LNoise.history_add command in
+
Ok full_store)
+
else Error (Eio.Process.Child_error res)
+
with Eio.Exn.Io (Eio.Process.E e, _) -> Error e
+1
src/lib/passthrough/cshell_passthrough.mli
···
+
include Cshell.Engine.S with type entry = string
+6
src/lib/passthrough/dune
···
+
(library
+
(name cshell_passthrough)
+
(public_name cshell.passthrough)
+
(preprocess
+
(pps ppx_repr))
+
(libraries cshell))
+6
src/lib/shelter/dune
···
+
(library
+
(name shelter)
+
(public_name cshell.shelter)
+
(preprocess
+
(pps ppx_repr))
+
(libraries cshell cid void))
+175
src/lib/shelter/shelter.ml
···
+
open Eio
+
+
module Build = struct
+
type cid = Cid.t
+
+
let cid_of_string s =
+
match Cid.of_string s with
+
| Ok v -> v
+
| Error (`Msg m) -> failwith m
+
| Error (`Unsupported _) -> failwith "unsupported cid"
+
+
let cid_t = Repr.map Repr.string cid_of_string Cid.to_string
+
+
type t = Image of string | Build of cid [@@deriving repr]
+
end
+
+
type mode = R | RW [@@deriving repr]
+
+
module History = struct
+
type t = { mode : mode; build : Build.t; args : string list }
+
[@@deriving repr]
+
+
let merge = Irmin.Merge.(default (Repr.option t))
+
end
+
+
type entry = History.t
+
+
type action =
+
| Set_mode of mode
+
| Set_session of string
+
| Exec of string list
+
| Info
+
| Unknown of string list
+
[@@deriving repr]
+
+
let split_and_remove_empty s =
+
String.split_on_char ' ' s |> List.filter (fun v -> not (String.equal "" v))
+
+
let action = action_t
+
+
let shelter_action = function
+
| "set" :: "mode" :: [ "r" ] -> Set_mode R
+
| "set" :: "mode" :: [ "rw" ] -> Set_mode R
+
| "session" :: [ m ] -> Set_session m
+
| [ "info" ] -> Info
+
| other -> Unknown other
+
+
let action_of_command cmd =
+
match split_and_remove_empty cmd with
+
| "@" :: rest -> shelter_action rest
+
| args -> Exec args
+
+
let () = Fmt.set_style_renderer Format.str_formatter `Ansi_tty
+
let history_key = [ "history" ]
+
let key clock = history_key @ [ string_of_float @@ Eio.Time.now clock ]
+
+
let list (Cshell.History.Store ((module S), store) : entry Cshell.History.t) =
+
match S.list store history_key with
+
| [] -> []
+
| xs ->
+
let rec loop acc = function
+
| (s, `Contents (v, _meta)) :: next -> loop ((s, v) :: acc) next
+
| _ :: next -> loop acc next
+
| [] -> List.rev acc
+
in
+
loop [] (List.map (fun (v, tree) -> (v, S.Tree.to_concrete tree)) xs)
+
|> List.stable_sort (fun (s1, _) (s2, _) -> String.compare s1 s2)
+
|> List.rev
+
+
let with_latest ~default s f =
+
match list s with [] -> default () | hd :: _ -> f hd
+
+
let text c = Fmt.(styled (`Fg c) string)
+
+
let sessions (Cshell.History.Store ((module S), store) : entry Cshell.History.t)
+
=
+
S.Branch.list (S.repo store)
+
+
let commit ~message clock
+
(Cshell.History.Store ((module S), store) : entry Cshell.History.t) v =
+
let info () = S.Info.v ~message (Eio.Time.now clock |> Int64.of_float) in
+
S.set_exn ~info store (key clock) v
+
+
let which_branch
+
((Cshell.History.Store ((module S), session) : entry Cshell.History.t) as s)
+
=
+
let branches = sessions s in
+
let repo = S.repo session in
+
let heads = List.map (fun b -> (S.Branch.find repo b, b)) branches in
+
let head = S.Head.find session in
+
List.assoc_opt head heads
+
+
let prompt status
+
((Cshell.History.Store ((module S), _session) : entry Cshell.History.t) as
+
store) =
+
let sesh = Option.value ~default:"main" (which_branch store) in
+
let prompt () =
+
Fmt.(styled (`Fg `Yellow) string) Format.str_formatter "shelter> ";
+
Format.flush_str_formatter ()
+
in
+
let pp_sesh fmt sesh = Fmt.pf fmt "[%a]" (text `Green) sesh in
+
let pp_status fmt = function
+
| `Exited 0 -> Fmt.nop fmt ()
+
| `Exited n -> Fmt.pf fmt "%a " (text `Red) (string_of_int n)
+
| _ -> Fmt.nop fmt ()
+
in
+
let prompt_entry (_, (e : entry)) =
+
Fmt.pf Format.str_formatter "%a%a%a : { mode: %a }> " pp_status status
+
(text `Yellow) "shelter" pp_sesh sesh (text `Red)
+
(if e.mode = R then "r" else "rw");
+
Format.flush_str_formatter ()
+
in
+
with_latest store ~default:prompt prompt_entry
+
+
let init s =
+
List.iter
+
(fun (_, { History.args; _ }) ->
+
LNoise.history_add (String.concat " " args) |> ignore)
+
(list s)
+
+
let run clock proc
+
((Cshell.History.Store ((module S), store) : entry Cshell.History.t) as s) =
+
function
+
| Set_mode mode ->
+
with_latest ~default:(fun _ -> Ok s) s @@ fun (_, entry) ->
+
commit ~message:"mode change" clock s { entry with mode };
+
Ok s
+
| Set_session m ->
+
with_latest ~default:(fun _ -> Ok s) s @@ fun (_, entry) ->
+
let new_store = S.of_branch (S.repo store) m in
+
let new_full_store = Cshell.History.Store ((module S), new_store) in
+
commit ~message:"new session" clock new_full_store entry;
+
Ok new_full_store
+
| Unknown args ->
+
Fmt.epr "%a: %s\n%!" (text `Red) "Unknown Shelter Action"
+
(String.concat " " args);
+
Ok s
+
| Info ->
+
let sessions = sessions s in
+
let sesh = Option.value ~default:"main" (which_branch s) in
+
let history = S.history store in
+
let pp_commit fmt (hash, msg) =
+
Fmt.pf fmt "[%a]: %s" (text `Yellow) hash msg
+
in
+
let commits =
+
S.History.fold_vertex
+
(fun commit acc ->
+
let info = S.Commit.info commit |> S.Info.message in
+
let hash = S.Commit.hash commit |> Repr.to_string S.Hash.t in
+
((String.sub hash 0 7), info) :: acc)
+
history []
+
in
+
Fmt.pr "Sessions: %a\nCurrent: %a\nCommits:@. %a\n%!"
+
Fmt.(list ~sep:(Fmt.any ", ") string)
+
sessions (text `Green) sesh
+
Fmt.(vbox ~indent:2 @@ list pp_commit)
+
commits;
+
Ok s
+
| Exec [] -> Ok s
+
| Exec command -> (
+
Switch.run @@ fun sw ->
+
try
+
let proc = Eio.Process.spawn ~sw proc [ "bash"; "-c"; String.concat " " command ] in
+
let res = Eio.Process.await proc in
+
if res = `Exited 0 then (
+
let entry =
+
History.{ mode = RW; build = Image "TODO"; args = command }
+
in
+
commit ~message:("exec " ^ (String.concat " " command)) clock s entry;
+
let _ : (unit, string) result =
+
LNoise.history_add (String.concat " " command)
+
in
+
Ok s)
+
else Error (Eio.Process.Child_error res)
+
with Eio.Exn.Io (Eio.Process.E e, _) -> Error e)
+14
src/lib/shelter/shelter.mli
···
+
module Build : sig
+
type t = Image of string | Build of Cid.t [@@deriving repr]
+
end
+
+
type mode = R | RW
+
+
module History : sig
+
type t = { mode : mode; build : Build.t; args : string list }
+
[@@deriving repr]
+
+
include Irmin.Contents.S with type t := t
+
end
+
+
include Cshell.Engine.S with type entry = History.t
+3
test/dune
···
+
(test
+
(name test_cshell)
+
(libraries eio void))
test/test_cshell.ml

This is a binary file and will not be displayed.

+1
vendor/dune
···
+
(vendored_dirs *)
+1
vendor/ocaml-linenoise/.github/CODEOWNERS
···
+
* @c-cube
+49
vendor/ocaml-linenoise/.github/workflows/main.yml
···
+
name: build
+
on:
+
push:
+
branches:
+
- main
+
pull_request:
+
jobs:
+
run:
+
name: build
+
strategy:
+
fail-fast: false
+
matrix:
+
os:
+
- ubuntu-latest
+
- macos-latest
+
- macos-13
+
setup-version:
+
- v2
+
- v3
+
ocaml-compiler:
+
- 4.06.x
+
- 4.14.x
+
- 5.1.x
+
exclude:
+
- os: ubuntu-latest
+
setup-version: v2
+
- os: macos-13
+
setup-version: v3
+
- os: macos-latest
+
setup-version: v2
+
- os: macos-latest
+
ocaml-compiler: 4.06.x
+
runs-on: ${{ matrix.os }}
+
steps:
+
- uses: actions/checkout@v4
+
- uses: ocaml/setup-ocaml@v2
+
if: matrix.setup-version == 'v2'
+
with:
+
ocaml-compiler: ${{ matrix.ocaml-compiler }}
+
allow-prerelease-opam: true
+
- uses: ocaml/setup-ocaml@v3
+
if: matrix.setup-version == 'v3'
+
with:
+
ocaml-compiler: ${{ matrix.ocaml-compiler }}
+
allow-prerelease-opam: true
+
- run: opam pin -n .
+
- run: opam install -t . --deps-only
+
- run: opam exec -- dune build --ignore-promoted-rules
+
- run: opam exec -- dune runtest --ignore-promoted-rules
+15
vendor/ocaml-linenoise/.gitignore
···
+
_build
+
*.docdir
+
+
history.txt
+
*.cmi
+
*.cmt
+
*.cmo
+
*.cma
+
*.cmx
+
*.o
+
T
+
setup.data
+
setup.log
+
*.install
+
.merlin
+10
vendor/ocaml-linenoise/CHANGES.md
···
+
# 1.5.1
+
+
- fix a deadlock from 1.5
+
+
# 1.5
+
+
- release runtime lock when calling `lnoise`
+
- fix potential memleaks and use of deprecate parts of
+
the OCaml C API
+
- remove dependency on `result`
+19
vendor/ocaml-linenoise/Makefile
···
+
+
all: build test
+
+
build:
+
@dune build @install
+
+
test:
+
@dune runtest --no-buffer --force
+
+
example:
+
@dune exec examples/show_off.exe
+
+
clean:
+
@dune clean
+
+
doc:
+
@dune build @doc
+
+
.PHONY: all build test example clean doc
+70
vendor/ocaml-linenoise/README.md
···
+
Linenoise in OCaml
+
--------------------
+
+
[![build](https://github.com/ocaml-community/ocaml-linenoise/actions/workflows/main.yml/badge.svg)](https://github.com/ocaml-community/ocaml-linenoise/actions/workflows/main.yml)
+
+
# Benefits
+
1. BSD licensed.
+
2. No system dependencies, no need for `readline` on your machine.
+
3. Related to 2, these bindings are self-contained, the source for
+
`linenoise` is in this repo and compiled all together with the
+
`OCaml`.
+
4. Written in OCaml + C.
+
5. Pretty cool hints feature, see the gif.
+
6. Additional features compared to linenoise, such as history search
+
+
# Installation
+
+
It is easy with `opam`
+
+
```shell
+
$ opam install linenoise
+
```
+
+
See the pretty
+
documentation [here](https://ocaml-community.github.io/ocaml-linenoise/)
+
+
# Example code
+
This example is also included in the repo under examples:
+
+
<p align="center" style='min-width:100%'>
+
<img style='min-width:100%' src='example.gif'/>
+
</p>
+
+
+
```ocaml
+
let rec user_input prompt cb =
+
match LNoise.linenoise prompt with
+
| None -> ()
+
| Some v ->
+
cb v;
+
user_input prompt cb
+
+
let () =
+
(* LNoise.set_multiline true; *)
+
LNoise.set_hints_callback (fun line ->
+
if line <> "git remote add " then None
+
else Some (" <this is the remote name> <this is the remote URL>",
+
LNoise.Yellow,
+
true)
+
);
+
LNoise.history_load ~filename:"history.txt" |> ignore;
+
LNoise.history_set ~max_length:100 |> ignore;
+
LNoise.set_completion_callback begin fun line_so_far ln_completions ->
+
if line_so_far <> "" && line_so_far.[0] = 'h' then
+
["Hey"; "Howard"; "Hughes";"Hocus"]
+
|> List.iter (LNoise.add_completion ln_completions);
+
end;
+
["These are OCaml bindings to linenoise";
+
"get tab completion with <TAB>, type h then hit <TAB>";
+
"type quit to exit gracefully";
+
"By Edgar Aroutiounian\n"]
+
|> List.iter print_endline;
+
(fun from_user ->
+
if from_user = "quit" then exit 0;
+
LNoise.history_add from_user |> ignore;
+
LNoise.history_save ~filename:"history.txt" |> ignore;
+
Printf.sprintf "Got: %s" from_user |> print_endline
+
)
+
|> user_input "test_program> "
+
```
+2
vendor/ocaml-linenoise/dune-project
···
+
(lang dune 1.1)
+
(name linenoise)
vendor/ocaml-linenoise/example.gif

This is a binary file and will not be displayed.

+4
vendor/ocaml-linenoise/examples/dune
···
+
(executable
+
(name show_off)
+
(libraries linenoise)
+
)
+34
vendor/ocaml-linenoise/examples/show_off.ml
···
+
let rec user_input prompt cb =
+
match LNoise.linenoise prompt with
+
| None -> ()
+
| Some v ->
+
cb v;
+
user_input prompt cb
+
+
let () =
+
(* LNoise.set_multiline true; *)
+
LNoise.set_hints_callback (fun line ->
+
if line <> "git remote add " then None
+
else Some (" <this is the remote name> <this is the remote URL>",
+
LNoise.Yellow,
+
true)
+
);
+
LNoise.history_load ~filename:"history.txt" |> ignore;
+
LNoise.history_set ~max_length:100 |> ignore;
+
LNoise.set_completion_callback begin fun line_so_far ln_completions ->
+
if line_so_far <> "" && line_so_far.[0] = 'h' then
+
["Hey"; "Howard"; "Hughes";"Hocus"]
+
|> List.iter (LNoise.add_completion ln_completions);
+
end;
+
["These are OCaml bindings to linenoise";
+
"get tab completion with <TAB>, type h then hit <TAB>";
+
"type quit to exit gracefully";
+
"By Edgar Aroutiounian\n"]
+
|> List.iter print_endline;
+
(fun from_user ->
+
if from_user = "quit" then exit 0;
+
LNoise.history_add from_user |> ignore;
+
LNoise.history_save ~filename:"history.txt" |> ignore;
+
Printf.sprintf "Got: %s" from_user |> print_endline
+
)
+
|> user_input "test_program> "
+20
vendor/ocaml-linenoise/linenoise.opam
···
+
opam-version: "2.0"
+
name: "linenoise"
+
version: "1.5.1"
+
synopsis: "Lightweight readline alternative"
+
maintainer: "Simon Cruanes"
+
authors: [ "Edgar Aroutiounian <edgar.factorial@gmail.com>" "Simon Cruanes" ]
+
license: "BSD-3-clause"
+
homepage: "https://github.com/ocaml-community/ocaml-linenoise"
+
dev-repo: "git+https://github.com/ocaml-community/ocaml-linenoise.git"
+
bug-reports: "https://github.com/ocaml-community/ocaml-linenoise/issues"
+
build: [
+
["dune" "build" "@install" "-p" name "-j" jobs]
+
["dune" "runtest" "-p" name] {with-test}
+
["dune" "build" "@doc" "-p" name] {with-doc}
+
]
+
depends: [
+
"dune" { >= "1.1" }
+
"ocaml" { >= "4.06.0" }
+
"odoc" {with-doc}
+
]
+8
vendor/ocaml-linenoise/src/dune
···
+
+
(library
+
(name linenoise)
+
(public_name linenoise)
+
(modules LNoise)
+
(wrapped false)
+
(flags :standard -warn-error -3)
+
(c_names linenoise_src utf8 linenoise_stubs))
+57
vendor/ocaml-linenoise/src/lNoise.ml
···
+
+
type completions
+
+
external add_completion : completions -> string -> unit = "ml_add_completion"
+
+
external linenoise : string -> string option = "ml_linenoise"
+
+
external history_add_ : string -> int = "ml_history_add"
+
external history_set_ : max_length:int -> int = "ml_history_set_maxlen"
+
external history_save_ : filename:string -> int = "ml_history_save"
+
external history_load_ : filename:string -> int = "ml_history_load"
+
+
external catch_break : bool -> unit = "ml_catch_break"
+
+
external setup_bridges : unit -> unit = "ml_setup_bridges"
+
+
type hint_color = Red | Green | Yellow | Blue | Magenta | Cyan | White
+
+
let completion_cb = ref (fun _ _ -> ())
+
let hints_cb = ref (fun _ -> None)
+
+
let set_completion_callback (f:string->completions->unit) : unit =
+
completion_cb := f;
+
Callback.register "lnoise_completion_cb" f
+
+
let set_hints_callback (f:string -> (string*hint_color*bool) option) : unit =
+
hints_cb := f;
+
Callback.register "lnoise_hints_cb" f
+
+
(* initialization: register [Sys.Break] and enable catch-break *)
+
let () =
+
setup_bridges();
+
set_completion_callback !completion_cb;
+
set_hints_callback !hints_cb;
+
Callback.register_exception "sys_break" Sys.Break;
+
catch_break true
+
+
let history_add h =
+
if history_add_ h = 0 then Error "Couldn't add to history"
+
else Ok ()
+
+
let history_set ~max_length =
+
if history_set_ ~max_length = 0
+
then Error "Couldn't set the max length of history"
+
else Ok ()
+
+
let history_save ~filename =
+
if history_save_ ~filename = 0 then Ok ()
+
else Error "Couldn't save"
+
+
let history_load ~filename =
+
if history_load_ ~filename = 0 then Ok ()
+
else Error "Couldn't load the file"
+
+
external clear_screen : unit -> unit = "ml_clearscreen"
+
external set_multiline : bool -> unit = "ml_set_multiline"
+
external print_keycodes : unit -> unit = "ml_printkeycodes"
+65
vendor/ocaml-linenoise/src/lNoise.mli
···
+
(** OCaml bindings to linenoise, functions that can fail use result
+
type *)
+
+
(** Abstract type of completions, given to your completion callback *)
+
type completions
+
+
(** This function is used by the callback function registered by the
+
user in order to add completion options given the input string
+
when the user typed <TAB>. *)
+
val add_completion : completions -> string -> unit
+
+
(** Register the callback function that is called for upon
+
tab-completion, aka when <TAB> is hit in the terminal *)
+
val set_completion_callback : (string -> completions -> unit) -> unit
+
+
(** The high level function that is the main API of the linenoise
+
library. This function checks if the terminal has basic
+
capabilities, just checking for a blacklist of stupid terminals,
+
and later either calls the line editing function or uses dummy
+
fgets() so that you will be able to type something even in the
+
most desperate of the conditions. *)
+
val linenoise : string -> string option
+
+
(** Add a string to the history *)
+
val history_add : string -> (unit, string) result
+
+
(** Set the maximum length for the history. This function can be
+
called even if there is already some history, the function will
+
make sure to retain just the latest 'len' elements if the new
+
history length value is smaller than the amount of items already
+
inside the history. *)
+
val history_set : max_length:int -> (unit, string) result
+
+
(** Save the history in the specified file *)
+
val history_save : filename:string -> (unit, string) result
+
+
(** Load the history from the specified file. *)
+
val history_load : filename:string -> (unit, string) result
+
+
(** Clear the screen; used to handle CTRL+L *)
+
val clear_screen : unit -> unit
+
+
(** If [true], [ctrl-c] during a call to {!linenoise}
+
will raise [Sys.Break] instead of returning an empty string.
+
@since 1.1 *)
+
val catch_break : bool -> unit
+
+
(** Set if to use or not use the multi line mode. *)
+
val set_multiline : bool -> unit
+
+
(** This special mode is used by linenoise in order to print scan
+
codes on screen for debugging / development purposes. *)
+
val print_keycodes : unit -> unit
+
+
(** What color you want the hints to be. *)
+
type hint_color = Red | Green | Yellow | Blue | Magenta | Cyan | White
+
+
(** Set a hints callback, callback gets a string, aka the line input,
+
and you get a chance to give a hint to the user. Example, imagine
+
if user types git remote add, then you can give a hint of <this is
+
where you add a remote name> <this is where you add the remote's
+
URL>, see animated gif in source repo for clear example. Returned
+
tuple represents the hint message, color, and whether it ought to
+
be bold. *)
+
val set_hints_callback : (string -> (string * hint_color * bool) option) -> unit
+1482
vendor/ocaml-linenoise/src/linenoise_src.c
···
+
/* linenoise.c -- guerrilla line editing library against the idea that a
+
* line editing lib needs to be 20,000 lines of C code.
+
*
+
* You can find the latest source code at:
+
*
+
* http://github.com/antirez/linenoise
+
*
+
* Does a number of crazy assumptions that happen to be true in 99.9999% of
+
* the 2010 UNIX computers around.
+
*
+
* ------------------------------------------------------------------------
+
*
+
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+
*
+
* All rights reserved.
+
*
+
* Redistribution and use in source and binary forms, with or without
+
* modification, are permitted provided that the following conditions are
+
* met:
+
*
+
* * Redistributions of source code must retain the above copyright
+
* notice, this list of conditions and the following disclaimer.
+
*
+
* * Redistributions in binary form must reproduce the above copyright
+
* notice, this list of conditions and the following disclaimer in the
+
* documentation and/or other materials provided with the distribution.
+
*
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
*
+
* ------------------------------------------------------------------------
+
*
+
* References:
+
* - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+
* - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+
*
+
* Todo list:
+
* - Filter bogus Ctrl+<char> combinations.
+
* - Win32 support
+
*
+
* Bloat:
+
* - History search like Ctrl+r in readline?
+
*
+
* List of escape sequences used by this program, we do everything just
+
* with three sequences. In order to be so cheap we may have some
+
* flickering effect with some slow terminal, but the lesser sequences
+
* the more compatible.
+
*
+
* EL (Erase Line)
+
* Sequence: ESC [ n K
+
* Effect: if n is 0 or missing, clear from cursor to end of line
+
* Effect: if n is 1, clear from beginning of line to cursor
+
* Effect: if n is 2, clear entire line
+
*
+
* CUF (CUrsor Forward)
+
* Sequence: ESC [ n C
+
* Effect: moves cursor forward n chars
+
*
+
* CUB (CUrsor Backward)
+
* Sequence: ESC [ n D
+
* Effect: moves cursor backward n chars
+
*
+
* The following is used to get the terminal width if getting
+
* the width with the TIOCGWINSZ ioctl fails
+
*
+
* DSR (Device Status Report)
+
* Sequence: ESC [ 6 n
+
* Effect: reports the current cusor position as ESC [ n ; m R
+
* where n is the row and m is the column
+
*
+
* When multi line mode is enabled, we also use an additional escape
+
* sequence. However multi line editing is disabled by default.
+
*
+
* CUU (Cursor Up)
+
* Sequence: ESC [ n A
+
* Effect: moves cursor up of n chars.
+
*
+
* CUD (Cursor Down)
+
* Sequence: ESC [ n B
+
* Effect: moves cursor down of n chars.
+
*
+
* When linenoiseClearScreen() is called, two additional escape sequences
+
* are used in order to clear the screen and position the cursor at home
+
* position.
+
*
+
* CUP (Cursor position)
+
* Sequence: ESC [ H
+
* Effect: moves the cursor to upper left corner
+
*
+
* ED (Erase display)
+
* Sequence: ESC [ 2 J
+
* Effect: clear the whole screen
+
*
+
*/
+
+
#include <termios.h>
+
#include <unistd.h>
+
#include <stdlib.h>
+
#include <stdio.h>
+
#include <errno.h>
+
#include <string.h>
+
#include <stdlib.h>
+
#include <ctype.h>
+
#include <sys/stat.h>
+
#include <sys/types.h>
+
#include <sys/ioctl.h>
+
#include <unistd.h>
+
#include "linenoise_src.h"
+
+
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+
#define LINENOISE_MAX_LINE 4096
+
#define UNUSED(x) (void)(x)
+
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
+
static linenoiseCompletionCallback *completionCallback = NULL;
+
static linenoiseHintsCallback *hintsCallback = NULL;
+
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
+
static char *linenoiseNoTTY(void);
+
static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags);
+
static void refreshLineWithFlags(struct linenoiseState *l, int flags);
+
+
static struct termios orig_termios; /* In order to restore at exit.*/
+
static int maskmode = 0; /* Show "***" instead of input. For passwords. */
+
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
+
static int mlmode = 0; /* Multi line mode. Default is single line. */
+
static int atexit_registered = 0; /* Register atexit just 1 time. */
+
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+
static int history_len = 0;
+
static char **history = NULL;
+
+
int linenoiseWasInterrupted = 0;
+
+
enum KEY_ACTION{
+
KEY_NULL = 0, /* NULL */
+
CTRL_A = 1, /* Ctrl+a */
+
CTRL_B = 2, /* Ctrl-b */
+
CTRL_C = 3, /* Ctrl-c */
+
CTRL_D = 4, /* Ctrl-d */
+
CTRL_E = 5, /* Ctrl-e */
+
CTRL_F = 6, /* Ctrl-f */
+
CTRL_H = 8, /* Ctrl-h */
+
TAB = 9, /* Tab */
+
CTRL_K = 11, /* Ctrl+k */
+
CTRL_L = 12, /* Ctrl+l */
+
ENTER = 13, /* Enter */
+
CTRL_N = 14, /* Ctrl-n */
+
CTRL_P = 16, /* Ctrl-p */
+
CTRL_T = 20, /* Ctrl-t */
+
CTRL_U = 21, /* Ctrl+u */
+
CTRL_W = 23, /* Ctrl+w */
+
ESC = 27, /* Escape */
+
BACKSPACE = 127 /* Backspace */
+
};
+
+
static void linenoiseAtExit(void);
+
int linenoiseHistoryAdd(const char *line);
+
#define REFRESH_CLEAN (1<<0) // Clean the old prompt from the screen
+
#define REFRESH_WRITE (1<<1) // Rewrite the prompt on the screen.
+
#define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both.
+
static void refreshLine(struct linenoiseState *l);
+
+
/* Debugging macro. */
+
#if 0
+
FILE *lndebug_fp = NULL;
+
#define lndebug(...) \
+
do { \
+
if (lndebug_fp == NULL) { \
+
lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
+
fprintf(lndebug_fp, \
+
"[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
+
(int)l->len,(int)l->pos,(int)l->oldcolpos,plen,rows,rpos, \
+
(int)l->oldrows,old_rows); \
+
} \
+
fprintf(lndebug_fp, ", " __VA_ARGS__); \
+
fflush(lndebug_fp); \
+
} while (0)
+
#else
+
#define lndebug(fmt, ...)
+
#endif
+
+
/* ========================== Encoding functions ============================= */
+
+
/* Get byte length and column length of the previous character */
+
static size_t defaultPrevCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) {
+
UNUSED(buf); UNUSED(buf_len); UNUSED(pos);
+
if (col_len != NULL) *col_len = 1;
+
return 1;
+
}
+
+
/* Get byte length and column length of the next character */
+
static size_t defaultNextCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) {
+
UNUSED(buf); UNUSED(buf_len); UNUSED(pos);
+
if (col_len != NULL) *col_len = 1;
+
return 1;
+
}
+
+
/* Read bytes of the next character */
+
static size_t defaultReadCode(int fd, char *buf, size_t buf_len, int* c) {
+
if (buf_len < 1) return -1;
+
int nread = read(fd,&buf[0],1);
+
if (nread == 1) *c = buf[0];
+
return nread;
+
}
+
+
/* Set default encoding functions */
+
static linenoisePrevCharLen *prevCharLen = defaultPrevCharLen;
+
static linenoiseNextCharLen *nextCharLen = defaultNextCharLen;
+
static linenoiseReadCode *readCode = defaultReadCode;
+
+
/* Set used defined encoding functions */
+
void linenoiseSetEncodingFunctions(
+
linenoisePrevCharLen *prevCharLenFunc,
+
linenoiseNextCharLen *nextCharLenFunc,
+
linenoiseReadCode *readCodeFunc) {
+
prevCharLen = prevCharLenFunc;
+
nextCharLen = nextCharLenFunc;
+
readCode = readCodeFunc;
+
}
+
+
/* Get column length from begining of buffer to current byte position */
+
static size_t columnPos(const char *buf, size_t buf_len, size_t pos) {
+
size_t ret = 0;
+
size_t off = 0;
+
while (off < pos) {
+
size_t col_len;
+
size_t len = nextCharLen(buf,buf_len,off,&col_len);
+
off += len;
+
ret += col_len;
+
}
+
return ret;
+
}
+
+
/* Get column length from begining of buffer to current byte position for multiline mode*/
+
static size_t columnPosForMultiLine(const char *buf, size_t buf_len, size_t pos, size_t cols, size_t ini_pos) {
+
size_t ret = 0;
+
size_t colwid = ini_pos;
+
+
size_t off = 0;
+
while (off < buf_len) {
+
size_t col_len;
+
size_t len = nextCharLen(buf,buf_len,off,&col_len);
+
+
int dif = (int)(colwid + col_len) - (int)cols;
+
if (dif > 0) {
+
ret += dif;
+
colwid = col_len;
+
} else if (dif == 0) {
+
colwid = 0;
+
} else {
+
colwid += col_len;
+
}
+
+
if (off >= pos) break;
+
off += len;
+
ret += col_len;
+
}
+
+
return ret;
+
}
+
+
/* ======================= Low level terminal handling ====================== */
+
+
/* Enable "mask mode". When it is enabled, instead of the input that
+
* the user is typing, the terminal will just display a corresponding
+
* number of asterisks, like "****". This is useful for passwords and other
+
* secrets that should not be displayed. */
+
void linenoiseMaskModeEnable(void) {
+
maskmode = 1;
+
}
+
+
/* Disable mask mode. */
+
void linenoiseMaskModeDisable(void) {
+
maskmode = 0;
+
}
+
+
/* Set if to use or not the multi line mode. */
+
void linenoiseSetMultiLine(int ml) {
+
mlmode = ml;
+
}
+
+
/* Return true if the terminal name is in the list of terminals we know are
+
* not able to understand basic escape sequences. */
+
static int isUnsupportedTerm(void) {
+
char *term = getenv("TERM");
+
int j;
+
+
if (term == NULL) return 0;
+
for (j = 0; unsupported_term[j]; j++)
+
if (!strcasecmp(term,unsupported_term[j])) return 1;
+
return 0;
+
}
+
+
/* Raw mode: 1960 magic shit. */
+
static int enableRawMode(int fd) {
+
struct termios raw;
+
+
if (!isatty(STDIN_FILENO)) goto fatal;
+
if (!atexit_registered) {
+
atexit(linenoiseAtExit);
+
atexit_registered = 1;
+
}
+
if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+
raw = orig_termios; /* modify the original mode */
+
/* input modes: no break, no CR to NL, no parity check, no strip char,
+
* no start/stop output control. */
+
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+
/* output modes - disable post processing */
+
raw.c_oflag &= ~(OPOST);
+
/* control modes - set 8 bit chars */
+
raw.c_cflag |= (CS8);
+
/* local modes - choing off, canonical off, no extended functions,
+
* no signal chars (^Z,^C) */
+
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+
/* control chars - set return condition: min number of bytes and timer.
+
* We want read to return every single byte, without timeout. */
+
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+
/* put terminal in raw mode after flushing */
+
if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
+
rawmode = 1;
+
return 0;
+
+
fatal:
+
errno = ENOTTY;
+
return -1;
+
}
+
+
static void disableRawMode(int fd) {
+
/* Don't even check the return value as it's too late. */
+
if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
+
rawmode = 0;
+
}
+
+
/* Use the ESC [6n escape sequence to query the horizontal cursor position
+
* and return it. On error -1 is returned, on success the position of the
+
* cursor. */
+
static int getCursorPosition(int ifd, int ofd) {
+
char buf[32];
+
int cols, rows;
+
unsigned int i = 0;
+
+
/* Report cursor location */
+
if (write(ofd, "\x1b[6n", 4) != 4) return -1;
+
+
/* Read the response: ESC [ rows ; cols R */
+
while (i < sizeof(buf)-1) {
+
if (read(ifd,buf+i,1) != 1) break;
+
if (buf[i] == 'R') break;
+
i++;
+
}
+
buf[i] = '\0';
+
+
/* Parse it. */
+
if (buf[0] != ESC || buf[1] != '[') return -1;
+
if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
+
return cols;
+
}
+
+
/* Try to get the number of columns in the current terminal, or assume 80
+
* if it fails. */
+
static int getColumns(int ifd, int ofd) {
+
struct winsize ws;
+
+
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+
/* ioctl() failed. Try to query the terminal itself. */
+
int start, cols;
+
+
/* Get the initial position so we can restore it later. */
+
start = getCursorPosition(ifd,ofd);
+
if (start == -1) goto failed;
+
+
/* Go to right margin and get position. */
+
if (write(ofd,"\x1b[999C",6) != 6) goto failed;
+
cols = getCursorPosition(ifd,ofd);
+
if (cols == -1) goto failed;
+
+
/* Restore position. */
+
if (cols > start) {
+
char seq[32];
+
snprintf(seq,32,"\x1b[%dD",cols-start);
+
if (write(ofd,seq,strlen(seq)) == -1) {
+
/* Can't recover... */
+
}
+
}
+
return cols;
+
} else {
+
return ws.ws_col;
+
}
+
+
failed:
+
return 80;
+
}
+
+
/* Clear the screen. Used to handle ctrl+l */
+
void linenoiseClearScreen(void) {
+
if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
+
/* nothing to do, just to avoid warning. */
+
}
+
}
+
+
/* Beep, used for completion when there is nothing to complete or when all
+
* the choices were already shown. */
+
static void linenoiseBeep(void) {
+
fprintf(stderr, "\x7");
+
fflush(stderr);
+
}
+
+
/* ============================== Completion ================================ */
+
+
/* Free a list of completion option populated by linenoiseAddCompletion(). */
+
static void freeCompletions(linenoiseCompletions *lc) {
+
size_t i;
+
for (i = 0; i < lc->len; i++)
+
free(lc->cvec[i]);
+
if (lc->cvec != NULL)
+
free(lc->cvec);
+
}
+
+
/* Called by completeLine() and linenoiseShow() to render the current
+
* edited line with the proposed completion. If the current completion table
+
* is already available, it is passed as second argument, otherwise the
+
* function will use the callback to obtain it.
+
*
+
* Flags are the same as refreshLine*(), that is REFRESH_* macros. */
+
static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags) {
+
/* Obtain the table of completions if the caller didn't provide one. */
+
linenoiseCompletions ctable = { 0, NULL };
+
if (lc == NULL) {
+
completionCallback(ls->buf,&ctable);
+
lc = &ctable;
+
}
+
+
/* Show the edited line with completion if possible, or just refresh. */
+
if (ls->completion_idx < lc->len) {
+
struct linenoiseState saved = *ls;
+
ls->len = ls->pos = strlen(lc->cvec[ls->completion_idx]);
+
ls->buf = lc->cvec[ls->completion_idx];
+
refreshLineWithFlags(ls,flags);
+
ls->len = saved.len;
+
ls->pos = saved.pos;
+
ls->buf = saved.buf;
+
} else {
+
refreshLineWithFlags(ls,flags);
+
}
+
+
/* Free the completions table if needed. */
+
if (lc != &ctable) freeCompletions(&ctable);
+
}
+
+
/* This is an helper function for linenoiseEdit*() and is called when the
+
* user types the <tab> key in order to complete the string currently in the
+
* input.
+
*
+
* The state of the editing is encapsulated into the pointed linenoiseState
+
* structure as described in the structure definition.
+
*
+
* If the function returns non-zero, the caller should handle the
+
* returned value as a byte read from the standard input, and process
+
* it as usually: this basically means that the function may return a byte
+
* read from the termianl but not processed. Otherwise, if zero is returned,
+
* the input was consumed by the completeLine() function to navigate the
+
* possible completions, and the caller should read for the next characters
+
* from stdin. */
+
static int completeLine(struct linenoiseState *ls, int keypressed) {
+
linenoiseCompletions lc = { 0, NULL };
+
int nwritten;
+
char c = keypressed;
+
+
completionCallback(ls->buf,&lc);
+
if (lc.len == 0) {
+
linenoiseBeep();
+
ls->in_completion = 0;
+
} else {
+
switch(c) {
+
case 9: /* tab */
+
if (ls->in_completion == 0) {
+
ls->in_completion = 1;
+
ls->completion_idx = 0;
+
} else {
+
ls->completion_idx = (ls->completion_idx+1) % (lc.len+1);
+
if (ls->completion_idx == lc.len) linenoiseBeep();
+
}
+
c = 0;
+
break;
+
case 27: /* escape */
+
/* Re-show original buffer */
+
if (ls->completion_idx < lc.len) refreshLine(ls);
+
ls->in_completion = 0;
+
c = 0;
+
break;
+
default:
+
/* Update buffer and return */
+
if (ls->completion_idx < lc.len) {
+
nwritten = snprintf(ls->buf,ls->buflen,"%s",
+
lc.cvec[ls->completion_idx]);
+
ls->len = ls->pos = nwritten;
+
}
+
ls->in_completion = 0;
+
break;
+
}
+
+
/* Show completion or original buffer */
+
if (ls->in_completion && ls->completion_idx < lc.len) {
+
refreshLineWithCompletion(ls,&lc,REFRESH_ALL);
+
} else {
+
refreshLine(ls);
+
}
+
}
+
+
freeCompletions(&lc);
+
return c; /* Return last read character */
+
}
+
+
/* Register a callback function to be called for tab-completion. */
+
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
+
completionCallback = fn;
+
}
+
+
/* Register a hits function to be called to show hits to the user at the
+
* right of the prompt. */
+
void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
+
hintsCallback = fn;
+
}
+
+
/* Register a function to free the hints returned by the hints callback
+
* registered with linenoiseSetHintsCallback(). */
+
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
+
freeHintsCallback = fn;
+
}
+
+
/* This function is used by the callback function registered by the user
+
* in order to add completion options given the input string when the
+
* user typed <tab>. See the example.c source code for a very easy to
+
* understand example. */
+
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
+
size_t len = strlen(str);
+
char *copy, **cvec;
+
+
copy = malloc(len+1);
+
if (copy == NULL) return;
+
memcpy(copy,str,len+1);
+
cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+
if (cvec == NULL) {
+
free(copy);
+
return;
+
}
+
lc->cvec = cvec;
+
lc->cvec[lc->len++] = copy;
+
}
+
+
/* =========================== Line editing ================================= */
+
+
/* We define a very simple "append buffer" structure, that is an heap
+
* allocated string where we can append to. This is useful in order to
+
* write all the escape sequences in a buffer and flush them to the standard
+
* output in a single call, to avoid flickering effects. */
+
struct abuf {
+
char *b;
+
int len;
+
};
+
+
static void abInit(struct abuf *ab) {
+
ab->b = NULL;
+
ab->len = 0;
+
}
+
+
static void abAppend(struct abuf *ab, const char *s, int len) {
+
char *new = realloc(ab->b,ab->len+len);
+
+
if (new == NULL) return;
+
memcpy(new+ab->len,s,len);
+
ab->b = new;
+
ab->len += len;
+
}
+
+
static void abFree(struct abuf *ab) {
+
free(ab->b);
+
}
+
+
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
+
* to the right of the prompt. */
+
void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int pcollen) {
+
char seq[64];
+
size_t collen = pcollen+columnPos(l->buf,l->len,l->len);
+
if (hintsCallback && collen < l->cols) {
+
int color = -1, bold = 0;
+
char *hint = hintsCallback(l->buf,&color,&bold);
+
if (hint) {
+
int hintlen = strlen(hint);
+
int hintmaxlen = l->cols-collen;
+
if (hintlen > hintmaxlen) hintlen = hintmaxlen;
+
if (bold == 1 && color == -1) color = 37;
+
if (color != -1 || bold != 0)
+
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
+
else
+
seq[0] = '\0';
+
abAppend(ab,seq,strlen(seq));
+
abAppend(ab,hint,hintlen);
+
if (color != -1 || bold != 0)
+
abAppend(ab,"\033[0m",4);
+
/* Call the function to free the hint returned. */
+
if (freeHintsCallback) freeHintsCallback(hint);
+
}
+
}
+
}
+
+
/* Check if text is an ANSI escape sequence
+
*/
+
static int isAnsiEscape(const char *buf, size_t buf_len, size_t* len) {
+
if (buf_len > 2 && !memcmp("\033[", buf, 2)) {
+
size_t off = 2;
+
while (off < buf_len) {
+
switch (buf[off++]) {
+
case 'A': case 'B': case 'C': case 'D': case 'E':
+
case 'F': case 'G': case 'H': case 'J': case 'K':
+
case 'S': case 'T': case 'f': case 'm':
+
*len = off;
+
return 1;
+
}
+
}
+
}
+
return 0;
+
}
+
+
/* Get column length of prompt text
+
*/
+
static size_t promptTextColumnLen(const char *prompt, size_t plen) {
+
char buf[LINENOISE_MAX_LINE];
+
size_t buf_len = 0;
+
size_t off = 0;
+
while (off < plen) {
+
size_t len;
+
if (isAnsiEscape(prompt + off, plen - off, &len)) {
+
off += len;
+
continue;
+
}
+
buf[buf_len++] = prompt[off++];
+
}
+
return columnPos(buf,buf_len,buf_len);
+
}
+
+
/* Single line low level line refresh.
+
*
+
* Rewrite the currently edited line accordingly to the buffer content,
+
* cursor position, and number of columns of the terminal.
+
*
+
* Flags is REFRESH_* macros. The function can just remove the old
+
* prompt, just write it, or both. */
+
static void refreshSingleLine(struct linenoiseState *l, int flags) {
+
char seq[64];
+
size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt));
+
int fd = l->ofd;
+
char *buf = l->buf;
+
size_t len = l->len;
+
size_t pos = l->pos;
+
struct abuf ab;
+
+
while((pcollen+columnPos(buf,len,pos)) >= l->cols) {
+
int chlen = nextCharLen(buf,len,0,NULL);
+
buf += chlen;
+
len -= chlen;
+
pos -= chlen;
+
}
+
while (pcollen+columnPos(buf,len,len) > l->cols) {
+
len -= prevCharLen(buf,len,len,NULL);
+
}
+
+
abInit(&ab);
+
/* Cursor to left edge */
+
snprintf(seq,sizeof(seq),"\r");
+
abAppend(&ab,seq,strlen(seq));
+
+
if (flags & REFRESH_WRITE) {
+
/* Write the prompt and the current buffer content */
+
abAppend(&ab,l->prompt,strlen(l->prompt));
+
if (maskmode == 1) {
+
while (len--) abAppend(&ab,"*",1);
+
} else {
+
abAppend(&ab,buf,len);
+
}
+
/* Show hits if any. */
+
refreshShowHints(&ab,l,pcollen);
+
}
+
+
/* Erase to right */
+
snprintf(seq,sizeof(seq),"\x1b[0K");
+
abAppend(&ab,seq,strlen(seq));
+
+
if (flags & REFRESH_WRITE) {
+
/* Move cursor to original position. */
+
snprintf(seq,sizeof(seq),"\r\x1b[%dC", (int)(columnPos(buf,len,pos)+pcollen));
+
abAppend(&ab,seq,strlen(seq));
+
}
+
+
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+
abFree(&ab);
+
}
+
+
/* Multi line low level line refresh.
+
*
+
* Rewrite the currently edited line accordingly to the buffer content,
+
* cursor position, and number of columns of the terminal.
+
*
+
* Flags is REFRESH_* macros. The function can just remove the old
+
* prompt, just write it, or both. */
+
static void refreshMultiLine(struct linenoiseState *l, int flags) {
+
char seq[64];
+
size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt));
+
int colpos = columnPosForMultiLine(l->buf, l->len, l->len, l->cols, pcollen);
+
int colpos2; /* cursor column position. */
+
int rows = (pcollen+colpos+l->cols-1)/l->cols; /* rows used by current buf. */
+
int rpos = (pcollen+l->oldcolpos+l->cols)/l->cols; /* cursor relative row. */
+
int rpos2; /* rpos after refresh. */
+
int col; /* colum position, zero-based. */
+
int old_rows = l->oldrows;
+
int fd = l->ofd, j;
+
struct abuf ab;
+
+
l->oldrows = rows;
+
+
/* First step: clear all the lines used before. To do so start by
+
* going to the last row. */
+
abInit(&ab);
+
+
if (flags & REFRESH_CLEAN) {
+
if (old_rows-rpos > 0) {
+
lndebug("go down %d", old_rows-rpos);
+
snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
+
abAppend(&ab,seq,strlen(seq));
+
}
+
+
/* Now for every row clear it, go up. */
+
for (j = 0; j < old_rows-1; j++) {
+
lndebug("clear+up");
+
snprintf(seq,64,"\r\x1b[0K\x1b[1A");
+
abAppend(&ab,seq,strlen(seq));
+
}
+
}
+
+
if (flags & REFRESH_ALL) {
+
/* Clean the top line. */
+
lndebug("clear");
+
snprintf(seq,64,"\r\x1b[0K");
+
abAppend(&ab,seq,strlen(seq));
+
}
+
+
/* Get column length to cursor position */
+
colpos2 = columnPosForMultiLine(l->buf,l->len,l->pos,l->cols,pcollen);
+
+
if (flags & REFRESH_WRITE) {
+
/* Write the prompt and the current buffer content */
+
abAppend(&ab,l->prompt,strlen(l->prompt));
+
if (maskmode == 1) {
+
unsigned int i;
+
for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
+
} else {
+
abAppend(&ab,l->buf,l->len);
+
}
+
+
/* Show hits if any. */
+
refreshShowHints(&ab,l,pcollen);
+
+
/* If we are at the very end of the screen with our prompt, we need to
+
* emit a newline and move the prompt to the first column. */
+
if (l->pos &&
+
l->pos == l->len &&
+
(colpos2+pcollen) % l->cols == 0)
+
{
+
lndebug("<newline>");
+
abAppend(&ab,"\n",1);
+
snprintf(seq,64,"\r");
+
abAppend(&ab,seq,strlen(seq));
+
rows++;
+
if (rows > (int)l->oldrows) l->oldrows = rows;
+
}
+
+
/* Move cursor to right position. */
+
rpos2 = (pcollen+colpos2+l->cols)/l->cols; /* Current cursor relative row */
+
lndebug("rpos2 %d", rpos2);
+
+
/* Go up till we reach the expected positon. */
+
if (rows-rpos2 > 0) {
+
lndebug("go-up %d", rows-rpos2);
+
snprintf(seq,64,"\x1b[%dA", rows-rpos2);
+
abAppend(&ab,seq,strlen(seq));
+
}
+
+
/* Set column. */
+
col = (pcollen+colpos2) % l->cols;
+
lndebug("set col %d", 1+col);
+
if (col)
+
snprintf(seq,64,"\r\x1b[%dC", col);
+
else
+
snprintf(seq,64,"\r");
+
abAppend(&ab,seq,strlen(seq));
+
}
+
+
lndebug("\n");
+
l->oldcolpos = colpos2;
+
+
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
+
abFree(&ab);
+
}
+
+
/* Calls the two low level functions refreshSingleLine() or
+
* refreshMultiLine() according to the selected mode. */
+
static void refreshLineWithFlags(struct linenoiseState *l, int flags) {
+
if (mlmode)
+
refreshMultiLine(l,flags);
+
else
+
refreshSingleLine(l,flags);
+
}
+
+
/* Utility function to avoid specifying REFRESH_ALL all the times. */
+
static void refreshLine(struct linenoiseState *l) {
+
refreshLineWithFlags(l,REFRESH_ALL);
+
}
+
+
/* Hide the current line, when using the multiplexing API. */
+
void linenoiseHide(struct linenoiseState *l) {
+
if (mlmode)
+
refreshMultiLine(l,REFRESH_CLEAN);
+
else
+
refreshSingleLine(l,REFRESH_CLEAN);
+
}
+
+
/* Show the current line, when using the multiplexing API. */
+
void linenoiseShow(struct linenoiseState *l) {
+
if (l->in_completion) {
+
refreshLineWithCompletion(l,NULL,REFRESH_WRITE);
+
} else {
+
refreshLineWithFlags(l,REFRESH_WRITE);
+
}
+
}
+
+
/* Insert the character 'c' at cursor current position.
+
*
+
* On error writing to the terminal -1 is returned, otherwise 0. */
+
int linenoiseEditInsert(struct linenoiseState *l, const char *cbuf, int clen) {
+
if (l->len+clen <= l->buflen) {
+
if (l->len == l->pos) {
+
memcpy(&l->buf[l->pos],cbuf,clen);
+
l->pos+=clen;
+
l->len+=clen;;
+
l->buf[l->len] = '\0';
+
if ((!mlmode && promptTextColumnLen(l->prompt,l->plen)+columnPos(l->buf,l->len,l->len) < l->cols && !hintsCallback)) {
+
/* Avoid a full update of the line in the
+
* trivial case. */
+
if (maskmode == 1) {
+
static const char d = '*';
+
if (write(l->ofd,&d,1) == -1) return -1;
+
} else {
+
if (write(l->ofd,cbuf,clen) == -1) return -1;
+
}
+
} else {
+
refreshLine(l);
+
}
+
} else {
+
memmove(l->buf+l->pos+clen,l->buf+l->pos,l->len-l->pos);
+
memcpy(&l->buf[l->pos],cbuf,clen);
+
l->pos+=clen;
+
l->len+=clen;
+
l->buf[l->len] = '\0';
+
refreshLine(l);
+
}
+
}
+
return 0;
+
}
+
+
/* Move cursor on the left. */
+
void linenoiseEditMoveLeft(struct linenoiseState *l) {
+
if (l->pos > 0) {
+
l->pos -= prevCharLen(l->buf,l->len,l->pos,NULL);
+
refreshLine(l);
+
}
+
}
+
+
/* Move cursor on the right. */
+
void linenoiseEditMoveRight(struct linenoiseState *l) {
+
if (l->pos != l->len) {
+
l->pos += nextCharLen(l->buf,l->len,l->pos,NULL);
+
refreshLine(l);
+
}
+
}
+
+
/* Move cursor to the start of the line. */
+
void linenoiseEditMoveHome(struct linenoiseState *l) {
+
if (l->pos != 0) {
+
l->pos = 0;
+
refreshLine(l);
+
}
+
}
+
+
/* Move cursor to the end of the line. */
+
void linenoiseEditMoveEnd(struct linenoiseState *l) {
+
if (l->pos != l->len) {
+
l->pos = l->len;
+
refreshLine(l);
+
}
+
}
+
+
/* Substitute the currently edited line with the next or previous history
+
* entry as specified by 'dir'. */
+
#define LINENOISE_HISTORY_NEXT 0
+
#define LINENOISE_HISTORY_PREV 1
+
void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
+
if (history_len > 1) {
+
/* Update the current history entry before to
+
* overwrite it with the next one. */
+
free(history[history_len - 1 - l->history_index]);
+
history[history_len - 1 - l->history_index] = strdup(l->buf);
+
/* Show the new entry */
+
l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
+
if (l->history_index < 0) {
+
l->history_index = 0;
+
return;
+
} else if (l->history_index >= history_len) {
+
l->history_index = history_len-1;
+
return;
+
}
+
strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
+
l->buf[l->buflen-1] = '\0';
+
l->len = l->pos = strlen(l->buf);
+
refreshLine(l);
+
}
+
}
+
+
/* Delete the character at the right of the cursor without altering the cursor
+
* position. Basically this is what happens with the "Delete" keyboard key. */
+
void linenoiseEditDelete(struct linenoiseState *l) {
+
if (l->len > 0 && l->pos < l->len) {
+
int chlen = nextCharLen(l->buf,l->len,l->pos,NULL);
+
memmove(l->buf+l->pos,l->buf+l->pos+chlen,l->len-l->pos-chlen);
+
l->len-=chlen;
+
l->buf[l->len] = '\0';
+
refreshLine(l);
+
}
+
}
+
+
/* Backspace implementation. */
+
void linenoiseEditBackspace(struct linenoiseState *l) {
+
if (l->pos > 0 && l->len > 0) {
+
int chlen = prevCharLen(l->buf,l->len,l->pos,NULL);
+
memmove(l->buf+l->pos-chlen,l->buf+l->pos,l->len-l->pos);
+
l->pos-=chlen;
+
l->len-=chlen;
+
l->buf[l->len] = '\0';
+
refreshLine(l);
+
}
+
}
+
+
/* Delete the previosu word, maintaining the cursor at the start of the
+
* current word. */
+
void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
+
size_t old_pos = l->pos;
+
size_t diff;
+
+
while (l->pos > 0 && l->buf[l->pos-1] == ' ')
+
l->pos--;
+
while (l->pos > 0 && l->buf[l->pos-1] != ' ')
+
l->pos--;
+
diff = old_pos - l->pos;
+
memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
+
l->len -= diff;
+
refreshLine(l);
+
}
+
+
/* This function is part of the multiplexed API of Linenoise, that is used
+
* in order to implement the blocking variant of the API but can also be
+
* called by the user directly in an event driven program. It will:
+
*
+
* 1. Initialize the linenoise state passed by the user.
+
* 2. Put the terminal in RAW mode.
+
* 3. Show the prompt.
+
* 4. Return control to the user, that will have to call linenoiseEditFeed()
+
* each time there is some data arriving in the standard input.
+
*
+
* The user can also call linenoiseEditHide() and linenoiseEditShow() if it
+
* is required to show some input arriving asyncronously, without mixing
+
* it with the currently edited line.
+
*
+
* When linenoiseEditFeed() returns non-NULL, the user finished with the
+
* line editing session (pressed enter CTRL-D/C): in this case the caller
+
* needs to call linenoiseEditStop() to put back the terminal in normal
+
* mode. This will not destroy the buffer, as long as the linenoiseState
+
* is still valid in the context of the caller.
+
*
+
* The function returns 0 on success, or -1 if writing to standard output
+
* fails. If stdin_fd or stdout_fd are set to -1, the default is to use
+
* STDIN_FILENO and STDOUT_FILENO.
+
*/
+
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) {
+
/* Populate the linenoise state that we pass to functions implementing
+
* specific editing functionalities. */
+
l->in_completion = 0;
+
l->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO;
+
l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO;
+
l->buf = buf;
+
l->buflen = buflen;
+
l->prompt = prompt;
+
l->plen = strlen(prompt);
+
l->oldcolpos = l->pos = 0;
+
l->len = 0;
+
+
/* Enter raw mode. */
+
if (enableRawMode(l->ifd) == -1) return -1;
+
+
l->cols = getColumns(stdin_fd, stdout_fd);
+
l->oldrows = 0;
+
l->history_index = 0;
+
+
/* Buffer starts empty. */
+
l->buf[0] = '\0';
+
l->buflen--; /* Make sure there is always space for the nulterm */
+
+
/* If stdin is not a tty, stop here with the initialization. We
+
* will actually just read a line from standard input in blocking
+
* mode later, in linenoiseEditFeed(). */
+
if (!isatty(l->ifd)) return 0;
+
+
/* The latest history entry is always our current buffer, that
+
* initially is just an empty string. */
+
linenoiseHistoryAdd("");
+
+
if (write(l->ofd,prompt,l->plen) == -1) return -1;
+
return 0;
+
}
+
+
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.";
+
+
/* This function is part of the multiplexed API of linenoise, see the top
+
* comment on linenoiseEditStart() for more information. Call this function
+
* each time there is some data to read from the standard input file
+
* descriptor. In the case of blocking operations, this function can just be
+
* called in a loop, and block.
+
*
+
* The function returns linenoiseEditMore to signal that line editing is still
+
* in progress, that is, the user didn't yet pressed enter / CTRL-D. Otherwise
+
* the function returns the pointer to the heap-allocated buffer with the
+
* edited line, that the user should free with linenoiseFree().
+
*
+
* On special conditions, NULL is returned and errno is populated:
+
*
+
* EAGAIN if the user pressed Ctrl-C
+
* ENOENT if the user pressed Ctrl-D
+
*
+
* Some other errno: I/O error.
+
*/
+
char *linenoiseEditFeed(struct linenoiseState *l) {
+
/* Not a TTY, pass control to line reading without character
+
* count limits. */
+
if (!isatty(l->ifd)) return linenoiseNoTTY();
+
+
int c;
+
int nread;
+
char cbuf[32]; // large enough for any encoding?
+
char seq[3];
+
+
nread = readCode(l->ifd,cbuf,sizeof(cbuf),&c);
+
if (nread <= 0) return NULL;
+
+
/* Only autocomplete when the callback is set. It returns < 0 when
+
* there was an error reading from fd. Otherwise it will return the
+
* character that should be handled next. */
+
if ((l->in_completion || c == 9) && completionCallback != NULL) {
+
c = completeLine(l,c);
+
/* Return on errors */
+
if (c < 0) return NULL;
+
/* Read next character when 0 */
+
if (c == 0) return linenoiseEditMore;
+
}
+
+
switch(c) {
+
case ENTER: /* enter */
+
history_len--;
+
free(history[history_len]);
+
if (mlmode) linenoiseEditMoveEnd(l);
+
if (hintsCallback) {
+
/* Force a refresh without hints to leave the previous
+
* line as the user typed it after a newline. */
+
linenoiseHintsCallback *hc = hintsCallback;
+
hintsCallback = NULL;
+
refreshLine(l);
+
hintsCallback = hc;
+
}
+
return strdup(l->buf);
+
case CTRL_C: /* ctrl-c */
+
errno = EAGAIN;
+
linenoiseWasInterrupted = 1;
+
return NULL;
+
case BACKSPACE: /* backspace */
+
case 8: /* ctrl-h */
+
linenoiseEditBackspace(l);
+
break;
+
case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
+
line is empty, act as end-of-file. */
+
if (l->len > 0) {
+
linenoiseEditDelete(l);
+
} else {
+
history_len--;
+
free(history[history_len]);
+
errno = ENOENT;
+
return NULL;
+
}
+
break;
+
case CTRL_T: /* ctrl-t, swaps current character with previous. */
+
if (l->pos > 0 && l->pos < l->len) {
+
int aux = l->buf[l->pos-1];
+
l->buf[l->pos-1] = l->buf[l->pos];
+
l->buf[l->pos] = aux;
+
if (l->pos != l->len-1) l->pos++;
+
refreshLine(l);
+
}
+
break;
+
case CTRL_B: /* ctrl-b */
+
linenoiseEditMoveLeft(l);
+
break;
+
case CTRL_F: /* ctrl-f */
+
linenoiseEditMoveRight(l);
+
break;
+
case CTRL_P: /* ctrl-p */
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
+
break;
+
case CTRL_N: /* ctrl-n */
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
+
break;
+
case ESC: /* escape sequence */
+
/* Read the next two bytes representing the escape sequence.
+
* Use two calls to handle slow terminals returning the two
+
* chars at different times. */
+
if (read(l->ifd,seq,1) == -1) break;
+
if (read(l->ifd,seq+1,1) == -1) break;
+
+
/* ESC [ sequences. */
+
if (seq[0] == '[') {
+
if (seq[1] >= '0' && seq[1] <= '9') {
+
/* Extended escape, read additional byte. */
+
if (read(l->ifd,seq+2,1) == -1) break;
+
if (seq[2] == '~') {
+
switch(seq[1]) {
+
case '3': /* Delete key. */
+
linenoiseEditDelete(l);
+
break;
+
}
+
}
+
} else {
+
switch(seq[1]) {
+
case 'A': /* Up */
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
+
break;
+
case 'B': /* Down */
+
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
+
break;
+
case 'C': /* Right */
+
linenoiseEditMoveRight(l);
+
break;
+
case 'D': /* Left */
+
linenoiseEditMoveLeft(l);
+
break;
+
case 'H': /* Home */
+
linenoiseEditMoveHome(l);
+
break;
+
case 'F': /* End*/
+
linenoiseEditMoveEnd(l);
+
break;
+
}
+
}
+
}
+
+
/* ESC O sequences. */
+
else if (seq[0] == 'O') {
+
switch(seq[1]) {
+
case 'H': /* Home */
+
linenoiseEditMoveHome(l);
+
break;
+
case 'F': /* End*/
+
linenoiseEditMoveEnd(l);
+
break;
+
}
+
}
+
break;
+
default:
+
if (linenoiseEditInsert(l,cbuf,nread)) return NULL;
+
break;
+
case CTRL_U: /* Ctrl+u, delete the whole line. */
+
l->buf[0] = '\0';
+
l->pos = l->len = 0;
+
refreshLine(l);
+
break;
+
case CTRL_K: /* Ctrl+k, delete from current to end of line. */
+
l->buf[l->pos] = '\0';
+
l->len = l->pos;
+
refreshLine(l);
+
break;
+
case CTRL_A: /* Ctrl+a, go to the start of the line */
+
linenoiseEditMoveHome(l);
+
break;
+
case CTRL_E: /* ctrl+e, go to the end of the line */
+
linenoiseEditMoveEnd(l);
+
break;
+
case CTRL_L: /* ctrl+l, clear screen */
+
linenoiseClearScreen();
+
refreshLine(l);
+
break;
+
case CTRL_W: /* ctrl+w, delete previous word */
+
linenoiseEditDeletePrevWord(l);
+
break;
+
}
+
return linenoiseEditMore;
+
}
+
+
/* This is part of the multiplexed linenoise API. See linenoiseEditStart()
+
* for more information. This function is called when linenoiseEditFeed()
+
* returns something different than NULL. At this point the user input
+
* is in the buffer, and we can restore the terminal in normal mode. */
+
void linenoiseEditStop(struct linenoiseState *l) {
+
if (!isatty(l->ifd)) return;
+
disableRawMode(l->ifd);
+
printf("\n");
+
}
+
+
/* This just implements a blocking loop for the multiplexed API.
+
* In many applications that are not event-drivern, we can just call
+
* the blocking linenoise API, wait for the user to complete the editing
+
* and return the buffer. */
+
static char *linenoiseBlockingEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
+
{
+
struct linenoiseState l;
+
+
/* Editing without a buffer is invalid. */
+
if (buflen == 0) {
+
errno = EINVAL;
+
return NULL;
+
}
+
+
linenoiseEditStart(&l,stdin_fd,stdout_fd,buf,buflen,prompt);
+
char *res;
+
while((res = linenoiseEditFeed(&l)) == linenoiseEditMore);
+
linenoiseEditStop(&l);
+
return res;
+
}
+
+
/* This special mode is used by linenoise in order to print scan codes
+
* on screen for debugging / development purposes. It is implemented
+
* by the linenoise_example program using the --keycodes option. */
+
void linenoisePrintKeyCodes(void) {
+
char quit[4];
+
+
printf("Linenoise key codes debugging mode.\n"
+
"Press keys to see scan codes. Type 'quit' at any time to exit.\n");
+
if (enableRawMode(STDIN_FILENO) == -1) return;
+
memset(quit,' ',4);
+
while(1) {
+
char c;
+
int nread;
+
+
nread = read(STDIN_FILENO,&c,1);
+
if (nread <= 0) continue;
+
memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
+
quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
+
if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
+
+
printf("'%c' %02x (%d) (type quit to exit)\n",
+
isprint((int)c) ? c : '?', (int)c, (int)c);
+
printf("\r"); /* Go left edge manually, we are in raw mode. */
+
fflush(stdout);
+
}
+
disableRawMode(STDIN_FILENO);
+
}
+
+
/* This function is called when linenoise() is called with the standard
+
* input file descriptor not attached to a TTY. So for example when the
+
* program using linenoise is called in pipe or with a file redirected
+
* to its standard input. In this case, we want to be able to return the
+
* line regardless of its length (by default we are limited to 4k). */
+
static char *linenoiseNoTTY(void) {
+
char *line = NULL;
+
size_t len = 0, maxlen = 0;
+
+
while(1) {
+
if (len == maxlen) {
+
if (maxlen == 0) maxlen = 16;
+
maxlen *= 2;
+
char *oldval = line;
+
line = realloc(line,maxlen);
+
if (line == NULL) {
+
if (oldval) free(oldval);
+
return NULL;
+
}
+
}
+
int c = fgetc(stdin);
+
if (c == EOF || c == '\n') {
+
if (c == EOF && len == 0) {
+
free(line);
+
return NULL;
+
} else {
+
line[len] = '\0';
+
return line;
+
}
+
} else {
+
line[len] = c;
+
len++;
+
}
+
}
+
}
+
+
/* The high level function that is the main API of the linenoise library.
+
* This function checks if the terminal has basic capabilities, just checking
+
* for a blacklist of stupid terminals, and later either calls the line
+
* editing function or uses dummy fgets() so that you will be able to type
+
* something even in the most desperate of the conditions. */
+
char *linenoise(const char *prompt) {
+
char buf[LINENOISE_MAX_LINE];
+
+
if (!isatty(STDIN_FILENO)) {
+
/* Not a tty: read from file / pipe. In this mode we don't want any
+
* limit to the line size, so we call a function to handle that. */
+
return linenoiseNoTTY();
+
} else if (isUnsupportedTerm()) {
+
size_t len;
+
+
printf("%s",prompt);
+
fflush(stdout);
+
if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
+
len = strlen(buf);
+
while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+
len--;
+
buf[len] = '\0';
+
}
+
return strdup(buf);
+
} else {
+
char *retval = linenoiseBlockingEdit(STDIN_FILENO,STDOUT_FILENO,buf,LINENOISE_MAX_LINE,prompt);
+
return retval;
+
}
+
}
+
+
/* This is just a wrapper the user may want to call in order to make sure
+
* the linenoise returned buffer is freed with the same allocator it was
+
* created with. Useful when the main program is using an alternative
+
* allocator. */
+
void linenoiseFree(void *ptr) {
+
if (ptr == linenoiseEditMore) return; // Protect from API misuse.
+
free(ptr);
+
}
+
+
/* ================================ History ================================= */
+
+
/* Free the history, but does not reset it. Only used when we have to
+
* exit() to avoid memory leaks are reported by valgrind & co. */
+
static void freeHistory(void) {
+
if (history) {
+
int j;
+
+
for (j = 0; j < history_len; j++)
+
free(history[j]);
+
free(history);
+
}
+
}
+
+
/* At exit we'll try to fix the terminal to the initial conditions. */
+
static void linenoiseAtExit(void) {
+
disableRawMode(STDIN_FILENO);
+
freeHistory();
+
}
+
+
/* This is the API call to add a new entry in the linenoise history.
+
* It uses a fixed array of char pointers that are shifted (memmoved)
+
* when the history max length is reached in order to remove the older
+
* entry and make room for the new one, so it is not exactly suitable for huge
+
* histories, but will work well for a few hundred of entries.
+
*
+
* Using a circular buffer is smarter, but a bit more complex to handle. */
+
int linenoiseHistoryAdd(const char *line) {
+
char *linecopy;
+
+
if (history_max_len == 0) return 0;
+
+
/* Initialization on first call. */
+
if (history == NULL) {
+
history = malloc(sizeof(char*)*history_max_len);
+
if (history == NULL) return 0;
+
memset(history,0,(sizeof(char*)*history_max_len));
+
}
+
+
/* Don't add duplicated lines. */
+
if (history_len && !strcmp(history[history_len-1], line)) return 0;
+
+
/* Add an heap allocated copy of the line in the history.
+
* If we reached the max length, remove the older line. */
+
linecopy = strdup(line);
+
if (!linecopy) return 0;
+
if (history_len == history_max_len) {
+
free(history[0]);
+
memmove(history,history+1,sizeof(char*)*(history_max_len-1));
+
history_len--;
+
}
+
history[history_len] = linecopy;
+
history_len++;
+
return 1;
+
}
+
+
/* Set the maximum length for the history. This function can be called even
+
* if there is already some history, the function will make sure to retain
+
* just the latest 'len' elements if the new history length value is smaller
+
* than the amount of items already inside the history. */
+
int linenoiseHistorySetMaxLen(int len) {
+
char **new;
+
+
if (len < 1) return 0;
+
if (history) {
+
int tocopy = history_len;
+
+
new = malloc(sizeof(char*)*len);
+
if (new == NULL) return 0;
+
+
/* If we can't copy everything, free the elements we'll not use. */
+
if (len < tocopy) {
+
int j;
+
+
for (j = 0; j < tocopy-len; j++) free(history[j]);
+
tocopy = len;
+
}
+
memset(new,0,sizeof(char*)*len);
+
memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
+
free(history);
+
history = new;
+
}
+
history_max_len = len;
+
if (history_len > history_max_len)
+
history_len = history_max_len;
+
return 1;
+
}
+
+
/* Save the history in the specified file. On success 0 is returned
+
* otherwise -1 is returned. */
+
int linenoiseHistorySave(const char *filename) {
+
mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+
FILE *fp;
+
int j;
+
+
fp = fopen(filename,"w");
+
umask(old_umask);
+
if (fp == NULL) return -1;
+
chmod(filename,S_IRUSR|S_IWUSR);
+
for (j = 0; j < history_len; j++)
+
fprintf(fp,"%s\n",history[j]);
+
fclose(fp);
+
return 0;
+
}
+
+
/* Load the history from the specified file. If the file does not exist
+
* zero is returned and no operation is performed.
+
*
+
* If the file exists and the operation succeeded 0 is returned, otherwise
+
* on error -1 is returned. */
+
int linenoiseHistoryLoad(const char *filename) {
+
FILE *fp = fopen(filename,"r");
+
char buf[LINENOISE_MAX_LINE];
+
+
if (fp == NULL) return -1;
+
+
while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
+
char *p;
+
+
p = strchr(buf,'\r');
+
if (!p) p = strchr(buf,'\n');
+
if (p) *p = '\0';
+
linenoiseHistoryAdd(buf);
+
}
+
fclose(fp);
+
return 0;
+
}
+123
vendor/ocaml-linenoise/src/linenoise_src.h
···
+
/* linenoise.h -- VERSION 1.0
+
*
+
* Guerrilla line editing library against the idea that a line editing lib
+
* needs to be 20,000 lines of C code.
+
*
+
* See linenoise.c for more information.
+
*
+
* ------------------------------------------------------------------------
+
*
+
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+
*
+
* All rights reserved.
+
*
+
* Redistribution and use in source and binary forms, with or without
+
* modification, are permitted provided that the following conditions are
+
* met:
+
*
+
* * Redistributions of source code must retain the above copyright
+
* notice, this list of conditions and the following disclaimer.
+
*
+
* * Redistributions in binary form must reproduce the above copyright
+
* notice, this list of conditions and the following disclaimer in the
+
* documentation and/or other materials provided with the distribution.
+
*
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
*/
+
+
#ifndef __LINENOISE_H
+
#define __LINENOISE_H
+
+
#ifdef __cplusplus
+
extern "C" {
+
#endif
+
+
#include <stddef.h> /* For size_t. */
+
+
extern int linenoiseWasInterrupted; /* boolean signalling if last call was ctrl-c */
+
extern char *linenoiseEditMore;
+
+
/* The linenoiseState structure represents the state during line editing.
+
* We pass this state to functions implementing specific editing
+
* functionalities. */
+
struct linenoiseState {
+
int in_completion; /* The user pressed TAB and we are now in completion
+
* mode, so input is handled by completeLine(). */
+
size_t completion_idx; /* Index of next completion to propose. */
+
int ifd; /* Terminal stdin file descriptor. */
+
int ofd; /* Terminal stdout file descriptor. */
+
char *buf; /* Edited line buffer. */
+
size_t buflen; /* Edited line buffer size. */
+
const char *prompt; /* Prompt to display. */
+
size_t plen; /* Prompt length. */
+
size_t pos; /* Current cursor position. */
+
size_t oldcolpos; /* Previous refresh cursor column position. */
+
size_t len; /* Current edited line length. */
+
size_t cols; /* Number of columns in terminal. */
+
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
+
int history_index; /* The history index we are currently editing. */
+
};
+
+
typedef struct linenoiseCompletions {
+
size_t len;
+
char **cvec;
+
} linenoiseCompletions;
+
+
/* Non blocking API. */
+
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt);
+
char *linenoiseEditFeed(struct linenoiseState *l);
+
void linenoiseEditStop(struct linenoiseState *l);
+
void linenoiseHide(struct linenoiseState *l);
+
void linenoiseShow(struct linenoiseState *l);
+
+
/* Blocking API. */
+
char *linenoise(const char *prompt);
+
void linenoiseFree(void *ptr);
+
+
/* Completion API. */
+
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
+
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
+
typedef void(linenoiseFreeHintsCallback)(void *);
+
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
+
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
+
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
+
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
+
+
/* History API. */
+
int linenoiseHistoryAdd(const char *line);
+
int linenoiseHistorySetMaxLen(int len);
+
int linenoiseHistorySave(const char *filename);
+
int linenoiseHistoryLoad(const char *filename);
+
+
/* Other utilities. */
+
void linenoiseClearScreen(void);
+
void linenoiseSetMultiLine(int ml);
+
void linenoisePrintKeyCodes(void);
+
void linenoiseMaskModeEnable(void);
+
void linenoiseMaskModeDisable(void);
+
+
typedef size_t (linenoisePrevCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len);
+
typedef size_t (linenoiseNextCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len);
+
typedef size_t (linenoiseReadCode)(int fd, char *buf, size_t buf_len, int* c);
+
+
void linenoiseSetEncodingFunctions(
+
linenoisePrevCharLen *prevCharLenFunc,
+
linenoiseNextCharLen *nextCharLenFunc,
+
linenoiseReadCode *readCodeFunc);
+
+
#ifdef __cplusplus
+
}
+
#endif
+
+
#endif /* __LINENOISE_H */
+183
vendor/ocaml-linenoise/src/linenoise_stubs.c
···
+
// OCaml declarations
+
#include <caml/alloc.h>
+
#include <caml/memory.h>
+
#include <caml/callback.h>
+
#include <caml/fail.h>
+
#include <caml/threads.h>
+
+
#include <errno.h>
+
#include <assert.h>
+
+
#include "linenoise_src.h"
+
+
// Ripped from ctypes
+
#define Val_none Val_int(0)
+
#define Some_val(v) Field(v, 0)
+
+
static value Val_some(value v)
+
{
+
CAMLparam1(v);
+
CAMLlocal1(some);
+
some = caml_alloc(1, 0);
+
Store_field(some, 0, v);
+
CAMLreturn(some);
+
}
+
+
/* if true, raise Sys.Break on ctrl-c */
+
static int raise_sys_break = 0;
+
+
CAMLprim value ml_catch_break(value flag)
+
{
+
CAMLparam1(flag);
+
raise_sys_break = Bool_val(flag);
+
CAMLreturn(Val_unit);
+
}
+
+
CAMLprim value ml_add_completion(value completions, value new_completion)
+
{
+
CAMLparam2(completions, new_completion);
+
char* c_new_completion = caml_stat_strdup(String_val(new_completion));
+
linenoiseAddCompletion((linenoiseCompletions *)completions, c_new_completion);
+
caml_stat_free(c_new_completion);
+
CAMLreturn(Val_unit);
+
}
+
+
// this bridge runs with the runtime lock acquired
+
static void completion_bridge_inner(const char *buf, linenoiseCompletions *lc)
+
{
+
CAMLparam0();
+
CAMLlocal1(str_copy);
+
str_copy = caml_copy_string(buf);
+
caml_callback2(*caml_named_value("lnoise_completion_cb"), str_copy, (value)lc);
+
CAMLreturn0;
+
}
+
+
static void completion_bridge(const char *buf, linenoiseCompletions *lc)
+
{
+
caml_acquire_runtime_system();
+
completion_bridge_inner(buf, lc);
+
caml_release_runtime_system();
+
}
+
+
static char *hints_bridge_inner(const char *buf, int *color, int *bold)
+
{
+
CAMLparam0();
+
CAMLlocal2(str_copy, cb_result);
+
+
str_copy = caml_copy_string(buf);
+
+
cb_result = caml_callback(*caml_named_value("lnoise_hints_cb"), str_copy);
+
if (cb_result == Val_none) {
+
CAMLreturnT(char *,NULL);
+
} else {
+
char* msg = caml_stat_strdup(String_val(Field(Field(cb_result, 0), 0)));
+
*color = Int_val(Field(Field(cb_result, 0), 1)) + 31;
+
*bold = Bool_val(Field(Field(cb_result, 0), 2));
+
CAMLreturnT(char *,msg);
+
}
+
}
+
+
static char *hints_bridge(const char *buf, int *color, int *bold)
+
{
+
caml_acquire_runtime_system();
+
char* res = hints_bridge_inner(buf, color, bold);
+
caml_release_runtime_system();
+
return res;
+
}
+
+
+
static void free_hints_bridge(void* data) {
+
caml_acquire_runtime_system();
+
caml_stat_free(data);
+
caml_release_runtime_system();
+
}
+
+
__attribute__((constructor))
+
void set_free_hints(void) { linenoiseSetFreeHintsCallback(free); }
+
+
CAMLprim value ml_setup_bridges(value unit) {
+
CAMLparam1(unit);
+
linenoiseSetCompletionCallback(completion_bridge);
+
linenoiseSetHintsCallback(hints_bridge);
+
linenoiseSetFreeHintsCallback(free_hints_bridge);
+
CAMLreturn(Val_unit);
+
}
+
+
CAMLprim value ml_linenoise(value prompt)
+
{
+
CAMLparam1(prompt);
+
CAMLlocal1(lnoise_result);
+
+
linenoiseWasInterrupted = 0; // reset
+
char* c_prompt = caml_stat_strdup(String_val(prompt));
+
+
caml_release_runtime_system();
+
const char *result = linenoise(c_prompt);
+
caml_acquire_runtime_system();
+
+
caml_stat_free(c_prompt);
+
if (!result) {
+
if (linenoiseWasInterrupted && raise_sys_break) {
+
caml_raise_constant(*caml_named_value("sys_break"));
+
} else {
+
CAMLreturn(Val_none);
+
}
+
}
+
lnoise_result = caml_copy_string(result);
+
linenoiseFree((void*)result);
+
CAMLreturn(Val_some(lnoise_result));
+
}
+
+
CAMLprim value ml_history_add(value line)
+
{
+
CAMLparam1(line);
+
char* c_line = caml_stat_strdup(String_val(line));
+
int res = linenoiseHistoryAdd(c_line);
+
caml_stat_free(c_line);
+
CAMLreturn(Val_int(res));
+
}
+
+
CAMLprim value ml_history_set_maxlen(value max)
+
{
+
CAMLparam1(max);
+
CAMLreturn(Val_int(linenoiseHistorySetMaxLen(Int_val(max))));
+
}
+
+
CAMLprim value ml_history_save(value filename)
+
{
+
CAMLparam1(filename);
+
char* c_filename = caml_stat_strdup(String_val(filename));
+
int res = linenoiseHistorySave(c_filename);
+
caml_stat_free(c_filename);
+
CAMLreturn(Val_int(res));
+
}
+
+
CAMLprim value ml_history_load(value filename)
+
{
+
CAMLparam1(filename);
+
char* c_filename= caml_stat_strdup(String_val(filename));
+
int res = linenoiseHistoryLoad(c_filename);
+
caml_stat_free(c_filename);
+
CAMLreturn(Val_int(res));
+
}
+
+
CAMLprim value ml_clearscreen(__attribute__((unused))value unit)
+
{
+
CAMLparam0();
+
linenoiseClearScreen();
+
CAMLreturn(Val_unit);
+
}
+
+
CAMLprim value ml_set_multiline(value use_multiline)
+
{
+
CAMLparam1(use_multiline);
+
linenoiseSetMultiLine(Bool_val(use_multiline));
+
CAMLreturn(Val_unit);
+
}
+
+
CAMLprim value ml_printkeycodes(void)
+
{
+
CAMLparam0();
+
linenoisePrintKeyCodes();
+
CAMLreturn(Val_unit);
+
}
+473
vendor/ocaml-linenoise/src/utf8.c
···
+
/* encoding/utf8.c -- VERSION 1.0
+
*
+
* Guerrilla line editing library against the idea that a line editing lib
+
* needs to be 20,000 lines of C code.
+
*
+
* You can find the latest source code at:
+
*
+
* http://github.com/antirez/linenoise
+
*
+
* Does a number of crazy assumptions that happen to be true in 99.9999% of
+
* the 2010 UNIX computers around.
+
*
+
* ------------------------------------------------------------------------
+
*
+
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+
*
+
* All rights reserved.
+
*
+
* Redistribution and use in source and binary forms, with or without
+
* modification, are permitted provided that the following conditions are
+
* met:
+
*
+
* * Redistributions of source code must retain the above copyright
+
* notice, this list of conditions and the following disclaimer.
+
*
+
* * Redistributions in binary form must reproduce the above copyright
+
* notice, this list of conditions and the following disclaimer in the
+
* documentation and/or other materials provided with the distribution.
+
*
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
*/
+
+
#include <unistd.h>
+
#include <stdio.h>
+
+
#define UNUSED(x) (void)(x)
+
+
/* ============================ UTF8 utilities ============================== */
+
+
static unsigned long wideCharTable[][2] = {
+
{ 0x1100, 0x115F }, { 0x231A, 0x231B }, { 0x2329, 0x232A }, { 0x23E9, 0x23EC },
+
{ 0x23F0, 0x23F0 }, { 0x23F3, 0x23F3 }, { 0x25FD, 0x25FE }, { 0x2614, 0x2615 },
+
{ 0x2630, 0x2637 }, { 0x2648, 0x2653 }, { 0x267F, 0x267F }, { 0x268A, 0x268F },
+
{ 0x2693, 0x2693 }, { 0x26A1, 0x26A1 }, { 0x26AA, 0x26AB }, { 0x26BD, 0x26BE },
+
{ 0x26C4, 0x26C5 }, { 0x26CE, 0x26CE }, { 0x26D4, 0x26D4 }, { 0x26EA, 0x26EA },
+
{ 0x26F2, 0x26F3 }, { 0x26F5, 0x26F5 }, { 0x26FA, 0x26FA }, { 0x26FD, 0x26FD },
+
{ 0x2705, 0x2705 }, { 0x270A, 0x270B }, { 0x2728, 0x2728 }, { 0x274C, 0x274C },
+
{ 0x274E, 0x274E }, { 0x2753, 0x2755 }, { 0x2757, 0x2757 }, { 0x2795, 0x2797 },
+
{ 0x27B0, 0x27B0 }, { 0x27BF, 0x27BF }, { 0x2B1B, 0x2B1C }, { 0x2B50, 0x2B50 },
+
{ 0x2B55, 0x2B55 }, { 0x2E80, 0x2E99 }, { 0x2E9B, 0x2EF3 }, { 0x2F00, 0x2FD5 },
+
{ 0x2FF0, 0x303E }, { 0x3041, 0x3096 }, { 0x3099, 0x30FF }, { 0x3105, 0x312F },
+
{ 0x3131, 0x318E }, { 0x3190, 0x31E5 }, { 0x31EF, 0x321E }, { 0x3220, 0x3247 },
+
{ 0x3250, 0xA48C }, { 0xA490, 0xA4C6 }, { 0xA960, 0xA97C }, { 0xAC00, 0xD7A3 },
+
{ 0xF900, 0xFAFF }, { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE52 }, { 0xFE54, 0xFE66 },
+
{ 0xFE68, 0xFE6B }, { 0xFF01, 0xFF60 }, { 0xFFE0, 0xFFE6 }, { 0x16FE0, 0x16FE4 },
+
{ 0x16FF0, 0x16FF1 }, { 0x17000, 0x187F7 }, { 0x18800, 0x18CD5 }, { 0x18CFF, 0x18D08 },
+
{ 0x1AFF0, 0x1AFF3 }, { 0x1AFF5, 0x1AFFB }, { 0x1AFFD, 0x1AFFE }, { 0x1B000, 0x1B122 },
+
{ 0x1B132, 0x1B132 }, { 0x1B150, 0x1B152 }, { 0x1B155, 0x1B155 }, { 0x1B164, 0x1B167 },
+
{ 0x1B170, 0x1B2FB }, { 0x1D300, 0x1D356 }, { 0x1D360, 0x1D376 }, { 0x1F004, 0x1F004 },
+
{ 0x1F0CF, 0x1F0CF }, { 0x1F18E, 0x1F18E }, { 0x1F191, 0x1F19A }, { 0x1F200, 0x1F202 },
+
{ 0x1F210, 0x1F23B }, { 0x1F240, 0x1F248 }, { 0x1F250, 0x1F251 }, { 0x1F260, 0x1F265 },
+
{ 0x1F300, 0x1F320 }, { 0x1F32D, 0x1F335 }, { 0x1F337, 0x1F37C }, { 0x1F37E, 0x1F393 },
+
{ 0x1F3A0, 0x1F3CA }, { 0x1F3CF, 0x1F3D3 }, { 0x1F3E0, 0x1F3F0 }, { 0x1F3F4, 0x1F3F4 },
+
{ 0x1F3F8, 0x1F43E }, { 0x1F440, 0x1F440 }, { 0x1F442, 0x1F4FC }, { 0x1F4FF, 0x1F53D },
+
{ 0x1F54B, 0x1F54E }, { 0x1F550, 0x1F567 }, { 0x1F57A, 0x1F57A }, { 0x1F595, 0x1F596 },
+
{ 0x1F5A4, 0x1F5A4 }, { 0x1F5FB, 0x1F64F }, { 0x1F680, 0x1F6C5 }, { 0x1F6CC, 0x1F6CC },
+
{ 0x1F6D0, 0x1F6D2 }, { 0x1F6D5, 0x1F6D7 }, { 0x1F6DC, 0x1F6DF }, { 0x1F6EB, 0x1F6EC },
+
{ 0x1F6F4, 0x1F6FC }, { 0x1F7E0, 0x1F7EB }, { 0x1F7F0, 0x1F7F0 }, { 0x1F90C, 0x1F93A },
+
{ 0x1F93C, 0x1F945 }, { 0x1F947, 0x1F9FF }, { 0x1FA70, 0x1FA7C }, { 0x1FA80, 0x1FA89 },
+
{ 0x1FA8F, 0x1FAC6 }, { 0x1FACE, 0x1FADC }, { 0x1FADF, 0x1FAE9 }, { 0x1FAF0, 0x1FAF8 },
+
{ 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD },
+
};
+
+
static size_t wideCharTableSize = sizeof(wideCharTable) / sizeof(wideCharTable[0]);
+
+
static unsigned long combiningCharTable[] = {
+
0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+
0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
+
0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+
0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
+
0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+
0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
+
0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+
0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
+
0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
+
0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
+
0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+
0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
+
0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+
0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
+
0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0591, 0x0592, 0x0593,
+
0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B,
+
0x059C, 0x059D, 0x059E, 0x059F, 0x05A0, 0x05A1, 0x05A2, 0x05A3,
+
0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB,
+
0x05AC, 0x05AD, 0x05AE, 0x05AF, 0x05B0, 0x05B1, 0x05B2, 0x05B3,
+
0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB,
+
0x05BC, 0x05BD, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7,
+
0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617,
+
0x0618, 0x0619, 0x061A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F,
+
0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657,
+
0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F,
+
0x0670, 0x06D6, 0x06D7, 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC,
+
0x06DF, 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E7, 0x06E8,
+
0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x0711, 0x0730, 0x0731, 0x0732,
+
0x0733, 0x0734, 0x0735, 0x0736, 0x0737, 0x0738, 0x0739, 0x073A,
+
0x073B, 0x073C, 0x073D, 0x073E, 0x073F, 0x0740, 0x0741, 0x0742,
+
0x0743, 0x0744, 0x0745, 0x0746, 0x0747, 0x0748, 0x0749, 0x074A,
+
0x07A6, 0x07A7, 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD,
+
0x07AE, 0x07AF, 0x07B0, 0x07EB, 0x07EC, 0x07ED, 0x07EE, 0x07EF,
+
0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07FD, 0x0816, 0x0817, 0x0818,
+
0x0819, 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, 0x0820, 0x0821,
+
0x0822, 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, 0x082A, 0x082B,
+
0x082C, 0x082D, 0x0859, 0x085A, 0x085B, 0x0897, 0x0898, 0x0899,
+
0x089A, 0x089B, 0x089C, 0x089D, 0x089E, 0x089F, 0x08CA, 0x08CB,
+
0x08CC, 0x08CD, 0x08CE, 0x08CF, 0x08D0, 0x08D1, 0x08D2, 0x08D3,
+
0x08D4, 0x08D5, 0x08D6, 0x08D7, 0x08D8, 0x08D9, 0x08DA, 0x08DB,
+
0x08DC, 0x08DD, 0x08DE, 0x08DF, 0x08E0, 0x08E1, 0x08E3, 0x08E4,
+
0x08E5, 0x08E6, 0x08E7, 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC,
+
0x08ED, 0x08EE, 0x08EF, 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4,
+
0x08F5, 0x08F6, 0x08F7, 0x08F8, 0x08F9, 0x08FA, 0x08FB, 0x08FC,
+
0x08FD, 0x08FE, 0x08FF, 0x0900, 0x0901, 0x0902, 0x093A, 0x093C,
+
0x0941, 0x0942, 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, 0x0948,
+
0x094D, 0x0951, 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957,
+
0x0962, 0x0963, 0x0981, 0x09BC, 0x09C1, 0x09C2, 0x09C3, 0x09C4,
+
0x09CD, 0x09E2, 0x09E3, 0x09FE, 0x0A01, 0x0A02, 0x0A3C, 0x0A41,
+
0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A51, 0x0A70,
+
0x0A71, 0x0A75, 0x0A81, 0x0A82, 0x0ABC, 0x0AC1, 0x0AC2, 0x0AC3,
+
0x0AC4, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0AE2, 0x0AE3, 0x0AFA,
+
0x0AFB, 0x0AFC, 0x0AFD, 0x0AFE, 0x0AFF, 0x0B01, 0x0B3C, 0x0B3F,
+
0x0B41, 0x0B42, 0x0B43, 0x0B44, 0x0B4D, 0x0B55, 0x0B56, 0x0B62,
+
0x0B63, 0x0B82, 0x0BC0, 0x0BCD, 0x0C00, 0x0C04, 0x0C3C, 0x0C3E,
+
0x0C3F, 0x0C40, 0x0C46, 0x0C47, 0x0C48, 0x0C4A, 0x0C4B, 0x0C4C,
+
0x0C4D, 0x0C55, 0x0C56, 0x0C62, 0x0C63, 0x0C81, 0x0CBC, 0x0CBF,
+
0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D00, 0x0D01, 0x0D3B,
+
0x0D3C, 0x0D41, 0x0D42, 0x0D43, 0x0D44, 0x0D4D, 0x0D62, 0x0D63,
+
0x0D81, 0x0DCA, 0x0DD2, 0x0DD3, 0x0DD4, 0x0DD6, 0x0E31, 0x0E34,
+
0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0E47, 0x0E48,
+
0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0EB1, 0x0EB4,
+
0x0EB5, 0x0EB6, 0x0EB7, 0x0EB8, 0x0EB9, 0x0EBA, 0x0EBB, 0x0EBC,
+
0x0EC8, 0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, 0x0ECD, 0x0ECE, 0x0F18,
+
0x0F19, 0x0F35, 0x0F37, 0x0F39, 0x0F71, 0x0F72, 0x0F73, 0x0F74,
+
0x0F75, 0x0F76, 0x0F77, 0x0F78, 0x0F79, 0x0F7A, 0x0F7B, 0x0F7C,
+
0x0F7D, 0x0F7E, 0x0F80, 0x0F81, 0x0F82, 0x0F83, 0x0F84, 0x0F86,
+
0x0F87, 0x0F8D, 0x0F8E, 0x0F8F, 0x0F90, 0x0F91, 0x0F92, 0x0F93,
+
0x0F94, 0x0F95, 0x0F96, 0x0F97, 0x0F99, 0x0F9A, 0x0F9B, 0x0F9C,
+
0x0F9D, 0x0F9E, 0x0F9F, 0x0FA0, 0x0FA1, 0x0FA2, 0x0FA3, 0x0FA4,
+
0x0FA5, 0x0FA6, 0x0FA7, 0x0FA8, 0x0FA9, 0x0FAA, 0x0FAB, 0x0FAC,
+
0x0FAD, 0x0FAE, 0x0FAF, 0x0FB0, 0x0FB1, 0x0FB2, 0x0FB3, 0x0FB4,
+
0x0FB5, 0x0FB6, 0x0FB7, 0x0FB8, 0x0FB9, 0x0FBA, 0x0FBB, 0x0FBC,
+
0x0FC6, 0x102D, 0x102E, 0x102F, 0x1030, 0x1032, 0x1033, 0x1034,
+
0x1035, 0x1036, 0x1037, 0x1039, 0x103A, 0x103D, 0x103E, 0x1058,
+
0x1059, 0x105E, 0x105F, 0x1060, 0x1071, 0x1072, 0x1073, 0x1074,
+
0x1082, 0x1085, 0x1086, 0x108D, 0x109D, 0x135D, 0x135E, 0x135F,
+
0x1712, 0x1713, 0x1714, 0x1732, 0x1733, 0x1752, 0x1753, 0x1772,
+
0x1773, 0x17B4, 0x17B5, 0x17B7, 0x17B8, 0x17B9, 0x17BA, 0x17BB,
+
0x17BC, 0x17BD, 0x17C6, 0x17C9, 0x17CA, 0x17CB, 0x17CC, 0x17CD,
+
0x17CE, 0x17CF, 0x17D0, 0x17D1, 0x17D2, 0x17D3, 0x17DD, 0x180B,
+
0x180C, 0x180D, 0x180F, 0x1885, 0x1886, 0x18A9, 0x1920, 0x1921,
+
0x1922, 0x1927, 0x1928, 0x1932, 0x1939, 0x193A, 0x193B, 0x1A17,
+
0x1A18, 0x1A1B, 0x1A56, 0x1A58, 0x1A59, 0x1A5A, 0x1A5B, 0x1A5C,
+
0x1A5D, 0x1A5E, 0x1A60, 0x1A62, 0x1A65, 0x1A66, 0x1A67, 0x1A68,
+
0x1A69, 0x1A6A, 0x1A6B, 0x1A6C, 0x1A73, 0x1A74, 0x1A75, 0x1A76,
+
0x1A77, 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, 0x1A7F, 0x1AB0,
+
0x1AB1, 0x1AB2, 0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, 0x1AB7, 0x1AB8,
+
0x1AB9, 0x1ABA, 0x1ABB, 0x1ABC, 0x1ABD, 0x1ABF, 0x1AC0, 0x1AC1,
+
0x1AC2, 0x1AC3, 0x1AC4, 0x1AC5, 0x1AC6, 0x1AC7, 0x1AC8, 0x1AC9,
+
0x1ACA, 0x1ACB, 0x1ACC, 0x1ACD, 0x1ACE, 0x1B00, 0x1B01, 0x1B02,
+
0x1B03, 0x1B34, 0x1B36, 0x1B37, 0x1B38, 0x1B39, 0x1B3A, 0x1B3C,
+
0x1B42, 0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71,
+
0x1B72, 0x1B73, 0x1B80, 0x1B81, 0x1BA2, 0x1BA3, 0x1BA4, 0x1BA5,
+
0x1BA8, 0x1BA9, 0x1BAB, 0x1BAC, 0x1BAD, 0x1BE6, 0x1BE8, 0x1BE9,
+
0x1BED, 0x1BEF, 0x1BF0, 0x1BF1, 0x1C2C, 0x1C2D, 0x1C2E, 0x1C2F,
+
0x1C30, 0x1C31, 0x1C32, 0x1C33, 0x1C36, 0x1C37, 0x1CD0, 0x1CD1,
+
0x1CD2, 0x1CD4, 0x1CD5, 0x1CD6, 0x1CD7, 0x1CD8, 0x1CD9, 0x1CDA,
+
0x1CDB, 0x1CDC, 0x1CDD, 0x1CDE, 0x1CDF, 0x1CE0, 0x1CE2, 0x1CE3,
+
0x1CE4, 0x1CE5, 0x1CE6, 0x1CE7, 0x1CE8, 0x1CED, 0x1CF4, 0x1CF8,
+
0x1CF9, 0x1DC0, 0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, 0x1DC5, 0x1DC6,
+
0x1DC7, 0x1DC8, 0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, 0x1DCD, 0x1DCE,
+
0x1DCF, 0x1DD0, 0x1DD1, 0x1DD2, 0x1DD3, 0x1DD4, 0x1DD5, 0x1DD6,
+
0x1DD7, 0x1DD8, 0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, 0x1DDD, 0x1DDE,
+
0x1DDF, 0x1DE0, 0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, 0x1DE5, 0x1DE6,
+
0x1DE7, 0x1DE8, 0x1DE9, 0x1DEA, 0x1DEB, 0x1DEC, 0x1DED, 0x1DEE,
+
0x1DEF, 0x1DF0, 0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, 0x1DF5, 0x1DF6,
+
0x1DF7, 0x1DF8, 0x1DF9, 0x1DFA, 0x1DFB, 0x1DFC, 0x1DFD, 0x1DFE,
+
0x1DFF, 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6,
+
0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20E1, 0x20E5,
+
0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED,
+
0x20EE, 0x20EF, 0x20F0, 0x2CEF, 0x2CF0, 0x2CF1, 0x2D7F, 0x2DE0,
+
0x2DE1, 0x2DE2, 0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6, 0x2DE7, 0x2DE8,
+
0x2DE9, 0x2DEA, 0x2DEB, 0x2DEC, 0x2DED, 0x2DEE, 0x2DEF, 0x2DF0,
+
0x2DF1, 0x2DF2, 0x2DF3, 0x2DF4, 0x2DF5, 0x2DF6, 0x2DF7, 0x2DF8,
+
0x2DF9, 0x2DFA, 0x2DFB, 0x2DFC, 0x2DFD, 0x2DFE, 0x2DFF, 0x302A,
+
0x302B, 0x302C, 0x302D, 0x3099, 0x309A, 0xA66F, 0xA674, 0xA675,
+
0xA676, 0xA677, 0xA678, 0xA679, 0xA67A, 0xA67B, 0xA67C, 0xA67D,
+
0xA69E, 0xA69F, 0xA6F0, 0xA6F1, 0xA802, 0xA806, 0xA80B, 0xA825,
+
0xA826, 0xA82C, 0xA8C4, 0xA8C5, 0xA8E0, 0xA8E1, 0xA8E2, 0xA8E3,
+
0xA8E4, 0xA8E5, 0xA8E6, 0xA8E7, 0xA8E8, 0xA8E9, 0xA8EA, 0xA8EB,
+
0xA8EC, 0xA8ED, 0xA8EE, 0xA8EF, 0xA8F0, 0xA8F1, 0xA8FF, 0xA926,
+
0xA927, 0xA928, 0xA929, 0xA92A, 0xA92B, 0xA92C, 0xA92D, 0xA947,
+
0xA948, 0xA949, 0xA94A, 0xA94B, 0xA94C, 0xA94D, 0xA94E, 0xA94F,
+
0xA950, 0xA951, 0xA980, 0xA981, 0xA982, 0xA9B3, 0xA9B6, 0xA9B7,
+
0xA9B8, 0xA9B9, 0xA9BC, 0xA9BD, 0xA9E5, 0xAA29, 0xAA2A, 0xAA2B,
+
0xAA2C, 0xAA2D, 0xAA2E, 0xAA31, 0xAA32, 0xAA35, 0xAA36, 0xAA43,
+
0xAA4C, 0xAA7C, 0xAAB0, 0xAAB2, 0xAAB3, 0xAAB4, 0xAAB7, 0xAAB8,
+
0xAABE, 0xAABF, 0xAAC1, 0xAAEC, 0xAAED, 0xAAF6, 0xABE5, 0xABE8,
+
0xABED, 0xFB1E, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05,
+
0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D,
+
0xFE0E, 0xFE0F, 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25,
+
0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D,
+
0xFE2E, 0xFE2F, 0x101FD, 0x102E0, 0x10376, 0x10377, 0x10378, 0x10379,
+
0x1037A, 0x10A01, 0x10A02, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A0D,
+
0x10A0E, 0x10A0F, 0x10A38, 0x10A39, 0x10A3A, 0x10A3F, 0x10AE5, 0x10AE6,
+
0x10D24, 0x10D25, 0x10D26, 0x10D27, 0x10D69, 0x10D6A, 0x10D6B, 0x10D6C,
+
0x10D6D, 0x10EAB, 0x10EAC, 0x10EFC, 0x10EFD, 0x10EFE, 0x10EFF, 0x10F46,
+
0x10F47, 0x10F48, 0x10F49, 0x10F4A, 0x10F4B, 0x10F4C, 0x10F4D, 0x10F4E,
+
0x10F4F, 0x10F50, 0x10F82, 0x10F83, 0x10F84, 0x10F85, 0x11001, 0x11038,
+
0x11039, 0x1103A, 0x1103B, 0x1103C, 0x1103D, 0x1103E, 0x1103F, 0x11040,
+
0x11041, 0x11042, 0x11043, 0x11044, 0x11045, 0x11046, 0x11070, 0x11073,
+
0x11074, 0x1107F, 0x11080, 0x11081, 0x110B3, 0x110B4, 0x110B5, 0x110B6,
+
0x110B9, 0x110BA, 0x110C2, 0x11100, 0x11101, 0x11102, 0x11127, 0x11128,
+
0x11129, 0x1112A, 0x1112B, 0x1112D, 0x1112E, 0x1112F, 0x11130, 0x11131,
+
0x11132, 0x11133, 0x11134, 0x11173, 0x11180, 0x11181, 0x111B6, 0x111B7,
+
0x111B8, 0x111B9, 0x111BA, 0x111BB, 0x111BC, 0x111BD, 0x111BE, 0x111C9,
+
0x111CA, 0x111CB, 0x111CC, 0x111CF, 0x1122F, 0x11230, 0x11231, 0x11234,
+
0x11236, 0x11237, 0x1123E, 0x11241, 0x112DF, 0x112E3, 0x112E4, 0x112E5,
+
0x112E6, 0x112E7, 0x112E8, 0x112E9, 0x112EA, 0x11300, 0x11301, 0x1133B,
+
0x1133C, 0x11340, 0x11366, 0x11367, 0x11368, 0x11369, 0x1136A, 0x1136B,
+
0x1136C, 0x11370, 0x11371, 0x11372, 0x11373, 0x11374, 0x113BB, 0x113BC,
+
0x113BD, 0x113BE, 0x113BF, 0x113C0, 0x113CE, 0x113D0, 0x113D2, 0x113E1,
+
0x113E2, 0x11438, 0x11439, 0x1143A, 0x1143B, 0x1143C, 0x1143D, 0x1143E,
+
0x1143F, 0x11442, 0x11443, 0x11444, 0x11446, 0x1145E, 0x114B3, 0x114B4,
+
0x114B5, 0x114B6, 0x114B7, 0x114B8, 0x114BA, 0x114BF, 0x114C0, 0x114C2,
+
0x114C3, 0x115B2, 0x115B3, 0x115B4, 0x115B5, 0x115BC, 0x115BD, 0x115BF,
+
0x115C0, 0x115DC, 0x115DD, 0x11633, 0x11634, 0x11635, 0x11636, 0x11637,
+
0x11638, 0x11639, 0x1163A, 0x1163D, 0x1163F, 0x11640, 0x116AB, 0x116AD,
+
0x116B0, 0x116B1, 0x116B2, 0x116B3, 0x116B4, 0x116B5, 0x116B7, 0x1171D,
+
0x1171F, 0x11722, 0x11723, 0x11724, 0x11725, 0x11727, 0x11728, 0x11729,
+
0x1172A, 0x1172B, 0x1182F, 0x11830, 0x11831, 0x11832, 0x11833, 0x11834,
+
0x11835, 0x11836, 0x11837, 0x11839, 0x1183A, 0x1193B, 0x1193C, 0x1193E,
+
0x11943, 0x119D4, 0x119D5, 0x119D6, 0x119D7, 0x119DA, 0x119DB, 0x119E0,
+
0x11A01, 0x11A02, 0x11A03, 0x11A04, 0x11A05, 0x11A06, 0x11A07, 0x11A08,
+
0x11A09, 0x11A0A, 0x11A33, 0x11A34, 0x11A35, 0x11A36, 0x11A37, 0x11A38,
+
0x11A3B, 0x11A3C, 0x11A3D, 0x11A3E, 0x11A47, 0x11A51, 0x11A52, 0x11A53,
+
0x11A54, 0x11A55, 0x11A56, 0x11A59, 0x11A5A, 0x11A5B, 0x11A8A, 0x11A8B,
+
0x11A8C, 0x11A8D, 0x11A8E, 0x11A8F, 0x11A90, 0x11A91, 0x11A92, 0x11A93,
+
0x11A94, 0x11A95, 0x11A96, 0x11A98, 0x11A99, 0x11C30, 0x11C31, 0x11C32,
+
0x11C33, 0x11C34, 0x11C35, 0x11C36, 0x11C38, 0x11C39, 0x11C3A, 0x11C3B,
+
0x11C3C, 0x11C3D, 0x11C3F, 0x11C92, 0x11C93, 0x11C94, 0x11C95, 0x11C96,
+
0x11C97, 0x11C98, 0x11C99, 0x11C9A, 0x11C9B, 0x11C9C, 0x11C9D, 0x11C9E,
+
0x11C9F, 0x11CA0, 0x11CA1, 0x11CA2, 0x11CA3, 0x11CA4, 0x11CA5, 0x11CA6,
+
0x11CA7, 0x11CAA, 0x11CAB, 0x11CAC, 0x11CAD, 0x11CAE, 0x11CAF, 0x11CB0,
+
0x11CB2, 0x11CB3, 0x11CB5, 0x11CB6, 0x11D31, 0x11D32, 0x11D33, 0x11D34,
+
0x11D35, 0x11D36, 0x11D3A, 0x11D3C, 0x11D3D, 0x11D3F, 0x11D40, 0x11D41,
+
0x11D42, 0x11D43, 0x11D44, 0x11D45, 0x11D47, 0x11D90, 0x11D91, 0x11D95,
+
0x11D97, 0x11EF3, 0x11EF4, 0x11F00, 0x11F01, 0x11F36, 0x11F37, 0x11F38,
+
0x11F39, 0x11F3A, 0x11F40, 0x11F42, 0x11F5A, 0x13440, 0x13447, 0x13448,
+
0x13449, 0x1344A, 0x1344B, 0x1344C, 0x1344D, 0x1344E, 0x1344F, 0x13450,
+
0x13451, 0x13452, 0x13453, 0x13454, 0x13455, 0x1611E, 0x1611F, 0x16120,
+
0x16121, 0x16122, 0x16123, 0x16124, 0x16125, 0x16126, 0x16127, 0x16128,
+
0x16129, 0x1612D, 0x1612E, 0x1612F, 0x16AF0, 0x16AF1, 0x16AF2, 0x16AF3,
+
0x16AF4, 0x16B30, 0x16B31, 0x16B32, 0x16B33, 0x16B34, 0x16B35, 0x16B36,
+
0x16F4F, 0x16F8F, 0x16F90, 0x16F91, 0x16F92, 0x16FE4, 0x1BC9D, 0x1BC9E,
+
0x1CF00, 0x1CF01, 0x1CF02, 0x1CF03, 0x1CF04, 0x1CF05, 0x1CF06, 0x1CF07,
+
0x1CF08, 0x1CF09, 0x1CF0A, 0x1CF0B, 0x1CF0C, 0x1CF0D, 0x1CF0E, 0x1CF0F,
+
0x1CF10, 0x1CF11, 0x1CF12, 0x1CF13, 0x1CF14, 0x1CF15, 0x1CF16, 0x1CF17,
+
0x1CF18, 0x1CF19, 0x1CF1A, 0x1CF1B, 0x1CF1C, 0x1CF1D, 0x1CF1E, 0x1CF1F,
+
0x1CF20, 0x1CF21, 0x1CF22, 0x1CF23, 0x1CF24, 0x1CF25, 0x1CF26, 0x1CF27,
+
0x1CF28, 0x1CF29, 0x1CF2A, 0x1CF2B, 0x1CF2C, 0x1CF2D, 0x1CF30, 0x1CF31,
+
0x1CF32, 0x1CF33, 0x1CF34, 0x1CF35, 0x1CF36, 0x1CF37, 0x1CF38, 0x1CF39,
+
0x1CF3A, 0x1CF3B, 0x1CF3C, 0x1CF3D, 0x1CF3E, 0x1CF3F, 0x1CF40, 0x1CF41,
+
0x1CF42, 0x1CF43, 0x1CF44, 0x1CF45, 0x1CF46, 0x1D167, 0x1D168, 0x1D169,
+
0x1D17B, 0x1D17C, 0x1D17D, 0x1D17E, 0x1D17F, 0x1D180, 0x1D181, 0x1D182,
+
0x1D185, 0x1D186, 0x1D187, 0x1D188, 0x1D189, 0x1D18A, 0x1D18B, 0x1D1AA,
+
0x1D1AB, 0x1D1AC, 0x1D1AD, 0x1D242, 0x1D243, 0x1D244, 0x1DA00, 0x1DA01,
+
0x1DA02, 0x1DA03, 0x1DA04, 0x1DA05, 0x1DA06, 0x1DA07, 0x1DA08, 0x1DA09,
+
0x1DA0A, 0x1DA0B, 0x1DA0C, 0x1DA0D, 0x1DA0E, 0x1DA0F, 0x1DA10, 0x1DA11,
+
0x1DA12, 0x1DA13, 0x1DA14, 0x1DA15, 0x1DA16, 0x1DA17, 0x1DA18, 0x1DA19,
+
0x1DA1A, 0x1DA1B, 0x1DA1C, 0x1DA1D, 0x1DA1E, 0x1DA1F, 0x1DA20, 0x1DA21,
+
0x1DA22, 0x1DA23, 0x1DA24, 0x1DA25, 0x1DA26, 0x1DA27, 0x1DA28, 0x1DA29,
+
0x1DA2A, 0x1DA2B, 0x1DA2C, 0x1DA2D, 0x1DA2E, 0x1DA2F, 0x1DA30, 0x1DA31,
+
0x1DA32, 0x1DA33, 0x1DA34, 0x1DA35, 0x1DA36, 0x1DA3B, 0x1DA3C, 0x1DA3D,
+
0x1DA3E, 0x1DA3F, 0x1DA40, 0x1DA41, 0x1DA42, 0x1DA43, 0x1DA44, 0x1DA45,
+
0x1DA46, 0x1DA47, 0x1DA48, 0x1DA49, 0x1DA4A, 0x1DA4B, 0x1DA4C, 0x1DA4D,
+
0x1DA4E, 0x1DA4F, 0x1DA50, 0x1DA51, 0x1DA52, 0x1DA53, 0x1DA54, 0x1DA55,
+
0x1DA56, 0x1DA57, 0x1DA58, 0x1DA59, 0x1DA5A, 0x1DA5B, 0x1DA5C, 0x1DA5D,
+
0x1DA5E, 0x1DA5F, 0x1DA60, 0x1DA61, 0x1DA62, 0x1DA63, 0x1DA64, 0x1DA65,
+
0x1DA66, 0x1DA67, 0x1DA68, 0x1DA69, 0x1DA6A, 0x1DA6B, 0x1DA6C, 0x1DA75,
+
0x1DA84, 0x1DA9B, 0x1DA9C, 0x1DA9D, 0x1DA9E, 0x1DA9F, 0x1DAA1, 0x1DAA2,
+
0x1DAA3, 0x1DAA4, 0x1DAA5, 0x1DAA6, 0x1DAA7, 0x1DAA8, 0x1DAA9, 0x1DAAA,
+
0x1DAAB, 0x1DAAC, 0x1DAAD, 0x1DAAE, 0x1DAAF, 0x1E000, 0x1E001, 0x1E002,
+
0x1E003, 0x1E004, 0x1E005, 0x1E006, 0x1E008, 0x1E009, 0x1E00A, 0x1E00B,
+
0x1E00C, 0x1E00D, 0x1E00E, 0x1E00F, 0x1E010, 0x1E011, 0x1E012, 0x1E013,
+
0x1E014, 0x1E015, 0x1E016, 0x1E017, 0x1E018, 0x1E01B, 0x1E01C, 0x1E01D,
+
0x1E01E, 0x1E01F, 0x1E020, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E027,
+
0x1E028, 0x1E029, 0x1E02A, 0x1E08F, 0x1E130, 0x1E131, 0x1E132, 0x1E133,
+
0x1E134, 0x1E135, 0x1E136, 0x1E2AE, 0x1E2EC, 0x1E2ED, 0x1E2EE, 0x1E2EF,
+
0x1E4EC, 0x1E4ED, 0x1E4EE, 0x1E4EF, 0x1E5EE, 0x1E5EF, 0x1E8D0, 0x1E8D1,
+
0x1E8D2, 0x1E8D3, 0x1E8D4, 0x1E8D5, 0x1E8D6, 0x1E944, 0x1E945, 0x1E946,
+
0x1E947, 0x1E948, 0x1E949, 0x1E94A, 0xE0100, 0xE0101, 0xE0102, 0xE0103,
+
0xE0104, 0xE0105, 0xE0106, 0xE0107, 0xE0108, 0xE0109, 0xE010A, 0xE010B,
+
0xE010C, 0xE010D, 0xE010E, 0xE010F, 0xE0110, 0xE0111, 0xE0112, 0xE0113,
+
0xE0114, 0xE0115, 0xE0116, 0xE0117, 0xE0118, 0xE0119, 0xE011A, 0xE011B,
+
0xE011C, 0xE011D, 0xE011E, 0xE011F, 0xE0120, 0xE0121, 0xE0122, 0xE0123,
+
0xE0124, 0xE0125, 0xE0126, 0xE0127, 0xE0128, 0xE0129, 0xE012A, 0xE012B,
+
0xE012C, 0xE012D, 0xE012E, 0xE012F, 0xE0130, 0xE0131, 0xE0132, 0xE0133,
+
0xE0134, 0xE0135, 0xE0136, 0xE0137, 0xE0138, 0xE0139, 0xE013A, 0xE013B,
+
0xE013C, 0xE013D, 0xE013E, 0xE013F, 0xE0140, 0xE0141, 0xE0142, 0xE0143,
+
0xE0144, 0xE0145, 0xE0146, 0xE0147, 0xE0148, 0xE0149, 0xE014A, 0xE014B,
+
0xE014C, 0xE014D, 0xE014E, 0xE014F, 0xE0150, 0xE0151, 0xE0152, 0xE0153,
+
0xE0154, 0xE0155, 0xE0156, 0xE0157, 0xE0158, 0xE0159, 0xE015A, 0xE015B,
+
0xE015C, 0xE015D, 0xE015E, 0xE015F, 0xE0160, 0xE0161, 0xE0162, 0xE0163,
+
0xE0164, 0xE0165, 0xE0166, 0xE0167, 0xE0168, 0xE0169, 0xE016A, 0xE016B,
+
0xE016C, 0xE016D, 0xE016E, 0xE016F, 0xE0170, 0xE0171, 0xE0172, 0xE0173,
+
0xE0174, 0xE0175, 0xE0176, 0xE0177, 0xE0178, 0xE0179, 0xE017A, 0xE017B,
+
0xE017C, 0xE017D, 0xE017E, 0xE017F, 0xE0180, 0xE0181, 0xE0182, 0xE0183,
+
0xE0184, 0xE0185, 0xE0186, 0xE0187, 0xE0188, 0xE0189, 0xE018A, 0xE018B,
+
0xE018C, 0xE018D, 0xE018E, 0xE018F, 0xE0190, 0xE0191, 0xE0192, 0xE0193,
+
0xE0194, 0xE0195, 0xE0196, 0xE0197, 0xE0198, 0xE0199, 0xE019A, 0xE019B,
+
0xE019C, 0xE019D, 0xE019E, 0xE019F, 0xE01A0, 0xE01A1, 0xE01A2, 0xE01A3,
+
0xE01A4, 0xE01A5, 0xE01A6, 0xE01A7, 0xE01A8, 0xE01A9, 0xE01AA, 0xE01AB,
+
0xE01AC, 0xE01AD, 0xE01AE, 0xE01AF, 0xE01B0, 0xE01B1, 0xE01B2, 0xE01B3,
+
0xE01B4, 0xE01B5, 0xE01B6, 0xE01B7, 0xE01B8, 0xE01B9, 0xE01BA, 0xE01BB,
+
0xE01BC, 0xE01BD, 0xE01BE, 0xE01BF, 0xE01C0, 0xE01C1, 0xE01C2, 0xE01C3,
+
0xE01C4, 0xE01C5, 0xE01C6, 0xE01C7, 0xE01C8, 0xE01C9, 0xE01CA, 0xE01CB,
+
0xE01CC, 0xE01CD, 0xE01CE, 0xE01CF, 0xE01D0, 0xE01D1, 0xE01D2, 0xE01D3,
+
0xE01D4, 0xE01D5, 0xE01D6, 0xE01D7, 0xE01D8, 0xE01D9, 0xE01DA, 0xE01DB,
+
0xE01DC, 0xE01DD, 0xE01DE, 0xE01DF, 0xE01E0, 0xE01E1, 0xE01E2, 0xE01E3,
+
0xE01E4, 0xE01E5, 0xE01E6, 0xE01E7, 0xE01E8, 0xE01E9, 0xE01EA, 0xE01EB,
+
0xE01EC, 0xE01ED, 0xE01EE, 0xE01EF,
+
};
+
+
static unsigned long combiningCharTableSize = sizeof(combiningCharTable) / sizeof(combiningCharTable[0]);
+
+
/* Check if the code is a wide character
+
*/
+
static int isWideChar(unsigned long cp) {
+
size_t i;
+
for (i = 0; i < wideCharTableSize; i++)
+
if (wideCharTable[i][0] <= cp && cp <= wideCharTable[i][1]) return 1;
+
return 0;
+
}
+
+
/* Check if the code is a combining character
+
*/
+
static int isCombiningChar(unsigned long cp) {
+
size_t i;
+
for (i = 0; i < combiningCharTableSize; i++)
+
if (combiningCharTable[i] == cp) return 1;
+
return 0;
+
}
+
+
/* Get length of previous UTF8 character
+
*/
+
static size_t prevUtf8CharLen(const char* buf, int pos) {
+
int end = pos--;
+
while (pos >= 0 && ((unsigned char)buf[pos] & 0xC0) == 0x80)
+
pos--;
+
return end - pos;
+
}
+
+
/* Convert UTF8 to Unicode code point
+
*/
+
static size_t utf8BytesToCodePoint(const char* buf, size_t len, int* cp) {
+
if (len) {
+
unsigned char byte = buf[0];
+
if ((byte & 0x80) == 0) {
+
*cp = byte;
+
return 1;
+
} else if ((byte & 0xE0) == 0xC0) {
+
if (len >= 2) {
+
*cp = (((unsigned long)(buf[0] & 0x1F)) << 6) |
+
((unsigned long)(buf[1] & 0x3F));
+
return 2;
+
}
+
} else if ((byte & 0xF0) == 0xE0) {
+
if (len >= 3) {
+
*cp = (((unsigned long)(buf[0] & 0x0F)) << 12) |
+
(((unsigned long)(buf[1] & 0x3F)) << 6) |
+
((unsigned long)(buf[2] & 0x3F));
+
return 3;
+
}
+
} else if ((byte & 0xF8) == 0xF0) {
+
if (len >= 4) {
+
*cp = (((unsigned long)(buf[0] & 0x07)) << 18) |
+
(((unsigned long)(buf[1] & 0x3F)) << 12) |
+
(((unsigned long)(buf[2] & 0x3F)) << 6) |
+
((unsigned long)(buf[3] & 0x3F));
+
return 4;
+
}
+
}
+
}
+
return 0;
+
}
+
+
/* Get length of next grapheme
+
*/
+
size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) {
+
size_t beg = pos;
+
int cp;
+
size_t len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp);
+
if (isCombiningChar(cp)) {
+
/* NOTREACHED */
+
return 0;
+
}
+
if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1;
+
pos += len;
+
while (pos < buf_len) {
+
int cp;
+
len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp);
+
if (!isCombiningChar(cp)) return pos - beg;
+
pos += len;
+
}
+
return pos - beg;
+
}
+
+
/* Get length of previous grapheme
+
*/
+
size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) {
+
UNUSED(buf_len);
+
size_t end = pos;
+
while (pos > 0) {
+
size_t len = prevUtf8CharLen(buf, pos);
+
pos -= len;
+
int cp;
+
utf8BytesToCodePoint(buf + pos, len, &cp);
+
if (!isCombiningChar(cp)) {
+
if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1;
+
return end - pos;
+
}
+
}
+
/* NOTREACHED */
+
return 0;
+
}
+
+
/* Read a Unicode from file.
+
*/
+
size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp) {
+
if (buf_len < 1) return -1;
+
size_t nread = read(fd,&buf[0],1);
+
if (nread <= 0) return nread;
+
+
unsigned char byte = buf[0];
+
if ((byte & 0x80) == 0) {
+
;
+
} else if ((byte & 0xE0) == 0xC0) {
+
if (buf_len < 2) return -1;
+
nread = read(fd,&buf[1],1);
+
if (nread <= 0) return nread;
+
} else if ((byte & 0xF0) == 0xE0) {
+
if (buf_len < 3) return -1;
+
nread = read(fd,&buf[1],2);
+
if (nread <= 0) return nread;
+
} else if ((byte & 0xF8) == 0xF0) {
+
if (buf_len < 3) return -1;
+
nread = read(fd,&buf[1],3);
+
if (nread <= 0) return nread;
+
} else {
+
return -1;
+
}
+
+
return utf8BytesToCodePoint(buf, buf_len, cp);
+
}
+55
vendor/ocaml-linenoise/src/utf8.h
···
+
/* encodings/utf8.h -- VERSION 1.0
+
*
+
* Guerrilla line editing library against the idea that a line editing lib
+
* needs to be 20,000 lines of C code.
+
*
+
* See linenoise.c for more information.
+
*
+
* ------------------------------------------------------------------------
+
*
+
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+
*
+
* All rights reserved.
+
*
+
* Redistribution and use in source and binary forms, with or without
+
* modification, are permitted provided that the following conditions are
+
* met:
+
*
+
* * Redistributions of source code must retain the above copyright
+
* notice, this list of conditions and the following disclaimer.
+
*
+
* * Redistributions in binary form must reproduce the above copyright
+
* notice, this list of conditions and the following disclaimer in the
+
* documentation and/or other materials provided with the distribution.
+
*
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
*/
+
+
#ifndef __LINENOISE_ENCODINGS_UTF8_H
+
#define __LINENOISE_ENCODINGS_UTF8_H
+
+
#ifdef __cplusplus
+
extern "C" {
+
#endif
+
+
size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len);
+
size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len);
+
size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp);
+
+
#ifdef __cplusplus
+
}
+
#endif
+
+
#endif /* __LINENOISE_ENCODINGS_UTF8_H */
+