Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for UTF-8 support #37

Open
joakim-tjernlund opened this issue Jan 11, 2020 · 16 comments · May be fixed by #49
Open

Request for UTF-8 support #37

joakim-tjernlund opened this issue Jan 11, 2020 · 16 comments · May be fixed by #49

Comments

@joakim-tjernlund
Copy link

Any idea if UTF support can be added to this lib?
Preferably without depending on locale stuff from glibc

@troglobit
Copy link
Owner

Sure it can, it's just a lot of work since most of the lib is built around the notion that a character is one byte wide. A Unicode character, as far as I know, can be two, three, or four bytes wide.

Do you mind telling med the usecase? If our interests interlock (which I've noticed quite a few times) I may be able to help in such an endeavor.

@joakim-tjernlund
Copy link
Author

Today most terminals default to UTF-8 so i think it wold be great if editline could do UTF-8
but limit accepted chars to ISO Latin .
That would help our use case where users can connect via telnet/ssh/rs232 and non ascii
chars would just work.

@troglobit
Copy link
Owner

Yeah, that's a good limitation. I'll have a look again and try to estimate the amount of work needed.

@troglobit
Copy link
Owner

Interestingly quite a lot actually works already. What needs attention is cursor movement, most notably when editing a line. It is hard-coded to one-character=one-byte ... also, most of the internals use 'int' to store a character during processing. So it shouldn't be too much work to straighten out libeditline \o/

@troglobit troglobit changed the title UTF8 support? Request for UTF-8 support Jan 12, 2020
@joakim-tjernlund
Copy link
Author

Any progress on this?

@troglobit
Copy link
Owner

Nope. I've been redirected/reprioritized at work atm, so I'll have to circulate back to this later. Sorry.

@skull-squadron
Copy link

skull-squadron commented Feb 26, 2020

When there is time, the easiest way to do this would be like how another library handled it: basically duplicate the API (functions, header and library) and append a suffix such as w to all relevant symbols. Then, use something like libunistring, utf8proc or icu4c to handle conversion from raw bytes to UTF-8, and then iterating on individual 21-bit code points (stored 1 to 4 bytes long). There are numerous edge-cases like non-/UTF-8 BOMs to discard at the beginning of input, code-points spanning reads and UTF-16 surrogate pairs to (not) handle, so writing a solid parser yourself isn't easy. It gets even more complicated (converting the latest Unicode standard version into optimal conditional statements) to decide what 21-bit value is valid Unicode.

@troglobit
Copy link
Owner

@steakknife Thank you for the input! I sort of came to the same conclusion wrt parsing, but I'm really hesitant to make editline depend on other libraries, the whole point of editline is to be small and have as few (no) dependencies as possible. Whenever I circle back to this (or someone else does), your input will be a valuable starting point for further investigation!

@Artoria2e5
Copy link

Artoria2e5 commented Apr 19, 2021

I doubt there is any need for an extra layer on top of that, let along any libraries. The one thing we need to get from a UTF-8 string is the width of characters. Every POSIX system has a int wcwidth(wchar_t c), and all we need is to use mbrtowc to feed it. (On Windows you will need to carry your own wcwidth table and the conversion function. busybox has a nice slim version.)

@troglobit
Copy link
Owner

Good point. Just remember that BusyBox is GPL, this lib isn't.

@minfrin
Copy link

minfrin commented Apr 20, 2021

Replxx (BSD license) has an example of calculating the number of unicode codepoints in a UTF8 string:

https://github.com/AmokHuginnsson/replxx/blob/master/examples/util.c#L3

@Artoria2e5 Artoria2e5 linked a pull request Apr 20, 2021 that will close this issue
@timkuijsten
Copy link

When it comes to supporting different locales I found this presentation by the OpenBSD maintainer of libedit very helpful: https://czyborra.com/yudit/eurobsdcon2016-utf8.pdf

Supporting UTF-8 might be possible without having to duplicate all interfaces or dragging in heavy weights like libiconv.

@skull-squadron
Copy link

skull-squadron commented Oct 13, 2022

@troglobit To be honest, Unicode (UTF-8, -16LE/BE, -32LE/BE, and UCS2/4) support including BOM detection/removal, sanitization, and normalization is trivial-adjacent. The catch is 95% of libraries get it wrong and/or don't keep it current, including tools at work used to parse Rust code. (We're in the midst of a refactoring bonanza.) The maintenance required is precision around keeping current with Unicode versions and correctly classifying characters that could fall under multiple domains. The older Unicode references list these concerns more pedantically than newer ones. It's also worth being away of runtime behavioral changes imposed by LC_CTYPE, LC_COLLATE, LC_ALL, and LANG in GNU/Linux, POSIX, and other systems. For example, when grepping random binary files for text, better set LC_ALL=C or it's likely to assume UTF-8 is the lingua franca.

@skull-squadron
Copy link

skull-squadron commented Oct 13, 2022

@minfrin

Nice. I recall having to fork hirb-unicode to correctly draw text tables in an ActiveRecord ORM containing Hebrew and Japanese.

Input: 
   unsigned integer c - the code point of the character to be encoded
Output: 
   byte b1, b2, b3, b4 - the encoded sequence of bytes
Algorithm:
   if (c<0x80) 
      b1 = c>>0  & 0x7F | 0x00
      b2 = 0x00
      b3 = 0x00
      b4 = 0x00
   else if (c<0x0800)
      b1 = c>>6  & 0x1F | 0xC0
      b2 = c>>0  & 0x3F | 0x80
      b3 = 0x00
      b4 = 0x00
   else if (c<0x010000)
      b1 = c>>12 & 0x0F | 0xE0
      b2 = c>>6  & 0x3F | 0x80
      b3 = c>>0  & 0x3F | 0x80
      b4 = 0x00
   else if (c<0x110000)
      b1 = c>>18 & 0x07 | 0xF0
      b2 = c>>12 & 0x3F | 0x80
      b3 = c>>6  & 0x3F | 0x80
      b4 = c>>0  & 0x3F | 0x80
   end if

https://herongyang.com/Unicode/UTF-8-UTF-8-Encoding-Algorithm.html

Absolute range of U+0 to U+10FFFF

UTF-16 surrogate pairs are U+D800 to U+DFFF, so never those.

And then it gets trickier.

What is valid depends on the Unicode version. Validation code can be mechanically generated from a list of all valid code points. Here's the latest database. It's XML but contains all of the painful-to-find equivalence class metadata. Simplicity but not so simple as to be incorrect. ;)

Here's how Microsoft C# / CLR counts UTF-8 code-points.

@Artoria2e5
Copy link

Artoria2e5 commented Oct 15, 2022

Well uh... I don't think a line-editing program needs to concern itself with BOM, or to know about any Unicode properties, let alone hard-code ranges of assigned UTF code points. Heck, do we even need to check for surrogates for "valid" UTF-8 as opposed to WTF-8? If someone types it it's their fault, and even that could have use on quirky filesystems (looks at Windows). Grapheme editing is also overrated -- being able to navigate individual code points in sequences is actually fun and not very distressing!

@troglobit
Copy link
Owner

Whoa, a sudden burst of activity in this old thread! 😃

Lots of interesting suggestions coming in, so let me just reiterate: no external dependencies, and keep it simple. No need to go full-blown unicode support, it's fine to start with a release that only does utf-8 and possibly even having to explicitly enable it at build time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants