blob: bb97b3e00d22995e2b12b37b69d93d8d2a0db343 [file] [log] [blame]
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -07001/* $OpenBSD: citrus_utf8.c,v 1.6 2012/12/05 23:19:59 deraadt Exp $ */
2
3/*-
4 * Copyright (c) 2002-2004 Tim J. Robbins
Elliott Hughes29c7f0b2012-10-22 17:05:27 -07005 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070010 * 1. Redistributions of source code must retain the above copyright
Elliott Hughes29c7f0b2012-10-22 17:05:27 -070011 * notice, this list of conditions and the following disclaimer.
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070012 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
Elliott Hughes29c7f0b2012-10-22 17:05:27 -070015 *
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070016 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
Elliott Hughes29c7f0b2012-10-22 17:05:27 -070026 * SUCH DAMAGE.
27 */
28
Elliott Hughes29c7f0b2012-10-22 17:05:27 -070029#include <errno.h>
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070030#include <sys/param.h>
Dan Albert7a7f9952014-06-02 11:33:04 -070031#include <string.h>
Elliott Hughes29c7f0b2012-10-22 17:05:27 -070032#include <wchar.h>
Dan Albert7a7f9952014-06-02 11:33:04 -070033#include <uchar.h>
34
35#include "private/bionic_mbstate.h"
Elliott Hughes29c7f0b2012-10-22 17:05:27 -070036
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070037//
Calin Juravle15a63102014-05-08 14:38:35 +010038// This file is basically OpenBSD's citrus_utf8.c but rewritten to not require a
39// 12-byte mbstate_t so we're backwards-compatible with our LP32 ABI where
40// mbstate_t was only 4 bytes.
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070041//
Calin Juravle15a63102014-05-08 14:38:35 +010042// The state is the UTF-8 sequence. We only support <= 4-bytes sequences so LP32
43// mbstate_t already has enough space (out of the 4 available bytes we only
44// need 3 since we should never need to store the entire sequence in the
45// intermediary state).
46//
47// The C standard leaves the conversion state undefined after a bad conversion.
48// To avoid unexpected failures due to the possible use of the internal private
49// state we always reset the conversion state when encountering illegal
50// sequences.
51//
52// We also implement the POSIX interface directly rather than being accessed via
53// function pointers.
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070054//
Elliott Hughes29c7f0b2012-10-22 17:05:27 -070055
Calin Juravle15a63102014-05-08 14:38:35 +010056int mbsinit(const mbstate_t* ps) {
Elliott Hughes2c966392021-11-16 11:03:19 -080057 return ps == nullptr || mbstate_is_initial(ps);
Calin Juravle15a63102014-05-08 14:38:35 +010058}
59
60size_t mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t* ps) {
61 static mbstate_t __private_state;
Yi Kong32bc0fc2018-08-02 17:31:13 -070062 mbstate_t* state = (ps == nullptr) ? &__private_state : ps;
Calin Juravle15a63102014-05-08 14:38:35 +010063
Elliott Hughes53de8742016-10-24 14:50:31 -070064 // Our wchar_t is UTF-32.
Dan Albert7a7f9952014-06-02 11:33:04 -070065 return mbrtoc32(reinterpret_cast<char32_t*>(pwc), s, n, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070066}
67
68size_t mbsnrtowcs(wchar_t* dst, const char** src, size_t nmc, size_t len, mbstate_t* ps) {
Calin Juravle15a63102014-05-08 14:38:35 +010069 static mbstate_t __private_state;
Yi Kong32bc0fc2018-08-02 17:31:13 -070070 mbstate_t* state = (ps == nullptr) ? &__private_state : ps;
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070071 size_t i, o, r;
72
Elliott Hughes89e29ee2016-09-29 17:21:43 -070073 // The fast paths in the loops below are not safe if an ASCII
74 // character appears as anything but the first byte of a
75 // multibyte sequence. Check now to avoid doing it in the loops.
76 if (nmc > 0 && mbstate_bytes_so_far(state) > 0 && static_cast<uint8_t>((*src)[0]) < 0x80) {
Elliott Hughes697f42a2017-07-14 17:00:05 -070077 return mbstate_reset_and_return_illegal(EILSEQ, state);
Elliott Hughes89e29ee2016-09-29 17:21:43 -070078 }
79
80 // Measure only?
Yi Kong32bc0fc2018-08-02 17:31:13 -070081 if (dst == nullptr) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070082 for (i = o = 0; i < nmc; i += r, o++) {
83 if (static_cast<uint8_t>((*src)[i]) < 0x80) {
84 // Fast path for plain ASCII characters.
85 if ((*src)[i] == '\0') {
Elliott Hughes697f42a2017-07-14 17:00:05 -070086 return mbstate_reset_and_return(o, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070087 }
88 r = 1;
89 } else {
Yi Kong32bc0fc2018-08-02 17:31:13 -070090 r = mbrtowc(nullptr, *src + i, nmc - i, state);
Dan Albert7a7f9952014-06-02 11:33:04 -070091 if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
Elliott Hughes697f42a2017-07-14 17:00:05 -070092 return mbstate_reset_and_return_illegal(EILSEQ, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070093 }
Dan Albert7a7f9952014-06-02 11:33:04 -070094 if (r == __MB_ERR_INCOMPLETE_SEQUENCE) {
Elliott Hughes697f42a2017-07-14 17:00:05 -070095 return mbstate_reset_and_return_illegal(EILSEQ, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070096 }
97 if (r == 0) {
Elliott Hughes697f42a2017-07-14 17:00:05 -070098 return mbstate_reset_and_return(o, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -070099 }
Elliott Hughes1b836ee2014-04-18 13:32:33 -0700100 }
Elliott Hughes05493712014-04-17 17:30:03 -0700101 }
Elliott Hughes697f42a2017-07-14 17:00:05 -0700102 return mbstate_reset_and_return(o, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700103 }
104
Elliott Hughes89e29ee2016-09-29 17:21:43 -0700105 // Actually convert, updating `dst` and `src`.
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700106 for (i = o = 0; i < nmc && o < len; i += r, o++) {
107 if (static_cast<uint8_t>((*src)[i]) < 0x80) {
108 // Fast path for plain ASCII characters.
109 dst[o] = (*src)[i];
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700110 r = 1;
Dan Albert6b55ba52014-07-20 11:51:26 -0700111 if ((*src)[i] == '\0') {
Dan Albertb6cc8e02014-07-31 11:31:03 -0700112 *src = nullptr;
Elliott Hughes697f42a2017-07-14 17:00:05 -0700113 return mbstate_reset_and_return(o, state);
Dan Albert6b55ba52014-07-20 11:51:26 -0700114 }
Elliott Hughes05493712014-04-17 17:30:03 -0700115 } else {
Calin Juravle15a63102014-05-08 14:38:35 +0100116 r = mbrtowc(dst + o, *src + i, nmc - i, state);
Dan Albert7a7f9952014-06-02 11:33:04 -0700117 if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700118 *src += i;
Elliott Hughes697f42a2017-07-14 17:00:05 -0700119 return mbstate_reset_and_return_illegal(EILSEQ, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700120 }
Dan Albert7a7f9952014-06-02 11:33:04 -0700121 if (r == __MB_ERR_INCOMPLETE_SEQUENCE) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700122 *src += nmc;
Elliott Hughes697f42a2017-07-14 17:00:05 -0700123 return mbstate_reset_and_return_illegal(EILSEQ, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700124 }
125 if (r == 0) {
Yi Kong32bc0fc2018-08-02 17:31:13 -0700126 *src = nullptr;
Elliott Hughes697f42a2017-07-14 17:00:05 -0700127 return mbstate_reset_and_return(o, state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700128 }
Elliott Hughes05493712014-04-17 17:30:03 -0700129 }
Elliott Hughes29c7f0b2012-10-22 17:05:27 -0700130 }
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700131 *src += i;
Elliott Hughes697f42a2017-07-14 17:00:05 -0700132 return mbstate_reset_and_return(o, state);
Elliott Hughes29c7f0b2012-10-22 17:05:27 -0700133}
134
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700135size_t mbsrtowcs(wchar_t* dst, const char** src, size_t len, mbstate_t* ps) {
136 return mbsnrtowcs(dst, src, SIZE_MAX, len, ps);
Elliott Hughes29c7f0b2012-10-22 17:05:27 -0700137}
Elliott Hughes2bd43162023-06-15 13:17:08 -0700138__strong_alias(mbsrtowcs_l, mbsrtowcs);
Elliott Hughes29c7f0b2012-10-22 17:05:27 -0700139
Calin Juravle15a63102014-05-08 14:38:35 +0100140size_t wcrtomb(char* s, wchar_t wc, mbstate_t* ps) {
141 static mbstate_t __private_state;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700142 mbstate_t* state = (ps == nullptr) ? &__private_state : ps;
Calin Juravle15a63102014-05-08 14:38:35 +0100143
Elliott Hughes89e29ee2016-09-29 17:21:43 -0700144 // Our wchar_t is UTF-32.
Dan Albert7a7f9952014-06-02 11:33:04 -0700145 return c32rtomb(s, static_cast<char32_t>(wc), state);
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700146}
147
148size_t wcsnrtombs(char* dst, const wchar_t** src, size_t nwc, size_t len, mbstate_t* ps) {
Calin Juravle15a63102014-05-08 14:38:35 +0100149 static mbstate_t __private_state;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700150 mbstate_t* state = (ps == nullptr) ? &__private_state : ps;
Calin Juravle15a63102014-05-08 14:38:35 +0100151
Elliott Hughes2c966392021-11-16 11:03:19 -0800152 if (!mbstate_is_initial(state)) {
Elliott Hughes697f42a2017-07-14 17:00:05 -0700153 return mbstate_reset_and_return_illegal(EILSEQ, state);
Calin Juravle15a63102014-05-08 14:38:35 +0100154 }
155
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700156 char buf[MB_LEN_MAX];
157 size_t i, o, r;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700158 if (dst == nullptr) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700159 for (i = o = 0; i < nwc; i++, o += r) {
160 wchar_t wc = (*src)[i];
Elliott Hughes0d0ccfe2014-05-01 19:03:18 -0700161 if (static_cast<uint32_t>(wc) < 0x80) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700162 // Fast path for plain ASCII characters.
163 if (wc == 0) {
164 return o;
165 }
166 r = 1;
167 } else {
Calin Juravle15a63102014-05-08 14:38:35 +0100168 r = wcrtomb(buf, wc, state);
Dan Albert7a7f9952014-06-02 11:33:04 -0700169 if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700170 return r;
171 }
172 }
173 }
174 return o;
175 }
176
177 for (i = o = 0; i < nwc && o < len; i++, o += r) {
178 wchar_t wc = (*src)[i];
Elliott Hughes0d0ccfe2014-05-01 19:03:18 -0700179 if (static_cast<uint32_t>(wc) < 0x80) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700180 // Fast path for plain ASCII characters.
181 dst[o] = wc;
182 if (wc == 0) {
Yi Kong32bc0fc2018-08-02 17:31:13 -0700183 *src = nullptr;
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700184 return o;
185 }
186 r = 1;
187 } else if (len - o >= sizeof(buf)) {
188 // Enough space to translate in-place.
Calin Juravle15a63102014-05-08 14:38:35 +0100189 r = wcrtomb(dst + o, wc, state);
Dan Albert7a7f9952014-06-02 11:33:04 -0700190 if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700191 *src += i;
192 return r;
193 }
194 } else {
195 // May not be enough space; use temp buffer.
Calin Juravle15a63102014-05-08 14:38:35 +0100196 r = wcrtomb(buf, wc, state);
Dan Albert7a7f9952014-06-02 11:33:04 -0700197 if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700198 *src += i;
199 return r;
200 }
201 if (r > len - o) {
202 break;
203 }
204 memcpy(dst + o, buf, r);
Elliott Hughes29c7f0b2012-10-22 17:05:27 -0700205 }
206 }
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700207 *src += i;
208 return o;
Elliott Hughes29c7f0b2012-10-22 17:05:27 -0700209}
210
Elliott Hughes5a0aa3d2014-04-30 22:03:12 -0700211size_t wcsrtombs(char* dst, const wchar_t** src, size_t len, mbstate_t* ps) {
212 return wcsnrtombs(dst, src, SIZE_MAX, len, ps);
Elliott Hughes29c7f0b2012-10-22 17:05:27 -0700213}
Elliott Hughes2bd43162023-06-15 13:17:08 -0700214__strong_alias(wcsrtombs_l, wcsrtombs);