]> git.lizzy.rs Git - linenoise.git/blob - utf8.c
Extend utf-8 support to 4 byte characters
[linenoise.git] / utf8.c
1 /**
2  * UTF-8 utility functions
3  *
4  * (c) 2010-2016 Steve Bennett <steveb@workware.net.au>
5  *
6  * See LICENCE for licence details.
7  */
8
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include "utf8.h"
14
15 #ifdef USE_UTF8
16 int utf8_fromunicode(char *p, unsigned uc)
17 {
18     if (uc <= 0x7f) {
19         *p = uc;
20         return 1;
21     }
22     else if (uc <= 0x7ff) {
23         *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
24         *p = 0x80 | (uc & 0x3f);
25         return 2;
26     }
27     else if (uc <= 0xffff) {
28         *p++ = 0xe0 | ((uc & 0xf000) >> 12);
29         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
30         *p = 0x80 | (uc & 0x3f);
31         return 3;
32     }
33     /* Note: We silently truncate to 21 bits here: 0x1fffff */
34     else {
35         *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
36         *p++ = 0x80 | ((uc & 0x3f000) >> 12);
37         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
38         *p = 0x80 | (uc & 0x3f);
39         return 4;
40     }
41 }
42
43 int utf8_charlen(int c)
44 {
45     if ((c & 0x80) == 0) {
46         return 1;
47     }
48     if ((c & 0xe0) == 0xc0) {
49         return 2;
50     }
51     if ((c & 0xf0) == 0xe0) {
52         return 3;
53     }
54     if ((c & 0xf8) == 0xf0) {
55         return 4;
56     }
57     /* Invalid sequence */
58     return -1;
59 }
60
61 int utf8_strlen(const char *str, int bytelen)
62 {
63     int charlen = 0;
64     if (bytelen < 0) {
65         bytelen = strlen(str);
66     }
67     while (bytelen) {
68         int c;
69         int l = utf8_tounicode(str, &c);
70         charlen++;
71         str += l;
72         bytelen -= l;
73     }
74     return charlen;
75 }
76
77 int utf8_index(const char *str, int index)
78 {
79     const char *s = str;
80     while (index--) {
81         int c;
82         s += utf8_tounicode(s, &c);
83     }
84     return s - str;
85 }
86
87 int utf8_tounicode(const char *str, int *uc)
88 {
89     unsigned const char *s = (unsigned const char *)str;
90
91     if (s[0] < 0xc0) {
92         *uc = s[0];
93         return 1;
94     }
95     if (s[0] < 0xe0) {
96         if ((s[1] & 0xc0) == 0x80) {
97             *uc = ((s[0] & ~0xc0) << 6) | (s[1] & ~0x80);
98             return 2;
99         }
100     }
101     else if (s[0] < 0xf0) {
102         if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80)) {
103             *uc = ((s[0] & ~0xe0) << 12) | ((s[1] & ~0x80) << 6) | (s[2] & ~0x80);
104             return 3;
105         }
106     }
107     else if (s[0] < 0xf8) {
108         if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80) && ((str[3] & 0xc0) == 0x80)) {
109             *uc = ((s[0] & ~0xf0) << 18) | ((s[1] & ~0x80) << 12) | ((s[2] & ~0x80) << 6) | (s[3] & ~0x80);
110             return 4;
111         }
112     }
113
114     /* Invalid sequence, so just return the byte */
115     *uc = *s;
116     return 1;
117 }
118
119 #endif