musique/lib/bestline/bestline.c

3579 lines
133 KiB
C
Raw Normal View History

2022-06-11 19:57:56 +02:00
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi
Bestline Library for interactive pseudoteletypewriter command
sessions using ANSI Standard X3.64 control sequences
OVERVIEW
Bestline is a fork of linenoise (a popular readline alternative)
that fixes its bugs and adds the missing features while reducing
binary footprint (surprisingly) by removing bloated dependencies
which means you can finally have a permissively-licensed command
prompt w/ a 30kb footprint that's nearly as good as gnu readline
EXAMPLE
main() {
char *line;
while ((line = bestlineWithHistory("IN> ", "foo"))) {
fputs("OUT> ", stdout);
fputs(line, stdout);
fputs("\n", stdout);
free(line);
}
}
CHANGES
- Remove bell
- Add kill ring
- Fix flickering
- Add UTF-8 editing
- Add CTRL-R search
- Support unlimited lines
- Add parentheses awareness
- React to terminal resizing
- Don't generate .data section
- Support terminal flow control
- Make history loading 10x faster
- Make multiline mode the only mode
- Accommodate O_NONBLOCK file descriptors
- Restore raw mode on process foregrounding
- Make source code compatible with C++ compilers
- Fix corruption issues by using generalized parsing
- Implement nearly all GNU readline editing shortcuts
- Remove heavyweight dependencies like printf/sprintf
- Remove ISIG^CEAGAIN hack and use ephemeral handlers
- Support running on Windows in MinTTY or CMD.EXE on Win10+
- Support diacratics, русский, Ελληνικά, , ,
SHORTCUTS
CTRL-E END
CTRL-A START
CTRL-B BACK
CTRL-F FORWARD
CTRL-L CLEAR
CTRL-H BACKSPACE
CTRL-D DELETE
CTRL-Y YANK
CTRL-D EOF (IF EMPTY)
CTRL-N NEXT HISTORY
CTRL-P PREVIOUS HISTORY
CTRL-R SEARCH HISTORY
CTRL-G CANCEL SEARCH
ALT-< BEGINNING OF HISTORY
ALT-> END OF HISTORY
ALT-F FORWARD WORD
ALT-B BACKWARD WORD
CTRL-ALT-F FORWARD EXPR
CTRL-ALT-B BACKWARD EXPR
ALT-RIGHT FORWARD EXPR
ALT-LEFT BACKWARD EXPR
ALT-SHIFT-B BARF EXPR
ALT-SHIFT-S SLURP EXPR
ALT-SHIFT-R RAISE EXPR
CTRL-K KILL LINE FORWARDS
CTRL-U KILL LINE BACKWARDS
ALT-H KILL WORD BACKWARDS
CTRL-W KILL WORD BACKWARDS
CTRL-ALT-H KILL WORD BACKWARDS
ALT-D KILL WORD FORWARDS
ALT-Y ROTATE KILL RING AND YANK AGAIN
ALT-\ SQUEEZE ADJACENT WHITESPACE
CTRL-T TRANSPOSE
ALT-T TRANSPOSE WORD
ALT-U UPPERCASE WORD
ALT-L LOWERCASE WORD
ALT-C CAPITALIZE WORD
CTRL-Z SUSPEND PROCESS
CTRL-\ QUIT PROCESS
CTRL-S PAUSE OUTPUT
CTRL-Q UNPAUSE OUTPUT (IF PAUSED)
CTRL-Q ESCAPED INSERT
CTRL-SPACE SET MARK
CTRL-X CTRL-X GOTO MARK
PROTIP REMAP CAPS LOCK TO CTRL
Copyright 2018-2021 Justine Tunney <jtunney@gmail.com>
Copyright 2010-2016 Salvatore Sanfilippo <antirez@gmail.com>
Copyright 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "bestline.h"
#ifndef __COSMOPOLITAN__
#define _POSIX_C_SOURCE 1 /* so GCC builds in ANSI mode */
#define _XOPEN_SOURCE 700 /* so GCC builds in ANSI mode */
#define _DARWIN_C_SOURCE 1 /* so SIGWINCH / IUTF8 on XNU */
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <setjmp.h>
#include <poll.h>
#include <assert.h>
#include <signal.h>
#include <fcntl.h>
#include <limits.h>
#ifndef SIGWINCH
#define SIGWINCH 28 /* GNU/Systemd + XNU + FreeBSD + NetBSD + OpenBSD */
#endif
#ifndef IUTF8
#define IUTF8 0
#endif
#endif
__asm__(".ident\t\"\\n\\n\
Bestline (BSD-2)\\n\
Copyright 2018-2020 Justine Tunney <jtunney@gmail.com>\\n\
Copyright 2010-2016 Salvatore Sanfilippo <antirez@gmail.com>\\n\
Copyright 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>\"");
#ifndef BESTLINE_MAX_RING
#define BESTLINE_MAX_RING 8
#endif
#ifndef BESTLINE_MAX_HISTORY
#define BESTLINE_MAX_HISTORY 1024
#endif
#define BESTLINE_HISTORY_FIRST +BESTLINE_MAX_HISTORY
#define BESTLINE_HISTORY_PREV +1
#define BESTLINE_HISTORY_NEXT -1
#define BESTLINE_HISTORY_LAST -BESTLINE_MAX_HISTORY
#define Ctrl(C) ((C) ^ 0100)
#define Min(X, Y) ((Y) > (X) ? (X) : (Y))
#define Max(X, Y) ((Y) < (X) ? (X) : (Y))
#define Case(X, Y) case X: Y; break
#define Read16le(X) \
((255 & (X)[0]) << 000 | \
(255 & (X)[1]) << 010)
#define Read32le(X) \
((unsigned)(255 & (X)[0]) << 000 | \
(unsigned)(255 & (X)[1]) << 010 | \
(unsigned)(255 & (X)[2]) << 020 | \
(unsigned)(255 & (X)[3]) << 030)
struct abuf {
char *b;
unsigned len;
unsigned cap;
};
struct rune {
unsigned c;
unsigned n;
};
struct bestlineRing {
unsigned i;
char *p[BESTLINE_MAX_RING];
};
/* The bestlineState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct bestlineState {
int ifd; /* terminal stdin file descriptor */
int ofd; /* terminal stdout file descriptor */
struct winsize ws; /* rows and columns in terminal */
char *buf; /* edited line buffer */
const char *prompt; /* prompt to display */
int hindex; /* history index */
int rows; /* rows being used */
int oldpos; /* previous refresh cursor position */
unsigned buflen; /* edited line buffer size */
unsigned pos; /* current buffer index */
unsigned len; /* current edited line length */
unsigned mark; /* saved cursor position */
unsigned yi, yj; /* boundaries of last yank */
char seq[2][16]; /* keystroke history for yanking code */
char final; /* set to true on last update */
char dirty; /* if an update was squashed */
};
static const char *const kUnsupported[] = {"dumb","cons25","emacs"};
static int gotint;
static int gotcont;
static int gotwinch;
static signed char rawmode;
static char maskmode;
static char ispaused;
static char iscapital;
static unsigned historylen;
static struct bestlineRing ring;
static struct sigaction orig_cont;
static struct sigaction orig_winch;
static struct termios orig_termios;
static char *history[BESTLINE_MAX_HISTORY];
static bestlineXlatCallback *xlatCallback;
static bestlineHintsCallback *hintsCallback;
static bestlineFreeHintsCallback *freeHintsCallback;
static bestlineCompletionCallback *completionCallback;
static void bestlineAtExit(void);
static void bestlineRefreshLine(struct bestlineState *);
static void bestlineOnInt(int sig) {
gotint = sig;
}
static void bestlineOnCont(int sig) {
gotcont = sig;
}
static void bestlineOnWinch(int sig) {
gotwinch = sig;
}
static char IsControl(unsigned c) {
return c <= 0x1F || (0x7F <= c && c <= 0x9F);
}
static int GetMonospaceCharacterWidth(unsigned c) {
return !IsControl(c)
+ (c >= 0x1100 &&
(c <= 0x115f || c == 0x2329 || c == 0x232a ||
(c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) ||
(c >= 0xac00 && c <= 0xd7a3) ||
(c >= 0xf900 && c <= 0xfaff) ||
(c >= 0xfe10 && c <= 0xfe19) ||
(c >= 0xfe30 && c <= 0xfe6f) ||
(c >= 0xff00 && c <= 0xff60) ||
(c >= 0xffe0 && c <= 0xffe6) ||
(c >= 0x20000 && c <= 0x2fffd) ||
(c >= 0x30000 && c <= 0x3fffd)));
}
/**
* Returns nonzero if 𝑐 isn't alphanumeric.
*
* Line reading interfaces generally define this operation as UNICODE
* characters that aren't in the letter category (Lu, Ll, Lt, Lm, Lo)
* and aren't in the number categorie (Nd, Nl, No). We also add a few
* other things like blocks and emoji (So).
*/
char bestlineIsSeparator(unsigned c) {
int m, l, r, n;
if (c < 0200) {
return !(('0' <= c && c <= '9') ||
('A' <= c && c <= 'Z') ||
('a' <= c && c <= 'z'));
}
if (c <= 0xffff) {
static const unsigned short kGlyphs[][2] = {
{0x00aa, 0x00aa}, /* 1x English */
{0x00b2, 0x00b3}, /* 2x English Arabic */
{0x00b5, 0x00b5}, /* 1x Greek */
{0x00b9, 0x00ba}, /* 2x English Arabic */
{0x00bc, 0x00be}, /* 3x Vulgar English Arabic */
{0x00c0, 0x00d6}, /* 23x Watin */
{0x00d8, 0x00f6}, /* 31x Watin */
{0x0100, 0x02c1}, /* 450x Watin-AB,IPA,Spacemod */
{0x02c6, 0x02d1}, /* 12x Spacemod */
{0x02e0, 0x02e4}, /* 5x Spacemod */
{0x02ec, 0x02ec}, /* 1x Spacemod */
{0x02ee, 0x02ee}, /* 1x Spacemod */
{0x0370, 0x0374}, /* 5x Greek */
{0x0376, 0x0377}, /* 2x Greek */
{0x037a, 0x037d}, /* 4x Greek */
{0x037f, 0x037f}, /* 1x Greek */
{0x0386, 0x0386}, /* 1x Greek */
{0x0388, 0x038a}, /* 3x Greek */
{0x038c, 0x038c}, /* 1x Greek */
{0x038e, 0x03a1}, /* 20x Greek */
{0x03a3, 0x03f5}, /* 83x Greek */
{0x03f7, 0x0481}, /* 139x Greek */
{0x048a, 0x052f}, /* 166x Cyrillic */
{0x0531, 0x0556}, /* 38x Armenian */
{0x0560, 0x0588}, /* 41x Armenian */
{0x05d0, 0x05ea}, /* 27x Hebrew */
{0x0620, 0x064a}, /* 43x Arabic */
{0x0660, 0x0669}, /* 10x Arabic */
{0x0671, 0x06d3}, /* 99x Arabic */
{0x06ee, 0x06fc}, /* 15x Arabic */
{0x0712, 0x072f}, /* 30x Syriac */
{0x074d, 0x07a5}, /* 89x Syriac,Arabic2,Thaana */
{0x07c0, 0x07ea}, /* 43x NKo */
{0x0800, 0x0815}, /* 22x Samaritan */
{0x0840, 0x0858}, /* 25x Mandaic */
{0x0904, 0x0939}, /* 54x Devanagari */
{0x0993, 0x09a8}, /* 22x Bengali */
{0x09e6, 0x09f1}, /* 12x Bengali */
{0x0a13, 0x0a28}, /* 22x Gurmukhi */
{0x0a66, 0x0a6f}, /* 10x Gurmukhi */
{0x0a93, 0x0aa8}, /* 22x Gujarati */
{0x0b13, 0x0b28}, /* 22x Oriya */
{0x0c92, 0x0ca8}, /* 23x Kannada */
{0x0caa, 0x0cb3}, /* 10x Kannada */
{0x0ce6, 0x0cef}, /* 10x Kannada */
{0x0d12, 0x0d3a}, /* 41x Malayalam */
{0x0d85, 0x0d96}, /* 18x Sinhala */
{0x0d9a, 0x0db1}, /* 24x Sinhala */
{0x0de6, 0x0def}, /* 10x Sinhala */
{0x0e01, 0x0e30}, /* 48x Thai */
{0x0e8c, 0x0ea3}, /* 24x Lao */
{0x0f20, 0x0f33}, /* 20x Tibetan */
{0x0f49, 0x0f6c}, /* 36x Tibetan */
{0x109e, 0x10c5}, /* 40x Myanmar,Georgian */
{0x10d0, 0x10fa}, /* 43x Georgian */
{0x10fc, 0x1248}, /* 333x Georgian,Hangul,Ethiopic */
{0x13a0, 0x13f5}, /* 86x Cherokee */
{0x1401, 0x166d}, /* 621x Aboriginal */
{0x16a0, 0x16ea}, /* 75x Runic */
{0x1700, 0x170c}, /* 13x Tagalog */
{0x1780, 0x17b3}, /* 52x Khmer */
{0x1820, 0x1878}, /* 89x Mongolian */
{0x1a00, 0x1a16}, /* 23x Buginese */
{0x1a20, 0x1a54}, /* 53x Tai Tham */
{0x1a80, 0x1a89}, /* 10x Tai Tham */
{0x1a90, 0x1a99}, /* 10x Tai Tham */
{0x1b05, 0x1b33}, /* 47x Balinese */
{0x1b50, 0x1b59}, /* 10x Balinese */
{0x1b83, 0x1ba0}, /* 30x Sundanese */
{0x1bae, 0x1be5}, /* 56x Sundanese */
{0x1c90, 0x1cba}, /* 43x Georgian2 */
{0x1cbd, 0x1cbf}, /* 3x Georgian2 */
{0x1e00, 0x1f15}, /* 278x Watin-C,Greek2 */
{0x2070, 0x2071}, /* 2x Supersub */
{0x2074, 0x2079}, /* 6x Supersub */
{0x207f, 0x2089}, /* 11x Supersub */
{0x2090, 0x209c}, /* 13x Supersub */
{0x2100, 0x2117}, /* 24x Letterlike */
{0x2119, 0x213f}, /* 39x Letterlike */
{0x2145, 0x214a}, /* 6x Letterlike */
{0x214c, 0x218b}, /* 64x Letterlike,Numbery */
{0x21af, 0x21cd}, /* 31x Arrows */
{0x21d5, 0x21f3}, /* 31x Arrows */
{0x230c, 0x231f}, /* 20x Technical */
{0x232b, 0x237b}, /* 81x Technical */
{0x237d, 0x239a}, /* 30x Technical */
{0x23b4, 0x23db}, /* 40x Technical */
{0x23e2, 0x2426}, /* 69x Technical,ControlPictures */
{0x2460, 0x25b6}, /* 343x Enclosed,Boxes,Blocks,Shapes */
{0x25c2, 0x25f7}, /* 54x Shapes */
{0x2600, 0x266e}, /* 111x Symbols */
{0x2670, 0x2767}, /* 248x Symbols,Dingbats */
{0x2776, 0x27bf}, /* 74x Dingbats */
{0x2800, 0x28ff}, /* 256x Braille */
{0x2c00, 0x2c2e}, /* 47x Glagolitic */
{0x2c30, 0x2c5e}, /* 47x Glagolitic */
{0x2c60, 0x2ce4}, /* 133x Watin-D */
{0x2d00, 0x2d25}, /* 38x Georgian2 */
{0x2d30, 0x2d67}, /* 56x Tifinagh */
{0x2d80, 0x2d96}, /* 23x Ethiopic2 */
{0x2e2f, 0x2e2f}, /* 1x Punctuation2 */
{0x3005, 0x3007}, /* 3x CJK Symbols & Punctuation */
{0x3021, 0x3029}, /* 9x CJK Symbols & Punctuation */
{0x3031, 0x3035}, /* 5x CJK Symbols & Punctuation */
{0x3038, 0x303c}, /* 5x CJK Symbols & Punctuation */
{0x3041, 0x3096}, /* 86x Hiragana */
{0x30a1, 0x30fa}, /* 90x Katakana */
{0x3105, 0x312f}, /* 43x Bopomofo */
{0x3131, 0x318e}, /* 94x Hangul Compatibility Jamo */
{0x31a0, 0x31ba}, /* 27x Bopomofo Extended */
{0x31f0, 0x31ff}, /* 16x Katakana Phonetic Extensions */
{0x3220, 0x3229}, /* 10x Enclosed CJK Letters & Months */
{0x3248, 0x324f}, /* 8x Enclosed CJK Letters & Months */
{0x3251, 0x325f}, /* 15x Enclosed CJK Letters & Months */
{0x3280, 0x3289}, /* 10x Enclosed CJK Letters & Months */
{0x32b1, 0x32bf}, /* 15x Enclosed CJK Letters & Months */
{0x3400, 0x4db5}, /* 6582x CJK Unified Ideographs Extension A */
{0x4dc0, 0x9fef}, /* 21040x Yijing Hexagram, CJK Unified Ideographs */
{0xa000, 0xa48c}, /* 1165x Yi Syllables */
{0xa4d0, 0xa4fd}, /* 46x Lisu */
{0xa500, 0xa60c}, /* 269x Vai */
{0xa610, 0xa62b}, /* 28x Vai */
{0xa6a0, 0xa6ef}, /* 80x Bamum */
{0xa80c, 0xa822}, /* 23x Syloti Nagri */
{0xa840, 0xa873}, /* 52x Phags-pa */
{0xa882, 0xa8b3}, /* 50x Saurashtra */
{0xa8d0, 0xa8d9}, /* 10x Saurashtra */
{0xa900, 0xa925}, /* 38x Kayah Li */
{0xa930, 0xa946}, /* 23x Rejang */
{0xa960, 0xa97c}, /* 29x Hangul Jamo Extended-A */
{0xa984, 0xa9b2}, /* 47x Javanese */
{0xa9cf, 0xa9d9}, /* 11x Javanese */
{0xaa00, 0xaa28}, /* 41x Cham */
{0xaa50, 0xaa59}, /* 10x Cham */
{0xabf0, 0xabf9}, /* 10x Meetei Mayek */
{0xac00, 0xd7a3}, /* 11172x Hangul Syllables */
{0xf900, 0xfa6d}, /* 366x CJK Compatibility Ideographs */
{0xfa70, 0xfad9}, /* 106x CJK Compatibility Ideographs */
{0xfb1f, 0xfb28}, /* 10x Alphabetic Presentation Forms */
{0xfb2a, 0xfb36}, /* 13x Alphabetic Presentation Forms */
{0xfb46, 0xfbb1}, /* 108x Alphabetic Presentation Forms */
{0xfbd3, 0xfd3d}, /* 363x Arabic Presentation Forms-A */
{0xfe76, 0xfefc}, /* 135x Arabic Presentation Forms-B */
{0xff10, 0xff19}, /* 10x Dubs */
{0xff21, 0xff3a}, /* 26x Dubs */
{0xff41, 0xff5a}, /* 26x Dubs */
{0xff66, 0xffbe}, /* 89x Dubs */
{0xffc2, 0xffc7}, /* 6x Dubs */
{0xffca, 0xffcf}, /* 6x Dubs */
{0xffd2, 0xffd7}, /* 6x Dubs */
{0xffda, 0xffdc}, /* 3x Dubs */
};
l = 0;
r = n = sizeof(kGlyphs) / sizeof(kGlyphs[0]);
while (l < r) {
m = (l + r) >> 1;
if (kGlyphs[m][1] < c) {
l = m + 1;
} else {
r = m;
}
}
return !(l < n && kGlyphs[l][0] <= c && c <= kGlyphs[l][1]);
} else {
static const unsigned kAstralGlyphs[][2] = {
{0x10107, 0x10133}, /* 45x Aegean */
{0x10140, 0x10178}, /* 57x Ancient Greek Numbers */
{0x1018a, 0x1018b}, /* 2x Ancient Greek Numbers */
{0x10280, 0x1029c}, /* 29x Lycian */
{0x102a0, 0x102d0}, /* 49x Carian */
{0x102e1, 0x102fb}, /* 27x Coptic Epact Numbers */
{0x10300, 0x10323}, /* 36x Old Italic */
{0x1032d, 0x1034a}, /* 30x Old Italic, Gothic */
{0x10350, 0x10375}, /* 38x Old Permic */
{0x10380, 0x1039d}, /* 30x Ugaritic */
{0x103a0, 0x103c3}, /* 36x Old Persian */
{0x103c8, 0x103cf}, /* 8x Old Persian */
{0x103d1, 0x103d5}, /* 5x Old Persian */
{0x10400, 0x1049d}, /* 158x Deseret, Shavian, Osmanya */
{0x104b0, 0x104d3}, /* 36x Osage */
{0x104d8, 0x104fb}, /* 36x Osage */
{0x10500, 0x10527}, /* 40x Elbasan */
{0x10530, 0x10563}, /* 52x Caucasian Albanian */
{0x10600, 0x10736}, /* 311x Linear A */
{0x10800, 0x10805}, /* 6x Cypriot Syllabary */
{0x1080a, 0x10835}, /* 44x Cypriot Syllabary */
{0x10837, 0x10838}, /* 2x Cypriot Syllabary */
{0x1083f, 0x1089e}, /* 86x Cypriot,ImperialAramaic,Palmyrene,Nabataean */
{0x108e0, 0x108f2}, /* 19x Hatran */
{0x108f4, 0x108f5}, /* 2x Hatran */
{0x108fb, 0x1091b}, /* 33x Hatran */
{0x10920, 0x10939}, /* 26x Lydian */
{0x10980, 0x109b7}, /* 56x Meroitic Hieroglyphs */
{0x109bc, 0x109cf}, /* 20x Meroitic Cursive */
{0x109d2, 0x10a00}, /* 47x Meroitic Cursive */
{0x10a10, 0x10a13}, /* 4x Kharoshthi */
{0x10a15, 0x10a17}, /* 3x Kharoshthi */
{0x10a19, 0x10a35}, /* 29x Kharoshthi */
{0x10a40, 0x10a48}, /* 9x Kharoshthi */
{0x10a60, 0x10a7e}, /* 31x Old South Arabian */
{0x10a80, 0x10a9f}, /* 32x Old North Arabian */
{0x10ac0, 0x10ac7}, /* 8x Manichaean */
{0x10ac9, 0x10ae4}, /* 28x Manichaean */
{0x10aeb, 0x10aef}, /* 5x Manichaean */
{0x10b00, 0x10b35}, /* 54x Avestan */
{0x10b40, 0x10b55}, /* 22x Inscriptional Parthian */
{0x10b58, 0x10b72}, /* 27x Inscriptional Parthian and Pahlavi */
{0x10b78, 0x10b91}, /* 26x Inscriptional Pahlavi, Psalter Pahlavi */
{0x10c00, 0x10c48}, /* 73x Old Turkic */
{0x10c80, 0x10cb2}, /* 51x Old Hungarian */
{0x10cc0, 0x10cf2}, /* 51x Old Hungarian */
{0x10cfa, 0x10d23}, /* 42x Old Hungarian, Hanifi Rohingya */
{0x10d30, 0x10d39}, /* 10x Hanifi Rohingya */
{0x10e60, 0x10e7e}, /* 31x Rumi Numeral Symbols */
{0x10f00, 0x10f27}, /* 40x Old Sogdian */
{0x10f30, 0x10f45}, /* 22x Sogdian */
{0x10f51, 0x10f54}, /* 4x Sogdian */
{0x10fe0, 0x10ff6}, /* 23x Elymaic */
{0x11003, 0x11037}, /* 53x Brahmi */
{0x11052, 0x1106f}, /* 30x Brahmi */
{0x11083, 0x110af}, /* 45x Kaithi */
{0x110d0, 0x110e8}, /* 25x Sora Sompeng */
{0x110f0, 0x110f9}, /* 10x Sora Sompeng */
{0x11103, 0x11126}, /* 36x Chakma */
{0x11136, 0x1113f}, /* 10x Chakma */
{0x11144, 0x11144}, /* 1x Chakma */
{0x11150, 0x11172}, /* 35x Mahajani */
{0x11176, 0x11176}, /* 1x Mahajani */
{0x11183, 0x111b2}, /* 48x Sharada */
{0x111c1, 0x111c4}, /* 4x Sharada */
{0x111d0, 0x111da}, /* 11x Sharada */
{0x111dc, 0x111dc}, /* 1x Sharada */
{0x111e1, 0x111f4}, /* 20x Sinhala Archaic Numbers */
{0x11200, 0x11211}, /* 18x Khojki */
{0x11213, 0x1122b}, /* 25x Khojki */
{0x11280, 0x11286}, /* 7x Multani */
{0x11288, 0x11288}, /* 1x Multani */
{0x1128a, 0x1128d}, /* 4x Multani */
{0x1128f, 0x1129d}, /* 15x Multani */
{0x1129f, 0x112a8}, /* 10x Multani */
{0x112b0, 0x112de}, /* 47x Khudawadi */
{0x112f0, 0x112f9}, /* 10x Khudawadi */
{0x11305, 0x1130c}, /* 8x Grantha */
{0x1130f, 0x11310}, /* 2x Grantha */
{0x11313, 0x11328}, /* 22x Grantha */
{0x1132a, 0x11330}, /* 7x Grantha */
{0x11332, 0x11333}, /* 2x Grantha */
{0x11335, 0x11339}, /* 5x Grantha */
{0x1133d, 0x1133d}, /* 1x Grantha */
{0x11350, 0x11350}, /* 1x Grantha */
{0x1135d, 0x11361}, /* 5x Grantha */
{0x11400, 0x11434}, /* 53x Newa */
{0x11447, 0x1144a}, /* 4x Newa */
{0x11450, 0x11459}, /* 10x Newa */
{0x1145f, 0x1145f}, /* 1x Newa */
{0x11480, 0x114af}, /* 48x Tirhuta */
{0x114c4, 0x114c5}, /* 2x Tirhuta */
{0x114c7, 0x114c7}, /* 1x Tirhuta */
{0x114d0, 0x114d9}, /* 10x Tirhuta */
{0x11580, 0x115ae}, /* 47x Siddham */
{0x115d8, 0x115db}, /* 4x Siddham */
{0x11600, 0x1162f}, /* 48x Modi */
{0x11644, 0x11644}, /* 1x Modi */
{0x11650, 0x11659}, /* 10x Modi */
{0x11680, 0x116aa}, /* 43x Takri */
{0x116b8, 0x116b8}, /* 1x Takri */
{0x116c0, 0x116c9}, /* 10x Takri */
{0x11700, 0x1171a}, /* 27x Ahom */
{0x11730, 0x1173b}, /* 12x Ahom */
{0x11800, 0x1182b}, /* 44x Dogra */
{0x118a0, 0x118f2}, /* 83x Warang Citi */
{0x118ff, 0x118ff}, /* 1x Warang Citi */
{0x119a0, 0x119a7}, /* 8x Nandinagari */
{0x119aa, 0x119d0}, /* 39x Nandinagari */
{0x119e1, 0x119e1}, /* 1x Nandinagari */
{0x119e3, 0x119e3}, /* 1x Nandinagari */
{0x11a00, 0x11a00}, /* 1x Zanabazar Square */
{0x11a0b, 0x11a32}, /* 40x Zanabazar Square */
{0x11a3a, 0x11a3a}, /* 1x Zanabazar Square */
{0x11a50, 0x11a50}, /* 1x Soyombo */
{0x11a5c, 0x11a89}, /* 46x Soyombo */
{0x11a9d, 0x11a9d}, /* 1x Soyombo */
{0x11ac0, 0x11af8}, /* 57x Pau Cin Hau */
{0x11c00, 0x11c08}, /* 9x Bhaiksuki */
{0x11c0a, 0x11c2e}, /* 37x Bhaiksuki */
{0x11c40, 0x11c40}, /* 1x Bhaiksuki */
{0x11c50, 0x11c6c}, /* 29x Bhaiksuki */
{0x11c72, 0x11c8f}, /* 30x Marchen */
{0x11d00, 0x11d06}, /* 7x Masaram Gondi */
{0x11d08, 0x11d09}, /* 2x Masaram Gondi */
{0x11d0b, 0x11d30}, /* 38x Masaram Gondi */
{0x11d46, 0x11d46}, /* 1x Masaram Gondi */
{0x11d50, 0x11d59}, /* 10x Masaram Gondi */
{0x11d60, 0x11d65}, /* 6x Gunjala Gondi */
{0x11d67, 0x11d68}, /* 2x Gunjala Gondi */
{0x11d6a, 0x11d89}, /* 32x Gunjala Gondi */
{0x11d98, 0x11d98}, /* 1x Gunjala Gondi */
{0x11da0, 0x11da9}, /* 10x Gunjala Gondi */
{0x11ee0, 0x11ef2}, /* 19x Makasar */
{0x11fc0, 0x11fd4}, /* 21x Tamil Supplement */
{0x12000, 0x12399}, /* 922x Cuneiform */
{0x12400, 0x1246e}, /* 111x Cuneiform Numbers & Punctuation */
{0x12480, 0x12543}, /* 196x Early Dynastic Cuneiform */
{0x13000, 0x1342e}, /* 1071x Egyptian Hieroglyphs */
{0x14400, 0x14646}, /* 583x Anatolian Hieroglyphs */
{0x16800, 0x16a38}, /* 569x Bamum Supplement */
{0x16a40, 0x16a5e}, /* 31x Mro */
{0x16a60, 0x16a69}, /* 10x Mro */
{0x16ad0, 0x16aed}, /* 30x Bassa Vah */
{0x16b00, 0x16b2f}, /* 48x Pahawh Hmong */
{0x16b40, 0x16b43}, /* 4x Pahawh Hmong */
{0x16b50, 0x16b59}, /* 10x Pahawh Hmong */
{0x16b5b, 0x16b61}, /* 7x Pahawh Hmong */
{0x16b63, 0x16b77}, /* 21x Pahawh Hmong */
{0x16b7d, 0x16b8f}, /* 19x Pahawh Hmong */
{0x16e40, 0x16e96}, /* 87x Medefaidrin */
{0x16f00, 0x16f4a}, /* 75x Miao */
{0x16f50, 0x16f50}, /* 1x Miao */
{0x16f93, 0x16f9f}, /* 13x Miao */
{0x16fe0, 0x16fe1}, /* 2x Ideographic Symbols & Punctuation */
{0x16fe3, 0x16fe3}, /* 1x Ideographic Symbols & Punctuation */
{0x17000, 0x187f7}, /* 6136x Tangut */
{0x18800, 0x18af2}, /* 755x Tangut Components */
{0x1b000, 0x1b11e}, /* 287x Kana Supplement */
{0x1b150, 0x1b152}, /* 3x Small Kana Extension */
{0x1b164, 0x1b167}, /* 4x Small Kana Extension */
{0x1b170, 0x1b2fb}, /* 396x Nushu */
{0x1bc00, 0x1bc6a}, /* 107x Duployan */
{0x1bc70, 0x1bc7c}, /* 13x Duployan */
{0x1bc80, 0x1bc88}, /* 9x Duployan */
{0x1bc90, 0x1bc99}, /* 10x Duployan */
{0x1d2e0, 0x1d2f3}, /* 20x Mayan Numerals */
{0x1d360, 0x1d378}, /* 25x Counting Rod Numerals */
{0x1d400, 0x1d454}, /* 85x 𝐀..𝑔 Math */
{0x1d456, 0x1d49c}, /* 71x 𝑖..𝒜 Math */
{0x1d49e, 0x1d49f}, /* 2x 𝒞..𝒟 Math */
{0x1d4a2, 0x1d4a2}, /* 1x 𝒢..𝒢 Math */
{0x1d4a5, 0x1d4a6}, /* 2x 𝒥..𝒦 Math */
{0x1d4a9, 0x1d4ac}, /* 4x 𝒩..𝒬 Math */
{0x1d4ae, 0x1d4b9}, /* 12x 𝒮..𝒹 Math */
{0x1d4bb, 0x1d4bb}, /* 1x 𝒻..𝒻 Math */
{0x1d4bd, 0x1d4c3}, /* 7x 𝒽..𝓃 Math */
{0x1d4c5, 0x1d505}, /* 65x 𝓅..𝔅 Math */
{0x1d507, 0x1d50a}, /* 4x 𝔇..𝔊 Math */
{0x1d50d, 0x1d514}, /* 8x 𝔍..𝔔 Math */
{0x1d516, 0x1d51c}, /* 7x 𝔖..𝔜 Math */
{0x1d51e, 0x1d539}, /* 28x 𝔞..𝔹 Math */
{0x1d53b, 0x1d53e}, /* 4x 𝔻..𝔾 Math */
{0x1d540, 0x1d544}, /* 5x 𝕀..𝕄 Math */
{0x1d546, 0x1d546}, /* 1x 𝕆..𝕆 Math */
{0x1d54a, 0x1d550}, /* 7x 𝕊..𝕐 Math */
{0x1d552, 0x1d6a5}, /* 340x 𝕒..𝚥 Math */
{0x1d6a8, 0x1d6c0}, /* 25x 𝚨..𝛀 Math */
{0x1d6c2, 0x1d6da}, /* 25x 𝛂..𝛚 Math */
{0x1d6dc, 0x1d6fa}, /* 31x 𝛜..𝛺 Math */
{0x1d6fc, 0x1d714}, /* 25x 𝛼..𝜔 Math */
{0x1d716, 0x1d734}, /* 31x 𝜖..𝜴 Math */
{0x1d736, 0x1d74e}, /* 25x 𝜶..𝝎 Math */
{0x1d750, 0x1d76e}, /* 31x 𝝐..𝝮 Math */
{0x1d770, 0x1d788}, /* 25x 𝝰..𝞈 Math */
{0x1d78a, 0x1d7a8}, /* 31x 𝞊..𝞨 Math */
{0x1d7aa, 0x1d7c2}, /* 25x 𝞪..𝟂 Math */
{0x1d7c4, 0x1d7cb}, /* 8x 𝟄..𝟋 Math */
{0x1d7ce, 0x1d9ff}, /* 562x Math, Sutton SignWriting */
{0x1f100, 0x1f10c}, /* 13x Enclosed Alphanumeric Supplement */
{0x20000, 0x2a6d6}, /* 42711x CJK Unified Ideographs Extension B */
{0x2a700, 0x2b734}, /* 4149x CJK Unified Ideographs Extension C */
{0x2b740, 0x2b81d}, /* 222x CJK Unified Ideographs Extension D */
{0x2b820, 0x2cea1}, /* 5762x CJK Unified Ideographs Extension E */
{0x2ceb0, 0x2ebe0}, /* 7473x CJK Unified Ideographs Extension F */
{0x2f800, 0x2fa1d}, /* 542x CJK Compatibility Ideographs Supplement */
};
l = 0;
r = n = sizeof(kAstralGlyphs) / sizeof(kAstralGlyphs[0]);
while (l < r) {
m = (l + r) >> 1;
if (kAstralGlyphs[m][1] < c) {
l = m + 1;
} else {
r = m;
}
}
return !(l < n && kAstralGlyphs[l][0] <= c && c <= kAstralGlyphs[l][1]);
}
}
unsigned bestlineLowercase(unsigned c) {
int m, l, r, n;
if (c < 0200) {
if ('A' <= c && c <= 'Z') {
return c + 32;
} else {
return c;
}
} else if (c <= 0xffff) {
if ((0x0100 <= c && c <= 0x0176) || /* 60x Ā..ā → ā..ŵ Watin-A */
(0x01de <= c && c <= 0x01ee) || /* 9x Ǟ..Ǯ → ǟ..ǯ Watin-B */
(0x01f8 <= c && c <= 0x021e) || /* 20x Ǹ..Ȟ → ǹ..ȟ Watin-B */
(0x0222 <= c && c <= 0x0232) || /* 9x Ȣ..Ȳ → ȣ..ȳ Watin-B */
(0x1e00 <= c && c <= 0x1eff)) { /*256x Ḁ..Ỿ → ḁ..ỿ Watin-C */
if (c == 0x0130) return c - 199;
if (c == 0x1e9e) return c;
return c + (~c & 1);
} else if (0x01cf <= c && c <= 0x01db) {
return c + (c & 1); /* 7x Ǐ..Ǜ → ǐ..ǜ Watin-B */
} else if (0x13a0 <= c && c <= 0x13ef) {
return c + 38864; /* 80x ..Ꮿ → ꭰ ..ꮿ Cherokee */
} else {
static const struct {
unsigned short a;
unsigned short b;
short d;
} kLower[] = {
{0x00c0, 0x00d6, +32}, /* 23x À ..Ö → à ..ö Watin */
{0x00d8, 0x00de, +32}, /* 7x Ø ..Þ → ø ..þ Watin */
{0x0178, 0x0178, -121}, /* 1x Ÿ ..Ÿ → ÿ ..ÿ Watin-A */
{0x0179, 0x0179, +1}, /* 1x Ź ..Ź → ź ..ź Watin-A */
{0x017b, 0x017b, +1}, /* 1x Ż ..Ż → ż ..ż Watin-A */
{0x017d, 0x017d, +1}, /* 1x Ž ..Ž → ž ..ž Watin-A */
{0x0181, 0x0181, +210}, /* 1x Ɓ ..Ɓ → ɓ ..ɓ Watin-B */
{0x0182, 0x0182, +1}, /* 1x Ƃ ..Ƃ → ƃ ..ƃ Watin-B */
{0x0184, 0x0184, +1}, /* 1x Ƅ ..Ƅ → ƅ ..ƅ Watin-B */
{0x0186, 0x0186, +206}, /* 1x Ɔ ..Ɔ → ɔ ..ɔ Watin-B */
{0x0187, 0x0187, +1}, /* 1x Ƈ ..Ƈ → ƈ ..ƈ Watin-B */
{0x0189, 0x018a, +205}, /* 2x Ɖ ..Ɗ → ɖ ..ɗ Watin-B */
{0x018b, 0x018b, +1}, /* 1x Ƌ ..Ƌ → ƌ ..ƌ Watin-B */
{0x018e, 0x018e, +79}, /* 1x Ǝ ..Ǝ → ǝ ..ǝ Watin-B */
{0x018f, 0x018f, +202}, /* 1x Ə ..Ə → ə ..ə Watin-B */
{0x0190, 0x0190, +203}, /* 1x Ɛ ..Ɛ → ɛ ..ɛ Watin-B */
{0x0191, 0x0191, +1}, /* 1x Ƒ ..Ƒ → ƒ ..ƒ Watin-B */
{0x0193, 0x0193, +205}, /* 1x Ɠ ..Ɠ → ɠ ..ɠ Watin-B */
{0x0194, 0x0194, +207}, /* 1x Ɣ ..Ɣ → ɣ ..ɣ Watin-B */
{0x0196, 0x0196, +211}, /* 1x Ɩ ..Ɩ → ɩ ..ɩ Watin-B */
{0x0197, 0x0197, +209}, /* 1x Ɨ ..Ɨ → ɨ ..ɨ Watin-B */
{0x0198, 0x0198, +1}, /* 1x Ƙ ..Ƙ → ƙ ..ƙ Watin-B */
{0x019c, 0x019c, +211}, /* 1x Ɯ ..Ɯ → ɯ ..ɯ Watin-B */
{0x019d, 0x019d, +213}, /* 1x Ɲ ..Ɲ → ɲ ..ɲ Watin-B */
{0x019f, 0x019f, +214}, /* 1x Ɵ ..Ɵ → ɵ ..ɵ Watin-B */
{0x01a0, 0x01a0, +1}, /* 1x Ơ ..Ơ → ơ ..ơ Watin-B */
{0x01a2, 0x01a2, +1}, /* 1x Ƣ ..Ƣ → ƣ ..ƣ Watin-B */
{0x01a4, 0x01a4, +1}, /* 1x Ƥ ..Ƥ → ƥ ..ƥ Watin-B */
{0x01a6, 0x01a6, +218}, /* 1x Ʀ ..Ʀ → ʀ ..ʀ Watin-B */
{0x01a7, 0x01a7, +1}, /* 1x Ƨ ..Ƨ → ƨ ..ƨ Watin-B */
{0x01a9, 0x01a9, +218}, /* 1x Ʃ ..Ʃ → ʃ ..ʃ Watin-B */
{0x01ac, 0x01ac, +1}, /* 1x Ƭ ..Ƭ → ƭ ..ƭ Watin-B */
{0x01ae, 0x01ae, +218}, /* 1x Ʈ ..Ʈ → ʈ ..ʈ Watin-B */
{0x01af, 0x01af, +1}, /* 1x Ư ..Ư → ư ..ư Watin-B */
{0x01b1, 0x01b2, +217}, /* 2x Ʊ ..Ʋ → ʊ ..ʋ Watin-B */
{0x01b3, 0x01b3, +1}, /* 1x Ƴ ..Ƴ → ƴ ..ƴ Watin-B */
{0x01b5, 0x01b5, +1}, /* 1x Ƶ ..Ƶ → ƶ ..ƶ Watin-B */
{0x01b7, 0x01b7, +219}, /* 1x Ʒ ..Ʒ → ʒ ..ʒ Watin-B */
{0x01b8, 0x01b8, +1}, /* 1x Ƹ ..Ƹ → ƹ ..ƹ Watin-B */
{0x01bc, 0x01bc, +1}, /* 1x Ƽ ..Ƽƽ ..ƽ Watin-B */
{0x01c4, 0x01c4, +2}, /* 1x DŽ ..DŽ → dž ..dž Watin-B */
{0x01c5, 0x01c5, +1}, /* 1x Dž ..Dž → dž ..dž Watin-B */
{0x01c7, 0x01c7, +2}, /* 1x LJ ..LJ → lj ..lj Watin-B */
{0x01c8, 0x01c8, +1}, /* 1x Lj ..Lj → lj ..lj Watin-B */
{0x01ca, 0x01ca, +2}, /* 1x NJ ..NJ → nj ..nj Watin-B */
{0x01cb, 0x01cb, +1}, /* 1x Nj ..Nj → nj ..nj Watin-B */
{0x01cd, 0x01cd, +1}, /* 1x Ǎ ..Ǎ → ǎ ..ǎ Watin-B */
{0x01f1, 0x01f1, +2}, /* 1x DZ ..DZ → dz ..dz Watin-B */
{0x01f2, 0x01f2, +1}, /* 1x Dz ..Dz → dz ..dz Watin-B */
{0x01f4, 0x01f4, +1}, /* 1x Ǵ ..Ǵ → ǵ ..ǵ Watin-B */
{0x01f6, 0x01f6, -97}, /* 1x Ƕ ..Ƕ → ƕ ..ƕ Watin-B */
{0x01f7, 0x01f7, -56}, /* 1x Ƿ ..Ƿ → ƿ ..ƿ Watin-B */
{0x0220, 0x0220, -130}, /* 1x Ƞ ..Ƞ → ƞ ..ƞ Watin-B */
{0x023b, 0x023b, +1}, /* 1x Ȼ ..Ȼ → ȼ ..ȼ Watin-B */
{0x023d, 0x023d, -163}, /* 1x Ƚ ..Ƚ → ƚ ..ƚ Watin-B */
{0x0241, 0x0241, +1}, /* 1x Ɂ ..Ɂ → ɂ ..ɂ Watin-B */
{0x0243, 0x0243, -195}, /* 1x Ƀ ..Ƀ → ƀ ..ƀ Watin-B */
{0x0244, 0x0244, +69}, /* 1x Ʉ ..Ʉ → ʉ ..ʉ Watin-B */
{0x0245, 0x0245, +71}, /* 1x Ʌ ..Ʌ → ʌ ..ʌ Watin-B */
{0x0246, 0x0246, +1}, /* 1x Ɇ ..Ɇ → ɇ ..ɇ Watin-B */
{0x0248, 0x0248, +1}, /* 1x Ɉ ..Ɉ → ɉ ..ɉ Watin-B */
{0x024a, 0x024a, +1}, /* 1x Ɋ ..Ɋ → ɋ ..ɋ Watin-B */
{0x024c, 0x024c, +1}, /* 1x Ɍ ..Ɍ → ɍ ..ɍ Watin-B */
{0x024e, 0x024e, +1}, /* 1x Ɏ ..Ɏ → ɏ ..ɏ Watin-B */
{0x0386, 0x0386, +38}, /* 1x Ά ..Ά → ά ..ά Greek */
{0x0388, 0x038a, +37}, /* 3x Έ ..Ί → έ ..ί Greek */
{0x038c, 0x038c, +64}, /* 1x Ό ..Ό → ό ..ό Greek */
{0x038e, 0x038f, +63}, /* 2x Ύ ..Ώ → ύ ..ώ Greek */
{0x0391, 0x03a1, +32}, /* 17x Α ..Ρα ..ρ Greek */
{0x03a3, 0x03ab, +32}, /* 9x Σ ..Ϋ → σ ..ϋ Greek */
{0x03dc, 0x03dc, +1}, /* 1x Ϝ ..Ϝ → ϝ ..ϝ Greek */
{0x03f4, 0x03f4, -60}, /* 1x ϴ ..ϴ → θ ..θ Greek */
{0x0400, 0x040f, +80}, /* 16x Ѐ ..Џ → ѐ ..џ Cyrillic */
{0x0410, 0x042f, +32}, /* 32x А ..Я → а ..я Cyrillic */
{0x0460, 0x0460, +1}, /* 1x Ѡ ..Ѡ → ѡ ..ѡ Cyrillic */
{0x0462, 0x0462, +1}, /* 1x Ѣ ..Ѣ → ѣ ..ѣ Cyrillic */
{0x0464, 0x0464, +1}, /* 1x Ѥ ..Ѥ → ѥ ..ѥ Cyrillic */
{0x0472, 0x0472, +1}, /* 1x Ѳ ..Ѳ → ѳ ..ѳ Cyrillic */
{0x0490, 0x0490, +1}, /* 1x Ґ ..Ґ → ґ ..ґ Cyrillic */
{0x0498, 0x0498, +1}, /* 1x Ҙ ..Ҙ → ҙ ..ҙ Cyrillic */
{0x049a, 0x049a, +1}, /* 1x Қ ..Қ → қ ..қ Cyrillic */
{0x0531, 0x0556, +48}, /* 38x Ա ..Ֆ → ա ..ֆ Armenian */
{0x10a0, 0x10c5, +7264}, /* 38x Ⴀ ..Ⴥ → ⴀ ..ⴥ Georgian */
{0x10c7, 0x10c7, +7264}, /* 1x Ⴧ ..Ⴧ → ⴧ ..ⴧ Georgian */
{0x10cd, 0x10cd, +7264}, /* 1x Ⴭ ..Ⴭ → ⴭ ..ⴭ Georgian */
{0x13f0, 0x13f5, +8}, /* 6x Ᏸ ..Ᏽ → ᏸ ..ᏽ Cherokee */
{0x1c90, 0x1cba, -3008}, /* 43x Ა ..Ჺ → ა ..ჺ Georgian2 */
{0x1cbd, 0x1cbf, -3008}, /* 3x Ჽ ..Ჿ → ჽ .. Georgian2 */
{0x1f08, 0x1f0f, -8}, /* 8x Ἀ ..Ἇ → ἀ ..ἇ Greek2 */
{0x1f18, 0x1f1d, -8}, /* 6x Ἐ ..Ἕ → ἐ ..ἕ Greek2 */
{0x1f28, 0x1f2f, -8}, /* 8x Ἠ ..Ἧ → ἠ ..ἧ Greek2 */
{0x1f38, 0x1f3f, -8}, /* 8x Ἰ ..Ἷ → ἰ ..ἷ Greek2 */
{0x1f48, 0x1f4d, -8}, /* 6x Ὀ ..Ὅ → ὀ ..ὅ Greek2 */
{0x1f59, 0x1f59, -8}, /* 1x Ὑ ..Ὑ → ὑ ..ὑ Greek2 */
{0x1f5b, 0x1f5b, -8}, /* 1x Ὓ ..Ὓ → ὓ ..ὓ Greek2 */
{0x1f5d, 0x1f5d, -8}, /* 1x Ὕ ..Ὕ → ὕ ..ὕ Greek2 */
{0x1f5f, 0x1f5f, -8}, /* 1x Ὗ ..Ὗ → ὗ ..ὗ Greek2 */
{0x1f68, 0x1f6f, -8}, /* 8x Ὠ ..Ὧ → ὠ ..ὧ Greek2 */
{0x1f88, 0x1f8f, -8}, /* 8x ᾈ ..ᾏ → ᾀ ..ᾇ Greek2 */
{0x1f98, 0x1f9f, -8}, /* 8x ᾘ ..ᾟ → ᾐ ..ᾗ Greek2 */
{0x1fa8, 0x1faf, -8}, /* 8x ᾨ ..ᾯ → ᾠ ..ᾧ Greek2 */
{0x1fb8, 0x1fb9, -8}, /* 2x Ᾰ ..Ᾱ → ᾰ ..ᾱ Greek2 */
{0x1fba, 0x1fbb, -74}, /* 2x Ὰ ..Ά → ὰ ..ά Greek2 */
{0x1fbc, 0x1fbc, -9}, /* 1x ᾼ ..ᾼ → ᾳ ..ᾳ Greek2 */
{0x1fc8, 0x1fcb, -86}, /* 4x Ὲ ..Ή → ὲ ..ή Greek2 */
{0x1fcc, 0x1fcc, -9}, /* 1x ῌ ..ῌ → ῃ ..ῃ Greek2 */
{0x1fd8, 0x1fd9, -8}, /* 2x Ῐ ..Ῑ → ῐ ..ῑ Greek2 */
{0x1fda, 0x1fdb, -100}, /* 2x Ὶ ..Ί → ὶ ..ί Greek2 */
{0x1fe8, 0x1fe9, -8}, /* 2x Ῠ ..Ῡ → ῠ ..ῡ Greek2 */
{0x1fea, 0x1feb, -112}, /* 2x Ὺ ..Ύ → ὺ ..ύ Greek2 */
{0x1fec, 0x1fec, -7}, /* 1x Ῥ ..Ῥ → ῥ ..ῥ Greek2 */
{0x1ff8, 0x1ff9, -128}, /* 2x Ὸ ..Ό → ὸ ..ό Greek2 */
{0x1ffa, 0x1ffb, -126}, /* 2x Ὼ ..Ώ → ὼ ..ώ Greek2 */
{0x1ffc, 0x1ffc, -9}, /* 1x ῼ ..ῼ → ῳ ..ῳ Greek2 */
{0x2126, 0x2126, -7517}, /* 1x Ω ..Ω → ω ..ω Letterlike */
{0x212a, 0x212a, -8383}, /* 1x .. → k ..k Letterlike */
{0x212b, 0x212b, -8262}, /* 1x Å ..Å → å ..å Letterlike */
{0x2132, 0x2132, +28}, /* 1x Ⅎ ..Ⅎ → ⅎ ..ⅎ Letterlike */
{0x2160, 0x216f, +16}, /* 16x .. ..ⅿ Numbery */
{0x2183, 0x2183, +1}, /* 1x Ↄ ..Ↄ → ↄ ..ↄ Numbery */
{0x24b6, 0x24cf, +26}, /* 26x Ⓐ ..Ⓩ → ⓐ ..ⓩ Enclosed */
{0x2c00, 0x2c2e, +48}, /* 47x Ⰰ ..Ⱞ → ⰰ ..ⱞ Glagolitic */
{0xff21, 0xff3a, +32}, /* 26x .... Dubs */
};
l = 0;
r = n = sizeof(kLower) / sizeof(kLower[0]);
while (l < r) {
m = (l + r) >> 1;
if (kLower[m].b < c) {
l = m + 1;
} else {
r = m;
}
}
if (l < n && kLower[l].a <= c && c <= kLower[l].b) {
return c + kLower[l].d;
} else {
return c;
}
}
} else {
static struct {
unsigned a;
unsigned b;
short d;
} kAstralLower[] = {
{0x10400, 0x10427, +40}, /* 40x 𐐀 ..𐐧 → 𐐨 ..𐑏 Deseret */
{0x104b0, 0x104d3, +40}, /* 36x 𐒰 ..𐓓 → 𐓘 ..𐓻 Osage */
{0x1d400, 0x1d419, +26}, /* 26x 𝐀 ..𝐙𝐚 ..𝐳 Math */
{0x1d43c, 0x1d44d, +26}, /* 18x 𝐼 ..𝑍𝑖 ..𝑧 Math */
{0x1d468, 0x1d481, +26}, /* 26x 𝑨 ..𝒁𝒂 ..𝒛 Math */
{0x1d4ae, 0x1d4b5, +26}, /* 8x 𝒮 ..𝒵𝓈 ..𝓏 Math */
{0x1d4d0, 0x1d4e9, +26}, /* 26x 𝓐 ..𝓩𝓪 ..𝔃 Math */
{0x1d50d, 0x1d514, +26}, /* 8x 𝔍 ..𝔔𝔧 ..𝔮 Math */
{0x1d56c, 0x1d585, +26}, /* 26x 𝕬 ..𝖅𝖆 ..𝖟 Math */
{0x1d5a0, 0x1d5b9, +26}, /* 26x 𝖠 ..𝖹𝖺 ..𝗓 Math */
{0x1d5d4, 0x1d5ed, +26}, /* 26x 𝗔 ..𝗭𝗮 ..𝘇 Math */
{0x1d608, 0x1d621, +26}, /* 26x 𝘈 ..𝘡𝘢 ..𝘻 Math */
{0x1d63c, 0x1d655, -442}, /* 26x 𝘼 ..𝙕𝒂 ..𝒛 Math */
{0x1d670, 0x1d689, +26}, /* 26x 𝙰 ..𝚉𝚊 ..𝚣 Math */
{0x1d6a8, 0x1d6b8, +26}, /* 17x 𝚨 ..𝚸𝛂 ..𝛒 Math */
{0x1d6e2, 0x1d6f2, +26}, /* 17x 𝛢 ..𝛲𝛼 ..𝜌 Math */
{0x1d71c, 0x1d72c, +26}, /* 17x 𝜜 ..𝜬𝜶 ..𝝆 Math */
{0x1d756, 0x1d766, +26}, /* 17x 𝝖 ..𝝦𝝰 ..𝞀 Math */
{0x1d790, 0x1d7a0, -90}, /* 17x 𝞐 ..𝞠𝜶 ..𝝆 Math */
};
l = 0;
r = n = sizeof(kAstralLower) / sizeof(kAstralLower[0]);
while (l < r) {
m = (l + r) >> 1;
if (kAstralLower[m].b < c) {
l = m + 1;
} else {
r = m;
}
}
if (l < n && kAstralLower[l].a <= c && c <= kAstralLower[l].b) {
return c + kAstralLower[l].d;
} else {
return c;
}
}
}
unsigned bestlineUppercase(unsigned c) {
int m, l, r, n;
if (c < 0200) {
if ('a' <= c && c <= 'z') {
return c - 32;
} else {
return c;
}
} else if (c <= 0xffff) {
if ((0x0101 <= c && c <= 0x0177) || /* 60x ā..ŵ → Ā..ā Watin-A */
(0x01df <= c && c <= 0x01ef) || /* 9x ǟ..ǯ → Ǟ..Ǯ Watin-B */
(0x01f8 <= c && c <= 0x021e) || /* 20x ǹ..ȟ → Ǹ..Ȟ Watin-B */
(0x0222 <= c && c <= 0x0232) || /* 9x ȣ..ȳ → Ȣ..Ȳ Watin-B */
(0x1e01 <= c && c <= 0x1eff)) { /*256x ḁ..ỿ → Ḁ..Ỿ Watin-C */
if (c == 0x0131) return c + 232;
if (c == 0x1e9e) return c;
return c - (c & 1);
} else if (0x01d0 <= c && c <= 0x01dc) {
return c - (~c & 1); /* 7x ǐ..ǜ → Ǐ..Ǜ Watin-B */
} else if (0xab70 <= c && c <= 0xabbf) {
return c - 38864; /* 80x ꭰ ..ꮿ → ..Ꮿ Cherokee Supplement */
} else {
static const struct {
unsigned short a;
unsigned short b;
short d;
} kUpper[] = {
{0x00b5, 0x00b5, +743}, /* 1x µ ..µ → Μ ..Μ Watin */
{0x00e0, 0x00f6, -32}, /* 23x à ..ö → À ..Ö Watin */
{0x00f8, 0x00fe, -32}, /* 7x ø ..þ → Ø ..Þ Watin */
{0x00ff, 0x00ff, +121}, /* 1x ÿ ..ÿ → Ÿ ..Ÿ Watin */
{0x017a, 0x017a, -1}, /* 1x ź ..ź → Ź ..Ź Watin-A */
{0x017c, 0x017c, -1}, /* 1x ż ..ż → Ż ..Ż Watin-A */
{0x017e, 0x017e, -1}, /* 1x ž ..ž → Ž ..Ž Watin-A */
{0x017f, 0x017f, -300}, /* 1x ſ ..ſ → S ..S Watin-A */
{0x0180, 0x0180, +195}, /* 1x ƀ ..ƀ → Ƀ ..Ƀ Watin-B */
{0x0183, 0x0183, -1}, /* 1x ƃ ..ƃ → Ƃ ..Ƃ Watin-B */
{0x0185, 0x0185, -1}, /* 1x ƅ ..ƅ → Ƅ ..Ƅ Watin-B */
{0x0188, 0x0188, -1}, /* 1x ƈ ..ƈ → Ƈ ..Ƈ Watin-B */
{0x018c, 0x018c, -1}, /* 1x ƌ ..ƌ → Ƌ ..Ƌ Watin-B */
{0x0192, 0x0192, -1}, /* 1x ƒ ..ƒ → Ƒ ..Ƒ Watin-B */
{0x0195, 0x0195, +97}, /* 1x ƕ ..ƕ → Ƕ ..Ƕ Watin-B */
{0x0199, 0x0199, -1}, /* 1x ƙ ..ƙ → Ƙ ..Ƙ Watin-B */
{0x019a, 0x019a, +163}, /* 1x ƚ ..ƚ → Ƚ ..Ƚ Watin-B */
{0x019e, 0x019e, +130}, /* 1x ƞ ..ƞ → Ƞ ..Ƞ Watin-B */
{0x01a1, 0x01a1, -1}, /* 1x ơ ..ơ → Ơ ..Ơ Watin-B */
{0x01a3, 0x01a3, -1}, /* 1x ƣ ..ƣ → Ƣ ..Ƣ Watin-B */
{0x01a5, 0x01a5, -1}, /* 1x ƥ ..ƥ → Ƥ ..Ƥ Watin-B */
{0x01a8, 0x01a8, -1}, /* 1x ƨ ..ƨ → Ƨ ..Ƨ Watin-B */
{0x01ad, 0x01ad, -1}, /* 1x ƭ ..ƭ → Ƭ ..Ƭ Watin-B */
{0x01b0, 0x01b0, -1}, /* 1x ư ..ư → Ư ..Ư Watin-B */
{0x01b4, 0x01b4, -1}, /* 1x ƴ ..ƴ → Ƴ ..Ƴ Watin-B */
{0x01b6, 0x01b6, -1}, /* 1x ƶ ..ƶ → Ƶ ..Ƶ Watin-B */
{0x01b9, 0x01b9, -1}, /* 1x ƹ ..ƹ → Ƹ ..Ƹ Watin-B */
{0x01bd, 0x01bd, -1}, /* 1x ƽ ..ƽƼ ..Ƽ Watin-B */
{0x01bf, 0x01bf, +56}, /* 1x ƿ ..ƿ → Ƿ ..Ƿ Watin-B */
{0x01c5, 0x01c5, -1}, /* 1x Dž ..Dž → DŽ ..DŽ Watin-B */
{0x01c6, 0x01c6, -2}, /* 1x dž ..dž → DŽ ..DŽ Watin-B */
{0x01c8, 0x01c8, -1}, /* 1x Lj ..Lj → LJ ..LJ Watin-B */
{0x01c9, 0x01c9, -2}, /* 1x lj ..lj → LJ ..LJ Watin-B */
{0x01cb, 0x01cb, -1}, /* 1x Nj ..Nj → NJ ..NJ Watin-B */
{0x01cc, 0x01cc, -2}, /* 1x nj ..nj → NJ ..NJ Watin-B */
{0x01ce, 0x01ce, -1}, /* 1x ǎ ..ǎ → Ǎ ..Ǎ Watin-B */
{0x01dd, 0x01dd, -79}, /* 1x ǝ ..ǝ → Ǝ ..Ǝ Watin-B */
{0x01f2, 0x01f2, -1}, /* 1x Dz ..Dz → DZ ..DZ Watin-B */
{0x01f3, 0x01f3, -2}, /* 1x dz ..dz → DZ ..DZ Watin-B */
{0x01f5, 0x01f5, -1}, /* 1x ǵ ..ǵ → Ǵ ..Ǵ Watin-B */
{0x023c, 0x023c, -1}, /* 1x ȼ ..ȼ → Ȼ ..Ȼ Watin-B */
{0x023f, 0x0240,+10815}, /* 2x ȿ ..ɀ → Ȿ ..Ɀ Watin-B */
{0x0242, 0x0242, -1}, /* 1x ɂ ..ɂ → Ɂ ..Ɂ Watin-B */
{0x0247, 0x0247, -1}, /* 1x ɇ ..ɇ → Ɇ ..Ɇ Watin-B */
{0x0249, 0x0249, -1}, /* 1x ɉ ..ɉ → Ɉ ..Ɉ Watin-B */
{0x024b, 0x024b, -1}, /* 1x ɋ ..ɋ → Ɋ ..Ɋ Watin-B */
{0x024d, 0x024d, -1}, /* 1x ɍ ..ɍ → Ɍ ..Ɍ Watin-B */
{0x024f, 0x024f, -1}, /* 1x ɏ ..ɏ → Ɏ ..Ɏ Watin-B */
{0x037b, 0x037d, +130}, /* 3x ͻ ..ͽ → Ͻ ..Ͽ Greek */
{0x03ac, 0x03ac, -38}, /* 1x ά ..ά → Ά ..Ά Greek */
{0x03ad, 0x03af, -37}, /* 3x έ ..ί → Έ ..Ί Greek */
{0x03b1, 0x03c1, -32}, /* 17x α ..ρΑ ..Ρ Greek */
{0x03c2, 0x03c2, -31}, /* 1x ς ..ς → Σ ..Σ Greek */
{0x03c3, 0x03cb, -32}, /* 9x σ ..ϋ → Σ ..Ϋ Greek */
{0x03cc, 0x03cc, -64}, /* 1x ό ..ό → Ό ..Ό Greek */
{0x03cd, 0x03ce, -63}, /* 2x ύ ..ώ → Ύ ..Ώ Greek */
{0x03d0, 0x03d0, -62}, /* 1x ϐ ..ϐ → Β ..Β Greek */
{0x03d1, 0x03d1, -57}, /* 1x ϑ ..ϑ → Θ ..Θ Greek */
{0x03d5, 0x03d5, -47}, /* 1x ϕ ..ϕ → Φ ..Φ Greek */
{0x03d6, 0x03d6, -54}, /* 1x ϖ ..ϖ → Π ..Π Greek */
{0x03dd, 0x03dd, -1}, /* 1x ϝ ..ϝ → Ϝ ..Ϝ Greek */
{0x03f0, 0x03f0, -86}, /* 1x ϰ ..ϰ → Κ ..Κ Greek */
{0x03f1, 0x03f1, -80}, /* 1x ϱ ..ϱΡ ..Ρ Greek */
{0x03f5, 0x03f5, -96}, /* 1x ϵ ..ϵ → Ε ..Ε Greek */
{0x0430, 0x044f, -32}, /* 32x а ..я → А ..Я Cyrillic */
{0x0450, 0x045f, -80}, /* 16x ѐ ..џ → Ѐ ..Џ Cyrillic */
{0x0461, 0x0461, -1}, /* 1x ѡ ..ѡ → Ѡ ..Ѡ Cyrillic */
{0x0463, 0x0463, -1}, /* 1x ѣ ..ѣ → Ѣ ..Ѣ Cyrillic */
{0x0465, 0x0465, -1}, /* 1x ѥ ..ѥ → Ѥ ..Ѥ Cyrillic */
{0x0473, 0x0473, -1}, /* 1x ѳ ..ѳ → Ѳ ..Ѳ Cyrillic */
{0x0491, 0x0491, -1}, /* 1x ґ ..ґ → Ґ ..Ґ Cyrillic */
{0x0499, 0x0499, -1}, /* 1x ҙ ..ҙ → Ҙ ..Ҙ Cyrillic */
{0x049b, 0x049b, -1}, /* 1x қ ..қ → Қ ..Қ Cyrillic */
{0x0561, 0x0586, -48}, /* 38x ա ..ֆ → Ա ..Ֆ Armenian */
{0x10d0, 0x10fa, +3008}, /* 43x ა ..ჺ → Ა ..Ჺ Georgian */
{0x10fd, 0x10ff, +3008}, /* 3x ჽ .. → Ჽ ..Ჿ Georgian */
{0x13f8, 0x13fd, -8}, /* 6x ᏸ ..ᏽ → Ᏸ ..Ᏽ Cherokee */
{0x214e, 0x214e, -28}, /* 1x ⅎ ..ⅎ → Ⅎ ..Ⅎ Letterlike */
{0x2170, 0x217f, -16}, /* 16x ..ⅿ → .. Numbery */
{0x2184, 0x2184, -1}, /* 1x ↄ ..ↄ → Ↄ ..Ↄ Numbery */
{0x24d0, 0x24e9, -26}, /* 26x ⓐ ..ⓩ → Ⓐ ..Ⓩ Enclosed */
{0x2c30, 0x2c5e, -48}, /* 47x ⰰ ..ⱞ → Ⰰ ..Ⱞ Glagolitic */
{0x2d00, 0x2d25, -7264}, /* 38x ⴀ ..ⴥ → Ⴀ ..Ⴥ Georgian2 */
{0x2d27, 0x2d27, -7264}, /* 1x ⴧ ..ⴧ → Ⴧ ..Ⴧ Georgian2 */
{0x2d2d, 0x2d2d, -7264}, /* 1x ⴭ ..ⴭ → Ⴭ ..Ⴭ Georgian2 */
{0xff41, 0xff5a, -32}, /* 26x .... Dubs */
};
l = 0;
r = n = sizeof(kUpper) / sizeof(kUpper[0]);
while (l < r) {
m = (l + r) >> 1;
if (kUpper[m].b < c) {
l = m + 1;
} else {
r = m;
}
}
if (l < n && kUpper[l].a <= c && c <= kUpper[l].b) {
return c + kUpper[l].d;
} else {
return c;
}
}
} else {
static const struct {
unsigned a;
unsigned b;
short d;
} kAstralUpper[] = {
{0x10428, 0x1044f, -40}, /* 40x 𐐨..𐑏 → 𐐀..𐐧 Deseret */
{0x104d8, 0x104fb, -40}, /* 36x 𐓘..𐓻 → 𐒰..𐓓 Osage */
{0x1d41a, 0x1d433, -26}, /* 26x 𝐚..𝐳𝐀..𝐙 Math */
{0x1d456, 0x1d467, -26}, /* 18x 𝑖..𝑧𝐼..𝑍 Math */
{0x1d482, 0x1d49b, -26}, /* 26x 𝒂..𝒛𝑨..𝒁 Math */
{0x1d4c8, 0x1d4cf, -26}, /* 8x 𝓈..𝓏𝒮..𝒵 Math */
{0x1d4ea, 0x1d503, -26}, /* 26x 𝓪..𝔃𝓐..𝓩 Math */
{0x1d527, 0x1d52e, -26}, /* 8x 𝔧..𝔮𝔍..𝔔 Math */
{0x1d586, 0x1d59f, -26}, /* 26x 𝖆..𝖟𝕬..𝖅 Math */
{0x1d5ba, 0x1d5d3, -26}, /* 26x 𝖺..𝗓𝖠..𝖹 Math */
{0x1d5ee, 0x1d607, -26}, /* 26x 𝗮..𝘇𝗔..𝗭 Math */
{0x1d622, 0x1d63b, -26}, /* 26x 𝘢..𝘻𝘈..𝘡 Math */
{0x1d68a, 0x1d6a3, +442}, /* 26x 𝒂..𝒛𝘼..𝙕 Math */
{0x1d6c2, 0x1d6d2, -26}, /* 26x 𝚊..𝚣𝙰..𝚉 Math */
{0x1d6fc, 0x1d70c, -26}, /* 17x 𝛂..𝛒𝚨..𝚸 Math */
{0x1d736, 0x1d746, -26}, /* 17x 𝛼..𝜌𝛢..𝛲 Math */
{0x1d770, 0x1d780, -26}, /* 17x 𝜶..𝝆𝜜..𝜬 Math */
{0x1d770, 0x1d756, -26}, /* 17x 𝝰..𝞀𝝖..𝝦 Math */
{0x1d736, 0x1d790, -90}, /* 17x 𝜶..𝝆𝞐..𝞠 Math */
};
l = 0;
r = n = sizeof(kAstralUpper) / sizeof(kAstralUpper[0]);
while (l < r) {
m = (l + r) >> 1;
if (kAstralUpper[m].b < c) {
l = m + 1;
} else {
r = m;
}
}
if (l < n && kAstralUpper[l].a <= c && c <= kAstralUpper[l].b) {
return c + kAstralUpper[l].d;
} else {
return c;
}
}
}
char bestlineNotSeparator(unsigned c) {
return !bestlineIsSeparator(c);
}
static unsigned GetMirror(const unsigned short A[][2], size_t n, unsigned c) {
int l, m, r;
l = 0;
r = n - 1;
while (l <= r) {
m = (l + r) >> 1;
if (A[m][0] < c) {
l = m + 1;
} else if (A[m][0] > c) {
r = m - 1;
} else {
return A[m][1];
}
}
return 0;
}
unsigned bestlineMirrorLeft(unsigned c) {
static const unsigned short kMirrorRight[][2] = {
{L')', L'('}, {L']', L'['}, {L'}', L'{'}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''},
};
return GetMirror(kMirrorRight,
sizeof(kMirrorRight) / sizeof(kMirrorRight[0]),
c);
}
unsigned bestlineMirrorRight(unsigned c) {
static const unsigned short kMirrorLeft[][2] = {
{L'(', L')'}, {L'[', L']'}, {L'{', L'}'}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''}, {L'', L''}, {L'', L''},
{L'', L''}, {L'', L''},
};
return GetMirror(kMirrorLeft,
sizeof(kMirrorLeft) / sizeof(kMirrorLeft[0]),
c);
}
char bestlineIsXeparator(unsigned c) {
return (bestlineIsSeparator(c) &&
!bestlineMirrorLeft(c) &&
!bestlineMirrorRight(c));
}
static unsigned Capitalize(unsigned c) {
if (!iscapital) {
c = bestlineUppercase(c);
iscapital = 1;
}
return c;
}
static inline int Bsr(unsigned long long x) {
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
int b;
b = __builtin_clzll(x);
b ^= sizeof(unsigned long long) * CHAR_BIT - 1;
return b;
#else
static const char kDebruijn[64] = {
0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61,
54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62,
46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63,
};
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x |= x >> 32;
return kDebruijn[(x * 0x03f79d71b4cb0a89) >> 58];
#endif
}
static struct rune DecodeUtf8(int c) {
struct rune r;
if (c < 252) {
r.n = Bsr(255 & ~c);
r.c = c & (((1 << r.n) - 1) | 3);
r.n = 6 - r.n;
} else {
r.c = c & 3;
r.n = 5;
}
return r;
}
static unsigned long long EncodeUtf8(unsigned c) {
static const unsigned short kTpEnc[32 - 7] = {
1|0300<<8, 1|0300<<8, 1|0300<<8, 1|0300<<8, 2|0340<<8,
2|0340<<8, 2|0340<<8, 2|0340<<8, 2|0340<<8, 3|0360<<8,
3|0360<<8, 3|0360<<8, 3|0360<<8, 3|0360<<8, 4|0370<<8,
4|0370<<8, 4|0370<<8, 4|0370<<8, 4|0370<<8, 5|0374<<8,
5|0374<<8, 5|0374<<8, 5|0374<<8, 5|0374<<8, 5|0374<<8,
};
int e, n;
unsigned long long w;
if (c < 0200) return c;
e = kTpEnc[Bsr(c) - 7];
n = e & 0xff;
w = 0;
do {
w |= 0200 | (c & 077);
w <<= 8;
c >>= 6;
} while (--n);
return c | w | e >> 8;
}
static struct rune GetUtf8(const char *p, size_t n) {
struct rune r;
if ((r.n = r.c = 0) < n && (r.c = p[r.n++] & 255) >= 0300) {
r.c = DecodeUtf8(r.c).c;
while (r.n < n && (p[r.n] & 0300) == 0200) {
r.c = r.c << 6 | (p[r.n++] & 077);
}
}
return r;
}
static char *FormatUnsigned(char *p, unsigned x) {
char t;
size_t i, a, b;
i = 0;
do {
p[i++] = x % 10 + '0';
x = x / 10;
} while (x > 0);
p[i] = '\0';
if (i) {
for (a = 0, b = i - 1; a < b; ++a, --b) {
t = p[a];
p[a] = p[b];
p[b] = t;
}
}
return p + i;
}
static void abInit(struct abuf *a) {
a->len = 0;
a->cap = 16;
a->b = (char *)malloc(a->cap);
a->b[0] = 0;
}
static char abGrow(struct abuf *a, int need) {
int cap;
char *b;
cap = a->cap;
do cap += cap / 2;
while (cap < need);
if (!(b = (char *)realloc(a->b, cap * sizeof(*a->b)))) return 0;
a->cap = cap;
a->b = b;
return 1;
}
static void abAppendw(struct abuf *a, unsigned long long w) {
char *p;
if (a->len + 8 > a->cap && !abGrow(a, a->len + 8)) return;
p = a->b + a->len;
p[0] = (0x00000000000000FF & w) >> 000;
p[1] = (0x000000000000FF00 & w) >> 010;
p[2] = (0x0000000000FF0000 & w) >> 020;
p[3] = (0x00000000FF000000 & w) >> 030;
p[4] = (0x000000FF00000000 & w) >> 040;
p[5] = (0x0000FF0000000000 & w) >> 050;
p[6] = (0x00FF000000000000 & w) >> 060;
p[7] = (0xFF00000000000000 & w) >> 070;
a->len += w ? (Bsr(w) >> 3) + 1 : 1;
}
static void abAppend(struct abuf *a, const char *s, int len) {
if (a->len + len + 1 > a->cap && !abGrow(a, a->len + len + 1)) return;
memcpy(a->b + a->len, s, len);
a->b[a->len + len] = 0;
a->len += len;
}
static void abAppends(struct abuf *a, const char *s) {
abAppend(a, s, strlen(s));
}
static void abAppendu(struct abuf *a, unsigned u) {
char b[11];
abAppend(a, b, FormatUnsigned(b, u) - b);
}
static void abFree(struct abuf *a) {
free(a->b);
a->b = 0;
}
static size_t GetFdSize(int fd) {
struct stat st;
st.st_size = 0;
fstat(fd, &st);
return st.st_size;
}
static char IsCharDev(int fd) {
struct stat st;
st.st_mode = 0;
fstat(fd, &st);
return (st.st_mode & S_IFMT) == S_IFCHR;
}
static int WaitUntilReady(int fd, int events) {
struct pollfd p[1];
p[0].fd = fd;
p[0].events = events;
return poll(p, 1, -1);
}
static char HasPendingInput(int fd) {
struct pollfd p[1];
p[0].fd = fd;
p[0].events = POLLIN;
return poll(p, 1, 0) == 1;
}
static char *GetLineBlock(FILE *f) {
ssize_t rc;
char *p = 0;
size_t n, c = 0;
if ((rc = getdelim(&p, &c, '\n', f)) != EOF) {
for (n = rc; n; --n) {
if (p[n - 1] == '\r' || p[n - 1] == '\n') {
p[n - 1] = 0;
} else {
break;
}
}
return p;
} else {
free(p);
return 0;
}
}
long bestlineReadCharacter(int fd, char *p, unsigned long n) {
int e;
size_t i;
ssize_t rc;
struct rune r;
unsigned char c;
enum { kAscii, kUtf8, kEsc, kCsi1, kCsi2, kSs, kNf, kStr, kStr2, kDone } t;
i = 0;
r.c = 0;
r.n = 0;
e = errno;
t = kAscii;
if (n) p[0] = 0;
do {
for (;;) {
if (gotint) {
errno = EINTR;
return -1;
}
if (n) {
rc = read(fd,&c,1);
} else {
rc = read(fd,0,0);
}
if (rc == -1 && errno == EINTR) {
if (!i) {
return -1;
}
} else if (rc == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
if (WaitUntilReady(fd, POLLIN) == -1) {
if (rc == -1 && errno == EINTR) {
if (!i) {
return -1;
}
} else {
return -1;
}
}
} else if (rc == -1) {
return -1;
} else if (!rc) {
if (!i) {
errno = e;
return 0;
} else {
errno = EILSEQ;
return -1;
}
} else {
break;
}
}
if (i + 1 < n) {
p[i] = c;
p[i+1] = 0;
} else if (i < n) {
p[i] = 0;
}
++i;
switch (t) {
Whoopsie:
if (n) p[0] = c;
t = kAscii;
i = 1;
/* fallthrough */
case kAscii:
if (c < 0200) {
if (c == 033) {
t = kEsc;
} else {
t = kDone;
}
} else if (c >= 0300) {
t = kUtf8;
r = DecodeUtf8(c);
} else {
/* ignore overlong sequences */
}
break;
case kUtf8:
if ((c & 0300) == 0200) {
r.c <<= 6;
r.c |= c & 077;
if (!--r.n) {
switch (r.c) {
case 033:
t = kEsc; /* parsed but not canonicalized */
break;
case 0x9b:
t = kCsi1; /* unusual but legal */
break;
case 0x8e: /* SS2 (Single Shift Two) */
case 0x8f: /* SS3 (Single Shift Three) */
t = kSs;
break;
case 0x90: /* DCS (Device Control String) */
case 0x98: /* SOS (Start of String) */
case 0x9d: /* OSC (Operating System Command) */
case 0x9e: /* PM (Privacy Message) */
case 0x9f: /* APC (Application Program Command) */
t = kStr;
break;
default:
t = kDone;
break;
}
}
} else {
goto Whoopsie; /* ignore underlong sequences if not eof */
}
break;
case kEsc:
if (0x20 <= c && c <= 0x2f) { /* Nf */
/*
* Almost no one uses ANSI Nf sequences
* They overlaps with alt+graphic keystrokes
* We care more about being able to type alt-/
*/
if (c == ' ' || c == '#') {
t = kNf;
} else {
t = kDone;
}
} else if (0x30 <= c && c <= 0x3f) { /* Fp */
t = kDone;
} else if (0x20 <= c && c <= 0x5F) { /* Fe */
switch (c) {
case '[':
t = kCsi1;
break;
case 'N': /* SS2 (Single Shift Two) */
case 'O': /* SS3 (Single Shift Three) */
t = kSs;
break;
case 'P': /* DCS (Device Control String) */
case 'X': /* SOS (Start of String) */
case ']': /* OSC (Operating System Command) */
case '^': /* PM (Privacy Message) */
case '_': /* APC (Application Program Command) */
t = kStr;
break;
default:
t = kDone;
break;
}
} else if (0x60 <= c && c <= 0x7e) { /* Fs */
t = kDone;
} else if (c == 033) {
if (i < 3) {
/* alt chording */
} else {
t = kDone; /* esc mashing */
i = 1;
}
} else {
t = kDone;
}
break;
case kSs:
t = kDone;
break;
case kNf:
if (0x30 <= c && c <= 0x7e) {
t = kDone;
} else if (!(0x20 <= c && c <= 0x2f)) {
goto Whoopsie;
}
break;
case kCsi1:
if (0x20 <= c && c <= 0x2f) {
t = kCsi2;
} else if (c == '[' && ((i == 3) ||
(i == 4 && p[1] == 033))) {
/* linux function keys */
} else if (0x40 <= c && c <= 0x7e) {
t = kDone;
} else if (!(0x30 <= c && c <= 0x3f)) {
goto Whoopsie;
}
break;
case kCsi2:
if (0x40 <= c && c <= 0x7e) {
t = kDone;
} else if (!(0x20 <= c && c <= 0x2f)) {
goto Whoopsie;
}
break;
case kStr:
switch (c) {
case '\a':
t = kDone;
break;
case 0033: /* ESC */
case 0302: /* C1 (UTF-8) */
t = kStr2;
break;
default:
break;
}
break;
case kStr2:
switch (c) {
case '\a':
case '\\': /* ST (ASCII) */
case 0234: /* ST (UTF-8) */
t = kDone;
break;
default:
t = kStr;
break;
}
break;
default:
assert(0);
}
} while (t != kDone);
errno = e;
return i;
}
static char *GetLineChar(int fin, int fout) {
size_t got;
ssize_t rc;
char seq[16];
struct abuf a;
struct sigaction sa[3];
abInit(&a);
gotint = 0;
sigemptyset(&sa->sa_mask);
sa->sa_flags = 0;
sa->sa_handler = bestlineOnInt;
sigaction(SIGINT,sa,sa+1);
sigaction(SIGQUIT,sa,sa+2);
for (;;) {
if (gotint) {
rc = -1;
break;
}
if ((rc = bestlineReadCharacter(fin, seq, sizeof(seq))) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (WaitUntilReady(fin, POLLIN) > 0) {
continue;
}
}
if (errno == EINTR) {
continue;
} else {
break;
}
}
if (!(got = rc)) {
if (a.len) {
break;
} else {
rc = -1;
break;
}
}
if (seq[0] == '\r') {
if (HasPendingInput(fin)) {
if ((rc = bestlineReadCharacter(fin, seq + 1, sizeof(seq) - 1)) > 0) {
if (seq[0] == '\n') {
break;
}
} else {
rc = -1;
break;
}
} else {
2023-01-15 01:39:42 +01:00
(void)write(fout, "\n", 1);
2022-06-11 19:57:56 +02:00
break;
}
} else if (seq[0] == Ctrl('D')) {
break;
} else if (seq[0] == '\n') {
break;
} else if (seq[0] == '\b') {
while (a.len && (a.b[a.len - 1] & 0300) == 0200) --a.len;
if (a.len) --a.len;
}
if (!IsControl(seq[0])) {
abAppend(&a, seq, got);
}
}
sigaction(SIGQUIT,sa+2,0);
sigaction(SIGINT,sa+1,0);
if (gotint) {
abFree(&a);
raise(gotint);
errno = EINTR;
rc = -1;
}
if (rc != -1) {
return a.b;
} else {
abFree(&a);
return 0;
}
}
static char *GetLine(FILE *in, FILE *out) {
if (!IsCharDev(fileno(in))) {
return GetLineBlock(in);
} else {
return GetLineChar(fileno(in), fileno(out));
}
}
static char *Copy(char *d, const char *s, size_t n) {
memcpy(d, s, n);
return d + n;
}
static int CompareStrings(const char *a, const char *b) {
size_t i;
int x, y, c;
for (i = 0;; ++i) {
x = bestlineLowercase(a[i] & 255);
y = bestlineLowercase(b[i] & 255);
if ((c = x - y) || !x) {
return c;
}
}
}
static const char *FindSubstringReverse(const char *p, size_t n,
const char *q, size_t m) {
size_t i;
if (m <= n) {
n -= m;
do {
for (i = 0; i < m; ++i) {
if (p[n + i] != q[i]) {
break;
}
}
if (i == m) {
return p + n;
}
} while (n--);
}
return 0;
}
static int ParseUnsigned(const char *s, void *e) {
int c, x;
for (x = 0; (c = *s++);) {
if ('0' <= c && c <= '9') {
x = Min(c - '0' + x * 10, 32767);
} else {
break;
}
}
if (e) *(const char **)e = s;
return x;
}
/**
* Returns UNICODE CJK Monospace Width of string.
*
* Control codes and ANSI sequences have a width of zero. We only parse
* a limited subset of ANSI here since we don't store ANSI codes in the
* linenoiseState::buf, but we do encourage CSI color codes in prompts.
*/
static size_t GetMonospaceWidth(const char *p, size_t n, char *out_haswides) {
int c, d;
size_t i, w;
struct rune r;
char haswides;
enum { kAscii, kUtf8, kEsc, kCsi1, kCsi2 } t;
for (haswides = r.c = r.n = w = i = 0, t = kAscii; i < n; ++i) {
c = p[i] & 255;
switch (t) {
Whoopsie:
t = kAscii;
/* fallthrough */
case kAscii:
if (c < 0200) {
if (c == 033) {
t = kEsc;
} else {
++w;
}
} else if (c >= 0300) {
t = kUtf8;
r = DecodeUtf8(c);
}
break;
case kUtf8:
if ((c & 0300) == 0200) {
r.c <<= 6;
r.c |= c & 077;
if (!--r.n) {
d = GetMonospaceCharacterWidth(r.c);
d = Max(0, d);
w += d;
haswides |= d > 1;
t = kAscii;
break;
}
} else {
goto Whoopsie;
}
break;
case kEsc:
if (c == '[') {
t = kCsi1;
} else {
t = kAscii;
}
break;
case kCsi1:
if (0x20 <= c && c <= 0x2f) {
t = kCsi2;
} else if (0x40 <= c && c <= 0x7e) {
t = kAscii;
} else if (!(0x30 <= c && c <= 0x3f)) {
goto Whoopsie;
}
break;
case kCsi2:
if (0x40 <= c && c <= 0x7e) {
t = kAscii;
} else if (!(0x20 <= c && c <= 0x2f)) {
goto Whoopsie;
}
break;
default:
assert(0);
}
}
if (out_haswides) {
*out_haswides = haswides;
}
return w;
}
static int bestlineIsUnsupportedTerm(void) {
size_t i;
char *term;
static char once, res;
if (!once) {
if ((term = getenv("TERM"))) {
for (i = 0; i < sizeof(kUnsupported) / sizeof(*kUnsupported); i++) {
if (!CompareStrings(term,kUnsupported[i])) {
res = 1;
break;
}
}
}
once = 1;
}
return res;
}
static int enableRawMode(int fd) {
struct termios raw;
struct sigaction sa;
if (tcgetattr(fd,&orig_termios) != -1) {
raw = orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_oflag &= ~OPOST;
raw.c_iflag |= IUTF8;
raw.c_cflag |= CS8;
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0;
if (tcsetattr(fd,TCSANOW,&raw) != -1) {
sa.sa_flags = 0;
sa.sa_handler = bestlineOnCont;
sigemptyset(&sa.sa_mask);
sigaction(SIGCONT,&sa,&orig_cont);
sa.sa_handler = bestlineOnWinch;
sigaction(SIGWINCH,&sa,&orig_winch);
rawmode = fd;
gotwinch = 0;
gotcont = 0;
return 0;
}
}
errno = ENOTTY;
return -1;
}
static void bestlineUnpause(int fd) {
if (ispaused) {
tcflow(fd, TCOON);
ispaused = 0;
}
}
void bestlineDisableRawMode(void) {
if (rawmode != -1) {
bestlineUnpause(rawmode);
sigaction(SIGCONT,&orig_cont,0);
sigaction(SIGWINCH,&orig_winch,0);
tcsetattr(rawmode,TCSANOW,&orig_termios);
rawmode = -1;
}
}
static int bestlineWrite(int fd, const void *p, size_t n) {
ssize_t rc;
size_t wrote;
do {
for (;;) {
if (gotint) {
errno = EINTR;
return -1;
}
if (ispaused) {
return 0;
}
rc = write(fd, p, n);
if (rc == -1 && errno == EINTR) {
continue;
} else if (rc == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
if (WaitUntilReady(fd, POLLOUT) == -1) {
if (errno == EINTR) {
continue;
} else {
return -1;
}
}
} else {
break;
}
}
if (rc != -1) {
wrote = rc;
n -= wrote;
p = (char *)p + wrote;
} else {
return -1;
}
} while (n);
return 0;
}
static int bestlineWriteStr(int fd, const char *p) {
return bestlineWrite(fd, p, strlen(p));
}
static ssize_t bestlineRead(int fd, char *buf, size_t size,
struct bestlineState *l) {
size_t got;
ssize_t rc;
int refreshme;
do {
refreshme = 0;
if (gotint) {
errno = EINTR;
return -1;
}
if (gotcont && rawmode != -1) {
enableRawMode(rawmode);
if (l) refreshme = 1;
}
if (gotwinch && l) {
refreshme = 1;
}
if (refreshme) bestlineRefreshLine(l);
rc = bestlineReadCharacter(fd, buf, size);
} while (rc == -1 && errno == EINTR);
if (rc != -1) {
got = rc;
if (got > 0 && l) {
memcpy(l->seq[1], l->seq[0], sizeof(l->seq[0]));
memset(l->seq[0], 0, sizeof(l->seq[0]));
memcpy(l->seq[0], buf, Min(Min(size, got), sizeof(l->seq[0]) - 1));
}
}
return rc;
}
/**
* Returns number of columns in current terminal.
*
* 1. Checks COLUMNS environment variable (set by Emacs)
* 2. Tries asking termios (works for pseudoteletypewriters)
* 3. Falls back to inband signalling (works w/ pipe or serial)
* 4. Otherwise we conservatively assume 80 columns
*
* @param ws should be initialized by caller to zero before first call
* @param ifd is input file descriptor
* @param ofd is output file descriptor
* @return window size
*/
static struct winsize GetTerminalSize(struct winsize ws, int ifd, int ofd) {
int x;
ssize_t n;
char *p, *s, b[16];
ioctl(ofd, TIOCGWINSZ, &ws);
if ((!ws.ws_row &&
(s = getenv("ROWS")) &&
(x = ParseUnsigned(s, 0)))) {
ws.ws_row = x;
}
if ((!ws.ws_col &&
(s = getenv("COLUMNS")) &&
(x = ParseUnsigned(s, 0)))) {
ws.ws_col = x;
}
if (((!ws.ws_col || !ws.ws_row) &&
bestlineRead(ifd,0,0,0) != -1 &&
bestlineWriteStr(ofd,
"\0337" /* save position */
"\033[9979;9979H" /* move cursor to bottom right corner */
"\033[6n" /* report position */
"\0338") != -1 && /* restore position */
(n = bestlineRead(ifd,b,sizeof(b),0)) != -1 &&
n && b[0] == 033 && b[1] == '[' && b[n - 1] == 'R')) {
p = b+2;
if ((x = ParseUnsigned(p,&p))) ws.ws_row = x;
if (*p++ == ';' && (x = ParseUnsigned(p,0))) ws.ws_col = x;
}
if (!ws.ws_col) ws.ws_col = 80;
if (!ws.ws_row) ws.ws_row = 24;
return ws;
}
/* Clear the screen. Used to handle ctrl+l */
void bestlineClearScreen(int fd) {
bestlineWriteStr(fd,
"\033[H" /* move cursor to top left corner */
"\033[2J"); /* erase display */
}
static void bestlineBeep(void) {
/* THE TERMINAL BELL IS DEAD - HISTORY HAS KILLED IT */
}
static char bestlineGrow(struct bestlineState *ls, size_t n) {
char *p;
size_t m;
m = ls->buflen;
if (m >= n) return 1;
do m += m >> 1;
while (m < n);
if (!(p = (char *)realloc(ls->buf, m * sizeof(*ls->buf)))) return 0;
ls->buf = p;
ls->buflen = m;
return 1;
}
/* This is an helper function for bestlineEdit() and is called when the
* user types the <tab> key in order to complete the string currently in the
* input.
*
* The state of the editing is encapsulated into the pointed bestlineState
* structure as described in the structure definition. */
static ssize_t bestlineCompleteLine(struct bestlineState *ls, char *seq, int size) {
ssize_t nread;
size_t i, n, stop;
bestlineCompletions lc;
struct bestlineState saved;
nread=0;
memset(&lc,0,sizeof(lc));
completionCallback(ls->buf,&lc);
if (!lc.len) {
bestlineBeep();
} else {
i = 0;
stop = 0;
while (!stop) {
/* Show completion or original buffer */
if (i < lc.len) {
saved = *ls;
ls->len = ls->pos = strlen(lc.cvec[i]);
ls->buf = lc.cvec[i];
bestlineRefreshLine(ls);
ls->len = saved.len;
ls->pos = saved.pos;
ls->buf = saved.buf;
} else {
bestlineRefreshLine(ls);
}
if ((nread = bestlineRead(ls->ifd,seq,size,ls)) <= 0) {
bestlineFreeCompletions(&lc);
return -1;
}
switch (seq[0]) {
case '\t':
i = (i+1) % (lc.len+1);
if (i == lc.len) {
bestlineBeep();
}
break;
default:
if (i < lc.len) {
n = strlen(lc.cvec[i]);
if (bestlineGrow(ls, n + 1)) {
memcpy(ls->buf, lc.cvec[i], n + 1);
ls->len = ls->pos = n;
}
}
stop = 1;
break;
}
}
}
bestlineFreeCompletions(&lc);
return nread;
}
static void bestlineEditHistoryGoto(struct bestlineState *l, unsigned i) {
size_t n;
if (historylen <= 1) return;
i = Max(Min(i,historylen-1),0);
free(history[historylen - 1 - l->hindex]);
history[historylen - 1 - l->hindex] = strdup(l->buf);
l->hindex = i;
n = strlen(history[historylen - 1 - l->hindex]);
bestlineGrow(l, n + 1);
n = Min(n, l->buflen - 1);
memcpy(l->buf, history[historylen - 1 - l->hindex], n);
l->buf[n] = 0;
l->len = l->pos = n;
bestlineRefreshLine(l);
}
static void bestlineEditHistoryMove(struct bestlineState *l, int dx) {
bestlineEditHistoryGoto(l,l->hindex+dx);
}
static char *bestlineMakeSearchPrompt(struct abuf *ab, int fail, const char *s, int n) {
ab->len=0;
abAppendw(ab,'(');
if (fail) abAppends(ab,"failed ");
abAppends(ab,"reverse-i-search `\033[4m");
abAppend(ab,s,n);
abAppends(ab,"\033[24m");
abAppends(ab,s+n);
abAppendw(ab,Read32le("') "));
return ab->b;
}
static int bestlineSearch(struct bestlineState *l, char *seq, int size) {
char *p;
char isstale;
struct abuf ab;
struct abuf prompt;
unsigned i, j, k, matlen;
const char *oldprompt, *q;
int rc, fail, added, oldpos, oldindex;
if (historylen <= 1) return 0;
abInit(&ab);
abInit(&prompt);
oldpos = l->pos;
oldprompt = l->prompt;
oldindex = l->hindex;
for (fail=matlen=0;;) {
l->prompt = bestlineMakeSearchPrompt(&prompt,fail,ab.b,matlen);
bestlineRefreshLine(l);
fail = 1;
added = 0;
j = l->pos;
i = l->hindex;
rc = bestlineRead(l->ifd,seq,size,l);
if (rc > 0) {
if (seq[0] == Ctrl('?') || seq[0] == Ctrl('H')) {
if (ab.len) {
--ab.len;
matlen = Min(matlen, ab.len);
}
} else if (seq[0] == Ctrl('R')) {
if (j) {
--j;
} else if (i + 1 < historylen) {
++i;
j = strlen(history[historylen - 1 - i]);
}
} else if (seq[0] == Ctrl('G')) {
bestlineEditHistoryGoto(l,oldindex);
l->pos = oldpos;
rc = 0;
break;
} else if (IsControl(seq[0])) { /* only sees canonical c0 */
break;
} else {
abAppend(&ab,seq,rc);
added = rc;
}
} else {
break;
}
isstale = 0;
while (i < historylen) {
p = history[historylen - 1 - i];
k = strlen(p);
if (!isstale) {
j = Min(k, j + ab.len);
} else {
isstale = 0;
j = k;
}
if ((q = FindSubstringReverse(p, j, ab.b, ab.len))) {
bestlineEditHistoryGoto(l,i);
l->pos = q - p;
fail = 0;
if (added) {
matlen += added;
added = 0;
}
break;
} else {
isstale = 1;
++i;
}
}
}
l->prompt = oldprompt;
bestlineRefreshLine(l);
abFree(&prompt);
abFree(&ab);
bestlineRefreshLine(l);
return rc;
}
static void bestlineRingFree(void) {
size_t i;
for (i = 0; i < BESTLINE_MAX_RING; ++i) {
if (ring.p[i]) {
free(ring.p[i]);
ring.p[i] = 0;
}
}
}
static void bestlineRingPush(const char *p, size_t n) {
char *q;
if (!n) return;
if (!(q = (char *)malloc(n + 1))) return;
ring.i = (ring.i + 1) % BESTLINE_MAX_RING;
free(ring.p[ring.i]);
ring.p[ring.i] = (char *)memcpy(q, p, n);
ring.p[ring.i][n] = 0;
}
static void bestlineRingRotate(void) {
size_t i;
for (i = 0; i < BESTLINE_MAX_RING; ++i) {
ring.i = (ring.i - 1) % BESTLINE_MAX_RING;
if (ring.p[ring.i]) break;
}
}
static char *bestlineRefreshHints(struct bestlineState *l) {
char *hint;
struct abuf ab;
const char *ansi1 = "\033[90m", *ansi2 = "\033[39m";
if (!hintsCallback) return 0;
if (!(hint = hintsCallback(l->buf, &ansi1, &ansi2))) return 0;
abInit(&ab);
if (ansi1) abAppends(&ab, ansi1);
abAppends(&ab, hint);
if (ansi2) abAppends(&ab, ansi2);
if (freeHintsCallback) freeHintsCallback(hint);
return ab.b;
}
static size_t Backward(struct bestlineState *l, size_t pos) {
if (pos) {
do --pos;
while (pos && (l->buf[pos] & 0300) == 0200);
}
return pos;
}
static int bestlineEditMirrorLeft(struct bestlineState *l, int res[2]) {
unsigned c, pos, left, right, depth, index;
if ((pos = Backward(l, l->pos))) {
right = GetUtf8(l->buf + pos, l->len - pos).c;
if ((left = bestlineMirrorLeft(right))) {
depth = 0;
index = pos;
do {
pos = Backward(l, pos);
c = GetUtf8(l->buf + pos, l->len - pos).c;
if (c == right) {
++depth;
} else if (c == left) {
if (depth) {
--depth;
} else {
res[0] = pos;
res[1] = index;
return 0;
}
}
} while (pos);
}
}
return -1;
}
static int bestlineEditMirrorRight(struct bestlineState *l, int res[2]) {
struct rune rune;
unsigned pos, left, right, depth, index;
pos = l->pos;
rune = GetUtf8(l->buf + pos, l->len - pos);
left = rune.c;
if ((right = bestlineMirrorRight(left))) {
depth = 0;
index = pos;
do {
pos += rune.n;
rune = GetUtf8(l->buf + pos, l->len - pos);
if (rune.c == left) {
++depth;
} else if (rune.c == right) {
if (depth) {
--depth;
} else {
res[0] = index;
res[1] = pos;
return 0;
}
}
} while (pos + rune.n < l->len);
}
return -1;
}
static int bestlineEditMirror(struct bestlineState *l, int res[2]) {
int rc;
rc = bestlineEditMirrorLeft(l, res);
if (rc == -1) rc = bestlineEditMirrorRight(l, res);
return rc;
}
static void bestlineRefreshLineImpl(struct bestlineState *l, int force) {
char *hint;
char flipit;
char hasflip;
char haswides;
struct abuf ab;
const char *buf;
struct rune rune;
struct winsize oldsize;
int fd, plen, rows, len, pos;
unsigned x, xn, yn, width, pwidth;
int i, t, cx, cy, tn, resized, flip[2];
/*
* synchonize the i/o state
*/
if (ispaused) {
if (force) {
bestlineUnpause(l->ofd);
} else {
return;
}
}
if (!force && HasPendingInput(l->ifd)) {
l->dirty = 1;
return;
}
oldsize = l->ws;
if ((resized = gotwinch) && rawmode != -1) {
gotwinch = 0;
l->ws = GetTerminalSize(l->ws, l->ifd, l->ofd);
}
hasflip = !l->final && !bestlineEditMirror(l, flip);
StartOver:
fd = l->ofd;
buf = l->buf;
pos = l->pos;
len = l->len;
xn = l->ws.ws_col;
yn = l->ws.ws_row;
plen = strlen(l->prompt);
pwidth = GetMonospaceWidth(l->prompt, plen, 0);
width = GetMonospaceWidth(buf, len, &haswides);
/*
* handle the case where the line is larger than the whole display
* gnu readline actually isn't able to deal with this situation!!!
* we kludge xn to address the edge case of wide chars on the edge
*/
for (tn = xn - haswides * 2;;) {
if (pwidth + width + 1 < tn * yn) break; /* we're fine */
if (!len || width < 2) break; /* we can't do anything */
if (pwidth + 2 > tn * yn) break; /* we can't do anything */
if (pos > len / 2) {
/* hide content on the left if we're editing on the right */
rune = GetUtf8(buf, len);
buf += rune.n;
len -= rune.n;
pos -= rune.n;
} else {
/* hide content on the right if we're editing on left */
t = len;
while (len && (buf[len - 1] & 0300) == 0200) --len;
if (len) --len;
rune = GetUtf8(buf + len, t - len);
}
if ((t = GetMonospaceCharacterWidth(rune.c)) > 0) {
width -= t;
}
}
pos = Max(0, Min(pos, len));
/*
* now generate the terminal codes to update the line
*
* since we support unlimited lines it's important that we don't
* clear the screen before we draw the screen. doing that causes
* flickering. the key with terminals is to overwrite cells, and
* then use \e[K and \e[J to clear everything else.
*
* we make the assumption that prompts and hints may contain ansi
* sequences, but the buffer does not.
*
* we need to handle the edge case where a wide character like
* might be at the edge of the window, when there's one cell left.
* so we can't use division based on string width to compute the
* coordinates and have to track it as we go.
*/
cy = -1;
cx = -1;
rows = 1;
abInit(&ab);
abAppendw(&ab, '\r'); /* start of line */
if (l->rows - l->oldpos - 1 > 0) {
abAppends(&ab, "\033[");
abAppendu(&ab, l->rows - l->oldpos - 1);
abAppendw(&ab, 'A'); /* cursor up clamped */
}
abAppends(&ab, l->prompt);
x = pwidth;
for (i = 0; i < len; i += rune.n) {
rune = GetUtf8(buf + i, len - i);
if (x && x + rune.n > xn) {
if (cy >= 0) ++cy;
if (x < xn) {
abAppends(&ab, "\033[K"); /* clear line forward */
}
abAppends(&ab, "\r" /* start of line */
"\n"); /* cursor down unclamped */
++rows;
x = 0;
}
if (i == pos) {
cy = 0;
cx = x;
}
if (maskmode) {
abAppendw(&ab, '*');
} else {
flipit = hasflip && (i == flip[0] || i == flip[1]);
if (flipit) abAppends(&ab, "\033[1m");
abAppendw(&ab, EncodeUtf8(rune.c));
if (flipit) abAppends(&ab, "\033[22m");
}
t = GetMonospaceCharacterWidth(rune.c);
t = Max(0, t);
x += t;
}
if (!l->final && (hint = bestlineRefreshHints(l))) {
if (GetMonospaceWidth(hint, strlen(hint), 0) < xn - x) {
if (cx < 0) {
cx = x;
}
abAppends(&ab, hint);
}
free(hint);
}
abAppendw(&ab, Read32le("\033[J")); /* erase display forwards */
/*
* if we are at the very end of the screen with our prompt, we need
* to emit a newline and move the prompt to the first column.
*/
if (pos && pos == len && x >= xn) {
abAppendw(&ab, Read32le("\n\r\0"));
++rows;
}
/*
* move cursor to right position
*/
if (cy > 0) {
abAppends(&ab, "\033[");
abAppendu(&ab, cy);
abAppendw(&ab, 'A'); /* cursor up */
}
if (cx > 0) {
abAppendw(&ab, Read32le("\r\033["));
abAppendu(&ab, cx);
abAppendw(&ab, 'C'); /* cursor right */
} else if (!cx) {
abAppendw(&ab, '\r'); /* start */
}
/*
* now get ready to progress state
* we use a mostly correct kludge when the tty resizes
*/
l->rows = rows;
if (resized && oldsize.ws_col > l->ws.ws_col) {
resized = 0;
abFree(&ab);
goto StartOver;
}
l->dirty = 0;
l->oldpos = Max(0, cy);
/*
* send codes to terminal
*/
bestlineWrite(fd, ab.b, ab.len);
abFree(&ab);
}
static void bestlineRefreshLine(struct bestlineState *l) {
bestlineRefreshLineImpl(l, 0);
}
static void bestlineRefreshLineForce(struct bestlineState *l) {
bestlineRefreshLineImpl(l, 1);
}
static void bestlineEditInsert(struct bestlineState *l,
const char *p, size_t n) {
if (!bestlineGrow(l, l->len + n + 1)) return;
memmove(l->buf + l->pos + n, l->buf + l->pos, l->len - l->pos);
memcpy(l->buf + l->pos, p, n);
l->pos += n;
l->len += n;
l->buf[l->len] = 0;
bestlineRefreshLine(l);
}
static void bestlineEditHome(struct bestlineState *l) {
l->pos = 0;
bestlineRefreshLine(l);
}
static void bestlineEditEnd(struct bestlineState *l) {
l->pos = l->len;
bestlineRefreshLine(l);
}
static void bestlineEditUp(struct bestlineState *l) {
bestlineEditHistoryMove(l,BESTLINE_HISTORY_PREV);
}
static void bestlineEditDown(struct bestlineState *l) {
bestlineEditHistoryMove(l,BESTLINE_HISTORY_NEXT);
}
static void bestlineEditBof(struct bestlineState *l) {
bestlineEditHistoryMove(l,BESTLINE_HISTORY_FIRST);
}
static void bestlineEditEof(struct bestlineState *l) {
bestlineEditHistoryMove(l,BESTLINE_HISTORY_LAST);
}
static void bestlineEditRefresh(struct bestlineState *l) {
bestlineClearScreen(l->ofd);
bestlineRefreshLine(l);
}
static size_t Forward(struct bestlineState *l, size_t pos) {
return pos + GetUtf8(l->buf + pos, l->len - pos).n;
}
static size_t Backwards(struct bestlineState *l, size_t pos, char pred(unsigned)) {
size_t i;
struct rune r;
while (pos) {
i = Backward(l, pos);
r = GetUtf8(l->buf + i, l->len - i);
if (pred(r.c)) {
pos = i;
} else {
break;
}
}
return pos;
}
static size_t Forwards(struct bestlineState *l, size_t pos, char pred(unsigned)) {
struct rune r;
while (pos < l->len) {
r = GetUtf8(l->buf + pos, l->len - pos);
if (pred(r.c)) {
pos += r.n;
} else {
break;
}
}
return pos;
}
static size_t ForwardWord(struct bestlineState *l, size_t pos) {
pos = Forwards(l, pos, bestlineIsSeparator);
pos = Forwards(l, pos, bestlineNotSeparator);
return pos;
}
static size_t BackwardWord(struct bestlineState *l, size_t pos) {
pos = Backwards(l, pos, bestlineIsSeparator);
pos = Backwards(l, pos, bestlineNotSeparator);
return pos;
}
static size_t EscapeWord(struct bestlineState *l, size_t i) {
size_t j;
struct rune r;
for (; i && i < l->len; i += r.n) {
if (i < l->len) {
r = GetUtf8(l->buf + i, l->len - i);
if (bestlineIsSeparator(r.c)) break;
}
if ((j = i)) {
do --j;
while (j && (l->buf[j] & 0300) == 0200);
r = GetUtf8(l->buf + j, l->len - j);
if (bestlineIsSeparator(r.c)) break;
}
}
return i;
}
static void bestlineEditLeft(struct bestlineState *l) {
l->pos = Backward(l, l->pos);
bestlineRefreshLine(l);
}
static void bestlineEditRight(struct bestlineState *l) {
if (l->pos == l->len) return;
do l->pos++;
while (l->pos < l->len && (l->buf[l->pos] & 0300) == 0200);
bestlineRefreshLine(l);
}
static void bestlineEditLeftWord(struct bestlineState *l) {
l->pos = BackwardWord(l, l->pos);
bestlineRefreshLine(l);
}
static void bestlineEditRightWord(struct bestlineState *l) {
l->pos = ForwardWord(l, l->pos);
bestlineRefreshLine(l);
}
static void bestlineEditLeftExpr(struct bestlineState *l) {
int mark[2];
l->pos = Backwards(l, l->pos, bestlineIsXeparator);
if (!bestlineEditMirrorLeft(l, mark)) {
l->pos = mark[0];
} else {
l->pos = Backwards(l, l->pos, bestlineNotSeparator);
}
bestlineRefreshLine(l);
}
static void bestlineEditRightExpr(struct bestlineState *l) {
int mark[2];
l->pos = Forwards(l, l->pos, bestlineIsXeparator);
if (!bestlineEditMirrorRight(l, mark)) {
l->pos = Forward(l, mark[1]);
} else {
l->pos = Forwards(l, l->pos, bestlineNotSeparator);
}
bestlineRefreshLine(l);
}
static void bestlineEditDelete(struct bestlineState *l) {
size_t i;
if (l->pos == l->len) return;
i = Forward(l, l->pos);
memmove(l->buf+l->pos, l->buf+i, l->len-i+1);
l->len -= i - l->pos;
bestlineRefreshLine(l);
}
static void bestlineEditRubout(struct bestlineState *l) {
size_t i;
if (!l->pos) return;
i = Backward(l, l->pos);
memmove(l->buf+i, l->buf+l->pos, l->len-l->pos+1);
l->len -= l->pos - i;
l->pos = i;
bestlineRefreshLine(l);
}
static void bestlineEditDeleteWord(struct bestlineState *l) {
size_t i;
if (l->pos == l->len) return;
i = ForwardWord(l, l->pos);
bestlineRingPush(l->buf + l->pos, i - l->pos);
memmove(l->buf + l->pos, l->buf + i, l->len - i + 1);
l->len -= i - l->pos;
bestlineRefreshLine(l);
}
static void bestlineEditRuboutWord(struct bestlineState *l) {
size_t i;
if (!l->pos) return;
i = BackwardWord(l, l->pos);
bestlineRingPush(l->buf + i, l->pos - i);
memmove(l->buf + i, l->buf + l->pos, l->len - l->pos + 1);
l->len -= l->pos - i;
l->pos = i;
bestlineRefreshLine(l);
}
static void bestlineEditXlatWord(struct bestlineState *l, unsigned xlat(unsigned)) {
unsigned c;
size_t i, j;
struct rune r;
struct abuf ab;
abInit(&ab);
i = Forwards(l, l->pos, bestlineIsSeparator);
for (j = i; j < l->len; j += r.n) {
r = GetUtf8(l->buf + j, l->len - j);
if (bestlineIsSeparator(r.c)) break;
if ((c = xlat(r.c)) != r.c) {
abAppendw(&ab, EncodeUtf8(c));
} else { /* avoid canonicalization */
abAppend(&ab, l->buf + j, r.n);
}
}
if (ab.len && bestlineGrow(l, i + ab.len + l->len - j + 1)) {
l->pos = i + ab.len;
abAppend(&ab, l->buf + j, l->len - j);
l->len = i + ab.len;
memcpy(l->buf + i, ab.b, ab.len + 1);
bestlineRefreshLine(l);
}
abFree(&ab);
}
static void bestlineEditLowercaseWord(struct bestlineState *l) {
bestlineEditXlatWord(l, bestlineLowercase);
}
static void bestlineEditUppercaseWord(struct bestlineState *l) {
bestlineEditXlatWord(l, bestlineUppercase);
}
static void bestlineEditCapitalizeWord(struct bestlineState *l) {
iscapital = 0;
bestlineEditXlatWord(l, Capitalize);
}
static void bestlineEditKillLeft(struct bestlineState *l) {
size_t diff, old_pos;
bestlineRingPush(l->buf, l->pos);
old_pos = l->pos;
l->pos = 0;
diff = old_pos - l->pos;
memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
l->len -= diff;
bestlineRefreshLine(l);
}
static void bestlineEditKillRight(struct bestlineState *l) {
bestlineRingPush(l->buf + l->pos, l->len - l->pos);
l->buf[l->pos] = '\0';
l->len = l->pos;
bestlineRefreshLine(l);
}
static void bestlineEditYank(struct bestlineState *l) {
char *p;
size_t n;
if (!ring.p[ring.i]) return;
n = strlen(ring.p[ring.i]);
if (!bestlineGrow(l, l->len + n + 1)) return;
if (!(p = (char *)malloc(l->len - l->pos + 1))) return;
memcpy(p, l->buf + l->pos, l->len - l->pos + 1);
memcpy(l->buf + l->pos, ring.p[ring.i], n);
memcpy(l->buf + l->pos + n, p, l->len - l->pos + 1);
free(p);
l->yi = l->pos;
l->yj = l->pos + n;
l->pos += n;
l->len += n;
bestlineRefreshLine(l);
}
static void bestlineEditRotate(struct bestlineState *l) {
if ((l->seq[1][0] == Ctrl('Y') ||
(l->seq[1][0] == 033 && l->seq[1][1] == 'y'))) {
if (l->yi < l->len && l->yj <= l->len) {
memmove(l->buf + l->yi, l->buf + l->yj, l->len - l->yj + 1);
l->len -= l->yj - l->yi;
l->pos -= l->yj - l->yi;
}
bestlineRingRotate();
bestlineEditYank(l);
}
}
static void bestlineEditTranspose(struct bestlineState *l) {
char *q, *p;
size_t a, b, c;
b = l->pos;
if (b == l->len) --b;
a = Backward(l, b);
c = Forward(l, b);
if (!(a < b && b < c)) return;
p = q = (char *)malloc(c - a);
p = Copy(p, l->buf + b, c - b);
p = Copy(p, l->buf + a, b - a);
assert((size_t)(p - q) == c - a);
memcpy(l->buf + a, q, p - q);
l->pos = c;
free(q);
bestlineRefreshLine(l);
}
static void bestlineEditTransposeWords(struct bestlineState *l) {
char *q, *p;
size_t i, pi, xi, xj, yi, yj;
i = l->pos;
if (i == l->len) {
i = Backwards(l, i, bestlineIsSeparator);
i = Backwards(l, i, bestlineNotSeparator);
}
pi = EscapeWord(l, i);
xj = Backwards(l, pi, bestlineIsSeparator);
xi = Backwards(l, xj, bestlineNotSeparator);
yi = Forwards(l, pi, bestlineIsSeparator);
yj = Forwards(l, yi, bestlineNotSeparator);
if (!(xi < xj && xj < yi && yi < yj)) return;
p = q = (char *)malloc(yj - xi);
p = Copy(p, l->buf + yi, yj - yi);
p = Copy(p, l->buf + xj, yi - xj);
p = Copy(p, l->buf + xi, xj - xi);
assert((size_t)(p - q) == yj - xi);
memcpy(l->buf + xi, q, p - q);
l->pos = yj;
free(q);
bestlineRefreshLine(l);
}
static void bestlineEditSqueeze(struct bestlineState *l) {
size_t i, j;
i = Backwards(l, l->pos, bestlineIsSeparator);
j = Forwards(l, l->pos, bestlineIsSeparator);
if (!(i < j)) return;
memmove(l->buf + i, l->buf + j, l->len - j + 1);
l->len -= j - i;
l->pos = i;
bestlineRefreshLine(l);
}
static void bestlineEditMark(struct bestlineState *l) {
l->mark = l->pos;
}
static void bestlineEditGoto(struct bestlineState *l) {
if (l->mark > l->len) return;
l->pos = Min(l->mark, l->len);
bestlineRefreshLine(l);
}
static size_t bestlineEscape(char *d, const char *s, size_t n) {
char *p;
size_t i;
unsigned c, w, l;
for (p = d, l = i = 0; i < n; ++i) {
switch ((c = s[i] & 255)) {
Case('\a', w = Read16le("\\a"));
Case('\b', w = Read16le("\\b"));
Case('\t', w = Read16le("\\t"));
Case('\n', w = Read16le("\\n"));
Case('\v', w = Read16le("\\v"));
Case('\f', w = Read16le("\\f"));
Case('\r', w = Read16le("\\r"));
Case('"', w = Read16le("\\\""));
Case('\'', w = Read16le("\\\'"));
Case('\\', w = Read16le("\\\\"));
default:
if (c <= 0x1F || c == 0x7F ||
(c == '?' && l == '?')) {
w = Read16le("\\x");
w |= "0123456789abcdef"[(c & 0xF0) >> 4] << 020;
w |= "0123456789abcdef"[(c & 0x0F) >> 0] << 030;
} else {
w = c;
}
break;
}
p[0] = (w & 0x000000ff) >> 000;
p[1] = (w & 0x0000ff00) >> 010;
p[2] = (w & 0x00ff0000) >> 020;
p[3] = (w & 0xff000000) >> 030;
p += (Bsr(w) >> 3) + 1;
l = w;
}
return p - d;
}
static void bestlineEditInsertEscape(struct bestlineState *l) {
size_t m;
ssize_t n;
char seq[16];
char esc[sizeof(seq) * 4];
if ((n = bestlineRead(l->ifd, seq, sizeof(seq), l)) > 0) {
m = bestlineEscape(esc, seq, n);
bestlineEditInsert(l, esc, m);
}
}
static void bestlineEditInterrupt(void) {
gotint = SIGINT;
}
static void bestlineEditQuit(void) {
gotint = SIGQUIT;
}
static void bestlineEditSuspend(void) {
raise(SIGSTOP);
}
static void bestlineEditPause(struct bestlineState *l) {
tcflow(l->ofd, TCOOFF);
ispaused = 1;
}
static void bestlineEditCtrlq(struct bestlineState *l) {
if (ispaused) {
bestlineUnpause(l->ofd);
bestlineRefreshLineForce(l);
} else {
bestlineEditInsertEscape(l);
}
}
/**
* Moves last item inside current s-expression to outside, e.g.
*
* (a| b c)
* (a| b) c
*
* The cursor position changes only if a paren is moved before it:
*
* (a b c |)
* (a b) c |
*
* To accommodate non-LISP languages we connect unspaced outer symbols:
*
* f(a,| b, g())
* f(a,| b), g()
*
* Our standard keybinding is ALT-SHIFT-B.
*/
static void bestlineEditBarf(struct bestlineState *l) {
struct rune r;
unsigned long w;
size_t i, pos, depth = 0;
unsigned lhs, rhs, end, *stack = 0;
/* go as far right within current s-expr as possible */
for (pos = l->pos;; pos += r.n) {
if (pos == l->len) goto Finish;
r = GetUtf8(l->buf + pos, l->len - pos);
if (depth) {
if (r.c == stack[depth - 1]) {
--depth;
}
} else {
if ((rhs = bestlineMirrorRight(r.c))) {
stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
stack[depth - 1] = rhs;
} else if (bestlineMirrorLeft(r.c)) {
end = pos;
break;
}
}
}
/* go back one item */
pos = Backwards(l, pos, bestlineIsXeparator);
for (;; pos = i) {
if (!pos) goto Finish;
i = Backward(l, pos);
r = GetUtf8(l->buf + i, l->len - i);
if (depth) {
if (r.c == stack[depth - 1]) {
--depth;
}
} else {
if ((lhs = bestlineMirrorLeft(r.c))) {
stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
stack[depth - 1] = lhs;
} else if (bestlineIsSeparator(r.c)) {
break;
}
}
}
pos = Backwards(l, pos, bestlineIsXeparator);
/* now move the text */
r = GetUtf8(l->buf + end, l->len - end);
memmove(l->buf + pos + r.n, l->buf + pos, end - pos);
w = EncodeUtf8(r.c);
for (i = 0; i < r.n; ++i) {
l->buf[pos + i] = w;
w >>= 8;
}
if (l->pos > pos) {
l->pos += r.n;
}
bestlineRefreshLine(l);
Finish:
free(stack);
}
/**
* Moves first item outside current s-expression to inside, e.g.
*
* (a| b) c d
* (a| b c) d
*
* To accommodate non-LISP languages we connect unspaced outer symbols:
*
* f(a,| b), g()
* f(a,| b, g())
*
* Our standard keybinding is ALT-SHIFT-S.
*/
static void bestlineEditSlurp(struct bestlineState *l) {
char rp[6];
struct rune r;
size_t pos, depth = 0;
unsigned rhs, point = 0, start = 0, *stack = 0;
/* go to outside edge of current s-expr */
for (pos = l->pos; pos < l->len; pos += r.n) {
r = GetUtf8(l->buf + pos, l->len - pos);
if (depth) {
if (r.c == stack[depth - 1]) {
--depth;
}
} else {
if ((rhs = bestlineMirrorRight(r.c))) {
stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
stack[depth - 1] = rhs;
} else if (bestlineMirrorLeft(r.c)) {
point = pos;
pos += r.n;
start = pos;
break;
}
}
}
/* go forward one item */
pos = Forwards(l, pos, bestlineIsXeparator);
for (; pos < l->len ; pos += r.n) {
r = GetUtf8(l->buf + pos, l->len - pos);
if (depth) {
if (r.c == stack[depth - 1]) {
--depth;
}
} else {
if ((rhs = bestlineMirrorRight(r.c))) {
stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
stack[depth - 1] = rhs;
} else if (bestlineIsSeparator(r.c)) {
break;
}
}
}
/* now move the text */
memcpy(rp, l->buf + point, start - point);
memmove(l->buf + point, l->buf + start, pos - start);
memcpy(l->buf + pos - (start - point), rp, start - point);
bestlineRefreshLine(l);
free(stack);
}
static void bestlineEditRaise(struct bestlineState *l) {
(void)l;
}
/**
* Runs bestline engine.
*
* This function is the core of the line editing capability of bestline.
* It expects 'fd' to be already in "raw mode" so that every key pressed
* will be returned ASAP to read().
*
* The resulting string is put into 'buf' when the user type enter, or
* when ctrl+d is typed.
*
* Returns chomped character count in buf >=0 or -1 on eof / error
*/
static ssize_t bestlineEdit(int stdin_fd, int stdout_fd, const char *prompt,
char **obuf) {
ssize_t rc;
size_t nread;
struct rune rune;
char *p, seq[16];
unsigned long long w;
struct bestlineState l;
memset(&l,0,sizeof(l));
if (!(l.buf = (char *)malloc((l.buflen = 32)))) return -1;
l.buf[0] = 0;
l.ifd = stdin_fd;
l.ofd = stdout_fd;
l.prompt = prompt ? prompt : "";
l.ws = GetTerminalSize(l.ws,l.ifd,l.ofd);
bestlineHistoryAdd("");
bestlineWriteStr(l.ofd,l.prompt);
while (1) {
if (l.dirty) bestlineRefreshLineForce(&l);
rc = bestlineRead(l.ifd,seq,sizeof(seq),&l);
if (rc > 0) {
if (seq[0] == Ctrl('R')) {
rc = bestlineSearch(&l,seq,sizeof(seq));
if (!rc) continue;
} else if (seq[0] == '\t' && completionCallback) {
rc = bestlineCompleteLine(&l,seq,sizeof(seq));
if (!rc) continue;
}
}
if (rc > 0) {
nread = rc;
} else if (!rc && l.len) {
nread = 1;
seq[0] = '\r';
seq[1] = 0;
} else {
free(history[--historylen]);
history[historylen] = 0;
free(l.buf);
return -1;
}
switch (seq[0]) {
Case(Ctrl('P'), bestlineEditUp(&l));
Case(Ctrl('E'), bestlineEditEnd(&l));
Case(Ctrl('N'), bestlineEditDown(&l));
Case(Ctrl('A'), bestlineEditHome(&l));
Case(Ctrl('B'), bestlineEditLeft(&l));
Case(Ctrl('@'), bestlineEditMark(&l));
Case(Ctrl('Y'), bestlineEditYank(&l));
Case(Ctrl('Q'), bestlineEditCtrlq(&l));
Case(Ctrl('F'), bestlineEditRight(&l));
Case(Ctrl('\\'), bestlineEditQuit());
Case(Ctrl('S'), bestlineEditPause(&l));
Case(Ctrl('?'), bestlineEditRubout(&l));
Case(Ctrl('H'), bestlineEditRubout(&l));
Case(Ctrl('L'), bestlineEditRefresh(&l));
Case(Ctrl('Z'), bestlineEditSuspend());
Case(Ctrl('U'), bestlineEditKillLeft(&l));
Case(Ctrl('T'), bestlineEditTranspose(&l));
Case(Ctrl('K'), bestlineEditKillRight(&l));
Case(Ctrl('W'), bestlineEditRuboutWord(&l));
case Ctrl('C'):
if (bestlineRead(l.ifd,seq,sizeof(seq),&l) != 1) break;
switch (seq[0]) {
Case(Ctrl('C'), bestlineEditInterrupt());
Case(Ctrl('B'), bestlineEditBarf(&l));
Case(Ctrl('S'), bestlineEditSlurp(&l));
Case(Ctrl('R'), bestlineEditRaise(&l));
default:
break;
}
break;
case Ctrl('X'):
if (l.seq[1][0] == Ctrl('X')) {
bestlineEditGoto(&l);
}
break;
case Ctrl('D'):
if (l.len) {
bestlineEditDelete(&l);
} else {
free(history[--historylen]);
history[historylen] = 0;
free(l.buf);
return -1;
}
break;
case '\r':
l.final = 1;
free(history[--historylen]);
history[historylen] = 0;
bestlineEditEnd(&l);
bestlineRefreshLineForce(&l);
if ((p = (char *)realloc(l.buf, l.len + 1))) l.buf = p;
*obuf = l.buf;
return l.len;
case 033:
if (nread < 2) break;
switch (seq[1]) {
Case('<', bestlineEditBof(&l));
Case('>', bestlineEditEof(&l));
Case('B', bestlineEditBarf(&l));
Case('S', bestlineEditSlurp(&l));
Case('R', bestlineEditRaise(&l));
Case('y', bestlineEditRotate(&l));
Case('\\', bestlineEditSqueeze(&l));
Case('b', bestlineEditLeftWord(&l));
Case('f', bestlineEditRightWord(&l));
Case('h', bestlineEditRuboutWord(&l));
Case('d', bestlineEditDeleteWord(&l));
Case('l', bestlineEditLowercaseWord(&l));
Case('u', bestlineEditUppercaseWord(&l));
Case('c', bestlineEditCapitalizeWord(&l));
Case('t', bestlineEditTransposeWords(&l));
Case(Ctrl('B'), bestlineEditLeftExpr(&l));
Case(Ctrl('F'), bestlineEditRightExpr(&l));
Case(Ctrl('H'), bestlineEditRuboutWord(&l));
case '[':
if (nread < 3) break;
if (seq[2] >= '0' && seq[2] <= '9') {
if (nread < 4) break;
if (seq[3] == '~') {
switch (seq[2]) {
Case('1', bestlineEditHome(&l)); /* \e[1~ */
Case('3', bestlineEditDelete(&l)); /* \e[3~ */
Case('4', bestlineEditEnd(&l)); /* \e[4~ */
default:
break;
}
}
} else {
switch (seq[2]) {
Case('A', bestlineEditUp(&l));
Case('B', bestlineEditDown(&l));
Case('C', bestlineEditRight(&l));
Case('D', bestlineEditLeft(&l));
Case('H', bestlineEditHome(&l));
Case('F', bestlineEditEnd(&l));
default:
break;
}
}
break;
case 'O':
if (nread < 3) break;
switch (seq[2]) {
Case('A', bestlineEditUp(&l));
Case('B', bestlineEditDown(&l));
Case('C', bestlineEditRight(&l));
Case('D', bestlineEditLeft(&l));
Case('H', bestlineEditHome(&l));
Case('F', bestlineEditEnd(&l));
default:
break;
}
break;
case 033:
if (nread < 3) break;
switch (seq[2]) {
case '[':
if (nread < 4) break;
switch (seq[3]) {
Case('C', bestlineEditRightExpr(&l)); /* \e\e[C alt-right */
Case('D', bestlineEditLeftExpr(&l)); /* \e\e[D alt-left */
default:
break;
}
break;
case 'O':
if (nread < 4) break;
switch (seq[3]) {
Case('C', bestlineEditRightExpr(&l)); /* \e\eOC alt-right */
Case('D', bestlineEditLeftExpr(&l)); /* \e\eOD alt-left */
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
break;
default:
if (!IsControl(seq[0])) { /* only sees canonical c0 */
if (xlatCallback) {
rune = GetUtf8(seq,nread);
w = EncodeUtf8(xlatCallback(rune.c));
nread = 0;
do {
seq[nread++] = w;
} while ((w >>= 8));
}
bestlineEditInsert(&l,seq,nread);
}
break;
}
}
}
void bestlineFree(void *ptr) {
free(ptr);
}
void bestlineHistoryFree(void) {
size_t i;
for (i = 0; i < BESTLINE_MAX_HISTORY; i++) {
if (history[i]) {
free(history[i]);
history[i] = 0;
}
}
historylen = 0;
}
static void bestlineAtExit(void) {
bestlineDisableRawMode();
bestlineHistoryFree();
bestlineRingFree();
}
int bestlineHistoryAdd(const char *line) {
char *linecopy;
if (!BESTLINE_MAX_HISTORY) return 0;
if (historylen && !strcmp(history[historylen-1], line)) return 0;
if (!(linecopy = strdup(line))) return 0;
if (historylen == BESTLINE_MAX_HISTORY) {
free(history[0]);
memmove(history,history+1,sizeof(char*)*(BESTLINE_MAX_HISTORY-1));
historylen--;
}
history[historylen++] = linecopy;
return 1;
}
/**
* Saves line editing history to file.
*
* @return 0 on success, or -1 w/ errno
*/
int bestlineHistorySave(const char *filename) {
FILE *fp;
unsigned j;
mode_t old_umask;
old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
fp = fopen(filename,"w");
umask(old_umask);
if (!fp) return -1;
chmod(filename,S_IRUSR|S_IWUSR);
for (j = 0; j < historylen; j++) {
fputs(history[j],fp);
fputc('\n',fp);
}
fclose(fp);
return 0;
}
/**
* Loads history from the specified file.
*
* If the file doesn't exist, zero is returned and this will do nothing.
* If the file does exists and the operation succeeded zero is returned
* otherwise on error -1 is returned.
*
* @return 0 on success, or -1 w/ errno
*/
int bestlineHistoryLoad(const char *filename) {
char **h;
int rc, fd, err;
size_t i, j, k, n, t;
char *m, *e, *p, *q, *f, *s;
err = errno, rc = 0;
if (!BESTLINE_MAX_HISTORY) return 0;
if (!(h = (char**)calloc(2*BESTLINE_MAX_HISTORY,sizeof(char*)))) return -1;
if ((fd = open(filename,O_RDONLY)) != -1) {
if ((n = GetFdSize(fd))) {
if ((m = (char *)mmap(0,n,PROT_READ,MAP_SHARED,fd,0))!=MAP_FAILED) {
for (i = 0, e = (p = m) + n; p < e; p = f + 1) {
if (!(q = (char *)memchr(p, '\n', e - p))) q = e;
for (f = q; q > p; --q) {
if (q[-1] != '\n' && q[-1] != '\r') break;
}
if (q > p) {
h[i * 2 + 0] = p;
h[i * 2 + 1] = q;
i = (i + 1) % BESTLINE_MAX_HISTORY;
}
}
bestlineHistoryFree();
for (j = 0; j < BESTLINE_MAX_HISTORY; ++j) {
if (h[(k = (i + j) % BESTLINE_MAX_HISTORY) * 2]) {
if ((s = (char *)malloc((t=h[k*2+1]-h[k*2])+1))) {
memcpy(s,h[k*2],t),s[t]=0;
history[historylen++] = s;
}
}
}
munmap(m,n);
} else {
rc = -1;
}
}
close(fd);
} else if (errno == ENOENT) {
errno = err;
} else {
rc = -1;
}
free(h);
return rc;
}
/**
* Reads line interactively.
*
* This function can be used instead of bestline() in cases where we
* know for certain we're dealing with a terminal, which means we can
* avoid linking any stdio code.
*
* @return chomped allocated string of read line or null on eof/error
*/
char *bestlineRaw(const char *prompt, int infd, int outfd) {
char *buf;
ssize_t rc;
static char once;
struct sigaction sa[3];
if (!once) atexit(bestlineAtExit), once = 1;
if (enableRawMode(infd) == -1) return 0;
buf = 0;
gotint = 0;
sigemptyset(&sa->sa_mask);
sa->sa_flags = 0;
sa->sa_handler = bestlineOnInt;
sigaction(SIGINT,sa,sa+1);
sigaction(SIGQUIT,sa,sa+2);
rc = bestlineEdit(infd,outfd,prompt,&buf);
bestlineDisableRawMode();
sigaction(SIGQUIT,sa+2,0);
sigaction(SIGINT,sa+1,0);
if (gotint) {
free(buf);
buf = 0;
raise(gotint);
errno = EINTR;
rc = -1;
}
if (rc != -1) {
bestlineWriteStr(outfd,"\n");
return buf;
} else {
free(buf);
return 0;
}
}
/**
* Reads line intelligently.
*
* The high level function that is the main API of the bestline library.
* This function checks if the terminal has basic capabilities, just checking
* for a blacklist of inarticulate terminals, and later either calls the line
* editing function or uses dummy fgets() so that you will be able to type
* something even in the most desperate of the conditions.
*
* @param prompt is printed before asking for input if we have a term
* and this may be set to empty or null to disable and prompt may
* contain ansi escape sequences, color, utf8, etc.
* @return chomped allocated string of read line or null on eof/error
*/
char *bestline(const char *prompt) {
if (prompt && *prompt &&
(strchr(prompt, '\n') || strchr(prompt, '\t') ||
strchr(prompt + 1, '\r'))) {
errno = EINVAL;
return 0;
}
if ((!isatty(fileno(stdin)) ||
!isatty(fileno(stdout)))) {
if (prompt && *prompt && (IsCharDev(fileno(stdin)) &&
IsCharDev(fileno(stdout)))) {
fputs(prompt,stdout);
fflush(stdout);
}
return GetLine(stdin, stdout);
} else if (bestlineIsUnsupportedTerm()) {
if (prompt && *prompt) {
fputs(prompt,stdout);
fflush(stdout);
}
return GetLine(stdin, stdout);
} else {
fflush(stdout);
return bestlineRaw(prompt,fileno(stdin),fileno(stdout));
}
}
/**
* Reads line intelligently w/ history, e.g.
*
* // see ~/.foo_history
* main() {
* char *line;
* while ((line = bestlineWithHistory("IN> ", "foo"))) {
* printf("OUT> %s\n", line);
* free(line);
* }
* }
*
* @param prompt is printed before asking for input if we have a term
* and this may be set to empty or null to disable and prompt may
* contain ansi escape sequences, color, utf8, etc.
* @param prog is name of your app, used to generate history filename
* however if it contains a slash / dot then we'll assume prog is
* the history filename which as determined by the caller
* @return chomped allocated string of read line or null on eof/error
*/
char *bestlineWithHistory(const char *prompt, const char *prog) {
char *line;
struct abuf path;
const char *a, *b;
abInit(&path);
if (prog) {
if (strchr(prog, '/') || strchr(prog, '.')) {
abAppends(&path, prog);
} else {
b = "";
if (!(a = getenv("HOME"))) {
if (!(a = getenv("HOMEDRIVE")) ||
!(b = getenv("HOMEPATH"))) {
a = "";
}
}
if (*a) {
abAppends(&path, a);
abAppends(&path, b);
abAppendw(&path, '/');
}
abAppendw(&path, '.');
abAppends(&path, prog);
abAppends(&path, "_history");
}
}
if (path.len) {
bestlineHistoryLoad(path.b);
}
line = bestline(prompt);
if (path.len && line && *line) {
/* history here is inefficient but helpful when the user has multiple
* repls open at the same time, so history propagates between them */
bestlineHistoryLoad(path.b);
bestlineHistoryAdd(line);
bestlineHistorySave(path.b);
}
abFree(&path);
return line;
}
/**
* Registers tab completion callback.
*/
void bestlineSetCompletionCallback(bestlineCompletionCallback *fn) {
completionCallback = fn;
}
/**
* Registers hints callback.
*
* Register a hits function to be called to show hits to the user at the
* right of the prompt.
*/
void bestlineSetHintsCallback(bestlineHintsCallback *fn) {
hintsCallback = fn;
}
/**
* Sets free hints callback.
*
* This registers a function to free the hints returned by the hints
* callback registered with bestlineSetHintsCallback().
*/
void bestlineSetFreeHintsCallback(bestlineFreeHintsCallback *fn) {
freeHintsCallback = fn;
}
/**
* Sets character translation callback.
*/
void bestlineSetXlatCallback(bestlineXlatCallback *fn) {
xlatCallback = fn;
}
/**
* Adds completion.
*
* This function is used by the callback function registered by the user
* in order to add completion options given the input string when the
* user typed <tab>. See the example.c source code for a very easy to
* understand example.
*/
void bestlineAddCompletion(bestlineCompletions *lc, const char *str) {
size_t len;
char *copy, **cvec;
if ((copy = (char *)malloc((len = strlen(str))+1))) {
memcpy(copy,str,len+1);
if ((cvec = (char **)realloc(lc->cvec,(lc->len+1)*sizeof(*lc->cvec)))) {
lc->cvec = cvec;
lc->cvec[lc->len++] = copy;
} else {
free(copy);
}
}
}
/**
* Frees list of completion option populated by bestlineAddCompletion().
*/
void bestlineFreeCompletions(bestlineCompletions *lc) {
size_t i;
for (i = 0; i < lc->len; i++)
free(lc->cvec[i]);
if (lc->cvec)
free(lc->cvec);
}
/**
* Enables "mask mode".
*
* When it is enabled, instead of the input that the user is typing, the
* terminal will just display a corresponding number of asterisks, like
* "****". This is useful for passwords and other secrets that should
* not be displayed.
*
* @see bestlineMaskModeDisable()
*/
void bestlineMaskModeEnable(void) {
maskmode = 1;
}
/**
* Disables "mask mode".
*/
void bestlineMaskModeDisable(void) {
maskmode = 0;
}