Simple interactive mode

Shells are always more fun when there is an interactive mode to play
around with. Here we vendor Shelter's ANSI-ready copy of ocaml-linenoise
and add a simple interactive loop to osh!

+32 -25
src/bin/main.ml
··· 1 1 (* A shell... one day *) 2 2 3 - module Shell = Merry.Eval.Make (Merry_posix.State) (Merry_posix.Exec) 3 + module C = Merry.Eval.Make (Merry_posix.State) (Merry_posix.Exec) 4 + module I = Merry.Interactive.Make (Merry_posix.State) (Merry_posix.Exec) 4 5 5 6 let sh ~command ~dump ~file env = 6 - let ast = 7 - match (file, command) with 8 - | None, None -> 9 - Fmt.pr "Expected either a file path or a command to run..."; 10 - exit 1 11 - | Some file, None -> Merry.of_file Eio.Path.(env#fs / file) 12 - | _, Some c -> Merry.of_string c 7 + let executor = Merry_posix.Exec.{ mgr = env#process_mgr } in 8 + let ctx = 9 + C. 10 + { 11 + state = Merry_posix.State.make ~home:(Sys.getenv "HOME") (); 12 + executor; 13 + fs = env#fs; 14 + options = Merry.Eval.Options.default; 15 + } 13 16 in 14 - if dump then Merry.Ast.Dump.pp Fmt.stdout ast 15 - else 16 - let _ctx, _ast = 17 - let executor = Merry_posix.Exec.{ mgr = env#process_mgr } in 18 - Shell.run 19 - Shell. 20 - { 21 - state = Merry_posix.State.make ~home:(Sys.getenv "HOME") (); 22 - executor; 23 - fs = env#fs; 24 - options = Merry.Eval.Options.default; 25 - } 26 - ast 27 - in 28 - () 17 + match (file, command) with 18 + | None, None -> I.run ctx 19 + | _ -> 20 + let ast = 21 + match (file, command) with 22 + | None, None -> assert false 23 + | Some file, None -> Merry.Ast.of_file Eio.Path.(env#fs / file) 24 + | _, Some c -> Merry.Ast.of_string c 25 + in 26 + if dump then Merry.Ast.Dump.pp Fmt.stdout ast 27 + else 28 + let _ctx, _ast = C.run ctx ast in 29 + () 29 30 30 31 open Cmdliner 31 32 open Cmdliner.Term.Syntax ··· 51 52 [ 52 53 `S Manpage.s_description; 53 54 `P 54 - "$(cmd) is first a foremost a shell written completely in the OCaml \ 55 - programming language"; 55 + "$(cmd) is first and foremost a shell written in the OCaml programming \ 56 + language. Like most shells, you can execute some series of commands \ 57 + either by specify a shell script file to run, by passing commands \ 58 + using the -c flag or by omitting both of these options and entering \ 59 + interactive mode."; 60 + `P 61 + "$(cmd) exists thanks to the Morbig static parser for the POSIX shell \ 62 + syntax."; 56 63 `S Manpage.s_bugs; 57 64 `P "Report bugs at https://tangled.org/patrick.sirref.org/merry/issues."; 58 65 ]
+6
src/lib/ast.ml
··· 732 732 let yjs = complete_commands_to_yojson v in 733 733 Yojson.Safe.pretty_print ppf yjs 734 734 end 735 + 736 + let of_string s = Morbig.parse_string "-" s |> of_program 737 + 738 + let of_file path = 739 + let fname = Eio.Path.native_exn path in 740 + Eio.Path.load path |> Morbig.parse_string fname |> of_program
+6
src/lib/ast.mli
··· 9 9 val of_program : Morbig.CST.program -> t 10 10 (** An AST from a Morbig program *) 11 11 12 + val of_string : string -> t 13 + (** Construct an AST from a string *) 14 + 15 + val of_file : _ Eio.Path.t -> t 16 + (** Construct an AST from a file path. *) 17 + 12 18 (** {2 Utilities} *) 13 19 14 20 (* class map : Ppxlib_traverse_builtins.map *)
+1 -1
src/lib/dune
··· 3 3 (public_name merry) 4 4 (preprocess 5 5 (pps ppx_deriving_yojson ppxlib.traverse)) 6 - (libraries eio eio.unix morbig yojson ppxlib)) 6 + (libraries eio eio.unix morbig yojson ppxlib linenoise))
+17
src/lib/interactive.ml
··· 1 + module Make (S : Types.State) (E : Types.Exec) = struct 2 + module Eval = Eval.Make (S) (E) 3 + 4 + let default_prompt _ = Fmt.str "%s >\t%!" (Unix.getcwd ()) 5 + 6 + let run ?(prompt = default_prompt) initial_ctx = 7 + let rec loop (ctx : Eval.ctx) = 8 + let p = prompt ctx.state in 9 + match LNoise.linenoise p with 10 + | None -> loop ctx 11 + | Some c -> 12 + let ast = Ast.of_string c in 13 + let ctx', _ast = Eval.run ctx ast in 14 + loop ctx' 15 + in 16 + loop initial_ctx 17 + end
+1 -6
src/lib/merry.ml
··· 1 1 module Ast = Ast 2 2 module Types = Types 3 3 module Eval = Eval 4 - 5 - let of_string s = Morbig.parse_string "-" s |> Ast.of_program 6 - 7 - let of_file path = 8 - let fname = Eio.Path.native_exn path in 9 - Eio.Path.load path |> Morbig.parse_string fname |> Ast.of_program 4 + module Interactive = Interactive 10 5 11 6 module Variable = struct 12 7 type t
+1 -7
src/lib/merry.mli
··· 1 1 module Ast = Ast 2 2 module Types = Types 3 - 4 - val of_string : string -> Ast.t 5 - (** Construct an AST from a string *) 6 - 7 - val of_file : _ Eio.Path.t -> Ast.t 8 - (** Construct an AST from a file path. *) 9 - 10 3 module Eval = Eval 4 + module Interactive = Interactive 11 5 12 6 module Variable : sig 13 7 type t
+1
vendor/ocaml-linenoise/.github/CODEOWNERS
··· 1 + * @c-cube
+49
vendor/ocaml-linenoise/.github/workflows/main.yml
··· 1 + name: build 2 + on: 3 + push: 4 + branches: 5 + - main 6 + pull_request: 7 + jobs: 8 + run: 9 + name: build 10 + strategy: 11 + fail-fast: false 12 + matrix: 13 + os: 14 + - ubuntu-latest 15 + - macos-latest 16 + - macos-13 17 + setup-version: 18 + - v2 19 + - v3 20 + ocaml-compiler: 21 + - 4.06.x 22 + - 4.14.x 23 + - 5.1.x 24 + exclude: 25 + - os: ubuntu-latest 26 + setup-version: v2 27 + - os: macos-13 28 + setup-version: v3 29 + - os: macos-latest 30 + setup-version: v2 31 + - os: macos-latest 32 + ocaml-compiler: 4.06.x 33 + runs-on: ${{ matrix.os }} 34 + steps: 35 + - uses: actions/checkout@v4 36 + - uses: ocaml/setup-ocaml@v2 37 + if: matrix.setup-version == 'v2' 38 + with: 39 + ocaml-compiler: ${{ matrix.ocaml-compiler }} 40 + allow-prerelease-opam: true 41 + - uses: ocaml/setup-ocaml@v3 42 + if: matrix.setup-version == 'v3' 43 + with: 44 + ocaml-compiler: ${{ matrix.ocaml-compiler }} 45 + allow-prerelease-opam: true 46 + - run: opam pin -n . 47 + - run: opam install -t . --deps-only 48 + - run: opam exec -- dune build --ignore-promoted-rules 49 + - run: opam exec -- dune runtest --ignore-promoted-rules
+15
vendor/ocaml-linenoise/.gitignore
··· 1 + _build 2 + *.docdir 3 + 4 + history.txt 5 + *.cmi 6 + *.cmt 7 + *.cmo 8 + *.cma 9 + *.cmx 10 + *.o 11 + T 12 + setup.data 13 + setup.log 14 + *.install 15 + .merlin
+10
vendor/ocaml-linenoise/CHANGES.md
··· 1 + # 1.5.1 2 + 3 + - fix a deadlock from 1.5 4 + 5 + # 1.5 6 + 7 + - release runtime lock when calling `lnoise` 8 + - fix potential memleaks and use of deprecate parts of 9 + the OCaml C API 10 + - remove dependency on `result`
+19
vendor/ocaml-linenoise/Makefile
··· 1 + 2 + all: build test 3 + 4 + build: 5 + @dune build @install 6 + 7 + test: 8 + @dune runtest --no-buffer --force 9 + 10 + example: 11 + @dune exec examples/show_off.exe 12 + 13 + clean: 14 + @dune clean 15 + 16 + doc: 17 + @dune build @doc 18 + 19 + .PHONY: all build test example clean doc
+70
vendor/ocaml-linenoise/README.md
··· 1 + Linenoise in OCaml 2 + -------------------- 3 + 4 + [![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) 5 + 6 + # Benefits 7 + 1. BSD licensed. 8 + 2. No system dependencies, no need for `readline` on your machine. 9 + 3. Related to 2, these bindings are self-contained, the source for 10 + `linenoise` is in this repo and compiled all together with the 11 + `OCaml`. 12 + 4. Written in OCaml + C. 13 + 5. Pretty cool hints feature, see the gif. 14 + 6. Additional features compared to linenoise, such as history search 15 + 16 + # Installation 17 + 18 + It is easy with `opam` 19 + 20 + ```shell 21 + $ opam install linenoise 22 + ``` 23 + 24 + See the pretty 25 + documentation [here](https://ocaml-community.github.io/ocaml-linenoise/) 26 + 27 + # Example code 28 + This example is also included in the repo under examples: 29 + 30 + <p align="center" style='min-width:100%'> 31 + <img style='min-width:100%' src='example.gif'/> 32 + </p> 33 + 34 + 35 + ```ocaml 36 + let rec user_input prompt cb = 37 + match LNoise.linenoise prompt with 38 + | None -> () 39 + | Some v -> 40 + cb v; 41 + user_input prompt cb 42 + 43 + let () = 44 + (* LNoise.set_multiline true; *) 45 + LNoise.set_hints_callback (fun line -> 46 + if line <> "git remote add " then None 47 + else Some (" <this is the remote name> <this is the remote URL>", 48 + LNoise.Yellow, 49 + true) 50 + ); 51 + LNoise.history_load ~filename:"history.txt" |> ignore; 52 + LNoise.history_set ~max_length:100 |> ignore; 53 + LNoise.set_completion_callback begin fun line_so_far ln_completions -> 54 + if line_so_far <> "" && line_so_far.[0] = 'h' then 55 + ["Hey"; "Howard"; "Hughes";"Hocus"] 56 + |> List.iter (LNoise.add_completion ln_completions); 57 + end; 58 + ["These are OCaml bindings to linenoise"; 59 + "get tab completion with <TAB>, type h then hit <TAB>"; 60 + "type quit to exit gracefully"; 61 + "By Edgar Aroutiounian\n"] 62 + |> List.iter print_endline; 63 + (fun from_user -> 64 + if from_user = "quit" then exit 0; 65 + LNoise.history_add from_user |> ignore; 66 + LNoise.history_save ~filename:"history.txt" |> ignore; 67 + Printf.sprintf "Got: %s" from_user |> print_endline 68 + ) 69 + |> user_input "test_program> " 70 + ```
+2
vendor/ocaml-linenoise/dune-project
··· 1 + (lang dune 1.1) 2 + (name linenoise)
vendor/ocaml-linenoise/example.gif

This is a binary file and will not be displayed.

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