Shells in OCaml
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

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!

+2734 -39
+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 +