1// LAF Base Library
2// Copyright (c) 2020-2022 Igara Studio S.A.
3// Copyright (c) 2001-2016 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "base/debug.h"
13#include "base/string.h"
14#include "base/utf8_decode.h"
15
16#include <cctype>
17#include <vector>
18
19#ifdef LAF_WINDOWS
20 #include <windows.h>
21#endif
22
23namespace base {
24
25std::string string_printf(const char* format, ...)
26{
27 std::va_list ap;
28 va_start(ap, format);
29 std::string result = string_vprintf(format, ap);
30 va_end(ap);
31 return result;
32}
33
34std::string string_vprintf(const char* format, va_list ap)
35{
36 std::vector<char> buf(1, 0);
37 std::va_list ap2;
38 va_copy(ap2, ap);
39 size_t required_size = std::vsnprintf(nullptr, 0, format, ap);
40 if (required_size) {
41 buf.resize(required_size+1);
42 std::vsnprintf(&buf[0], buf.size(), format, ap2);
43 }
44 va_end(ap2);
45 return std::string(&buf[0]);
46}
47
48std::string string_to_lower(const std::string& original)
49{
50 std::wstring result(from_utf8(original));
51 auto it(result.begin());
52 auto end(result.end());
53 while (it != end) {
54 *it = std::tolower(*it);
55 ++it;
56 }
57 return to_utf8(result);
58}
59
60std::string string_to_upper(const std::string& original)
61{
62 std::wstring result(from_utf8(original));
63 auto it(result.begin());
64 auto end(result.end());
65 while (it != end) {
66 *it = std::toupper(*it);
67 ++it;
68 }
69 return to_utf8(result);
70}
71
72#ifdef LAF_WINDOWS
73
74std::string to_utf8(const wchar_t* src, const int n)
75{
76 int required_size =
77 ::WideCharToMultiByte(CP_UTF8, 0,
78 src, (int)n,
79 NULL, 0, NULL, NULL);
80
81 if (required_size == 0)
82 return std::string();
83
84 std::vector<char> buf(++required_size);
85
86 ::WideCharToMultiByte(CP_UTF8, 0,
87 src, (int)n,
88 &buf[0], required_size,
89 NULL, NULL);
90
91 return std::string(&buf[0]);
92}
93
94std::wstring from_utf8(const std::string& src)
95{
96 int required_size =
97 MultiByteToWideChar(CP_UTF8, 0,
98 src.c_str(), (int)src.size(),
99 NULL, 0);
100
101 if (required_size == 0)
102 return std::wstring();
103
104 std::vector<wchar_t> buf(++required_size);
105
106 ::MultiByteToWideChar(CP_UTF8, 0,
107 src.c_str(), (int)src.size(),
108 &buf[0], required_size);
109
110 return std::wstring(&buf[0]);
111}
112
113#else
114
115// Based on Allegro Unicode code (allegro/src/unicode.c)
116static std::size_t insert_utf8_char(std::string* result, wchar_t chr)
117{
118 int size, bits, b, i;
119
120 if (chr < 128) {
121 if (result)
122 result->push_back(chr);
123 return 1;
124 }
125
126 bits = 7;
127 while (chr >= (1<<bits))
128 bits++;
129
130 size = 2;
131 b = 11;
132
133 while (b < bits) {
134 size++;
135 b += 5;
136 }
137
138 if (result) {
139 b -= (7-size);
140 int firstbyte = chr>>b;
141 for (i=0; i<size; i++)
142 firstbyte |= (0x80>>i);
143
144 result->push_back(firstbyte);
145
146 for (i=1; i<size; i++) {
147 b -= 6;
148 result->push_back(0x80 | ((chr>>b)&0x3F));
149 }
150 }
151
152 return size;
153}
154
155std::string to_utf8(const wchar_t* src, const int n)
156{
157 // Get required size to reserve a string so string::push_back()
158 // doesn't need to reallocate its data.
159 std::size_t required_size = 0;
160 auto p = src;
161 for (int i=0; i<n; ++i, ++p)
162 required_size += insert_utf8_char(nullptr, *p);
163 if (!required_size)
164 return "";
165
166 std::string result;
167 result.reserve(++required_size);
168 p = src;
169 for (int i=0; i<n; ++i, ++p)
170 insert_utf8_char(&result, *p);
171 return result;
172}
173
174std::wstring from_utf8(const std::string& src)
175{
176 int required_size = utf8_length(src);
177 std::vector<wchar_t> buf(++required_size);
178 std::vector<wchar_t>::iterator buf_it = buf.begin();
179#ifdef _DEBUG
180 std::vector<wchar_t>::iterator buf_end = buf.end();
181#endif
182 utf8_decode decode(src);
183
184 while (int chr = decode.next()) {
185 ASSERT(buf_it != buf_end);
186 *buf_it = chr;
187 ++buf_it;
188 }
189
190 return std::wstring(&buf[0]);
191}
192
193#endif
194
195int utf8_length(const std::string& utf8string)
196{
197 utf8_decode decode(utf8string);
198 int c = 0;
199
200 while (decode.next())
201 ++c;
202
203 return c;
204}
205
206int utf8_icmp(const std::string& a, const std::string& b, int n)
207{
208 utf8_decode a_decode(a);
209 utf8_decode b_decode(b);
210 int i = 0;
211
212 for (; (n == 0 || i < n)
213 && !a_decode.is_end()
214 && !b_decode.is_end(); ++i) {
215 int a_chr = a_decode.next();
216 if (!a_chr)
217 break;
218
219 int b_chr = b_decode.next();
220 if (!b_chr)
221 break;
222
223 a_chr = std::tolower(a_chr);
224 b_chr = std::tolower(b_chr);
225
226 if (a_chr < b_chr)
227 return -1;
228 else if (a_chr > b_chr)
229 return 1;
230 }
231
232 if (n > 0 && i == n)
233 return 0;
234 else if (a_decode.is_end() && b_decode.is_end())
235 return 0;
236 else if (a_decode.is_end())
237 return -1;
238 else
239 return 1;
240}
241
242} // namespace base
243