1 #include "util/string-processor.h"
2 #include "util/int-char-converter.h"
6 * Global array for converting numbers to uppercase hecidecimal digit
7 * This array can also be used to convert a number to an octal digit
9 const char hexsym[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
11 int max_macrotrigger = 0; /*!< 現在登録中のマクロ(トリガー)の数 */
12 concptr macro_template = nullptr; /*!< Angband設定ファイルのT: タグ情報から読み込んだ長いTコードを処理するために利用する文字列ポインタ */
13 concptr macro_modifier_chr; /*!< &x# で指定されるマクロトリガーに関する情報を記録する文字列ポインタ */
14 concptr macro_modifier_name[MAX_MACRO_MOD]; /*!< マクロ上で取り扱う特殊キーを文字列上で表現するためのフォーマットを記録した文字列ポインタ配列 */
15 concptr macro_trigger_name[MAX_MACRO_TRIG]; /*!< マクロのトリガーコード */
16 concptr macro_trigger_keycode[2][MAX_MACRO_TRIG]; /*!< マクロの内容 */
19 * Convert a decimal to a single digit octal number
21 static char octify(uint i)
23 return (hexsym[i % 8]);
27 * Convert a decimal to a single digit hex number
29 static char hexify(uint i)
31 return (hexsym[i % 16]);
35 * Convert a octal-digit into a decimal
37 static int deoct(char c)
45 * Convert a hexidecimal-digit into a decimal
47 static int dehex(char c)
54 return (A2I(tolower(c)) + 10);
58 static char force_upper(char a)
60 return (islower(a)) ? toupper(a) : a;
63 static int angband_stricmp(concptr a, concptr b)
65 for (concptr s1 = a, s2 = b; true; s1++, s2++) {
66 char z1 = force_upper(*s1);
67 char z2 = force_upper(*s2);
77 static int angband_strnicmp(concptr a, concptr b, int n)
79 for (concptr s1 = a, s2 = b; n > 0; s1++, s2++, n--) {
80 char z1 = force_upper(*s1);
81 char z2 = force_upper(*s2);
93 static void trigger_text_to_ascii(char **bufptr, concptr *strptr)
96 concptr str = *strptr;
97 bool mod_status[MAX_MACRO_MOD];
103 if (macro_template == nullptr)
106 for (i = 0; macro_modifier_chr[i]; i++)
107 mod_status[i] = false;
110 /* Examine modifier keys */
112 for (i = 0; macro_modifier_chr[i]; i++) {
113 len = strlen(macro_modifier_name[i]);
115 if (!angband_strnicmp(str, macro_modifier_name[i], len))
119 if (!macro_modifier_chr[i])
122 mod_status[i] = true;
123 if ('S' == macro_modifier_chr[i])
127 for (i = 0; i < max_macrotrigger; i++) {
128 len = strlen(macro_trigger_name[i]);
129 if (!angband_strnicmp(str, macro_trigger_name[i], len) && ']' == str[len]) {
134 if (i == max_macrotrigger) {
135 str = angband_strchr(str, ']');
140 *strptr = str; /* where **strptr == ']' */
146 key_code = macro_trigger_keycode[shiftstatus][i];
150 for (i = 0; macro_template[i]; i++) {
151 char ch = macro_template[i];
154 for (int j = 0; macro_modifier_chr[j]; j++) {
156 *s++ = macro_modifier_chr[j];
162 s += strlen(key_code);
173 *strptr = str; /* where **strptr == ']' */
178 * Hack -- convert a printable string into real ascii
180 * I have no clue if this function correctly handles, for example,
181 * parsing "\xFF" into a (signed) char. Whoever thought of making
182 * the "sign" of a "char" undefined is a complete moron. Oh well.
184 void text_to_ascii(char *buf, std::string_view sv)
187 auto str = sv.data();
195 trigger_text_to_ascii(&s, &str);
198 *s = 16 * (char)dehex(*++str);
199 *s++ += (char)dehex(*++str);
200 } else if (*str == '\\') {
202 } else if (*str == '^') {
204 } else if (*str == 's') {
206 } else if (*str == 'e') {
208 } else if (*str == 'b') {
210 } else if (*str == 'n') {
212 } else if (*str == 'r') {
214 } else if (*str == 't') {
216 } else if (*str == '0') {
217 *s = 8 * (char)deoct(*++str);
218 *s++ += (char)deoct(*++str);
219 } else if (*str == '1') {
220 *s = 64 + 8 * (char)deoct(*++str);
221 *s++ += (char)deoct(*++str);
222 } else if (*str == '2') {
223 *s = 64 * 2 + 8 * (char)deoct(*++str);
224 *s++ += (char)deoct(*++str);
225 } else if (*str == '3') {
226 *s = 64 * 3 + 8 * (char)deoct(*++str);
227 *s++ += (char)deoct(*++str);
232 } else if (*str == '^') {
234 *s++ = (*str++ & 037);
243 static bool trigger_ascii_to_text(char **bufptr, concptr *strptr)
246 concptr str = *strptr;
249 if (macro_template == nullptr)
256 for (i = 0; macro_template[i]; i++) {
257 char ch = macro_template[i];
261 while ((tmp = angband_strchr(macro_modifier_chr, *str)) != 0) {
262 int j = (int)(tmp - macro_modifier_chr);
263 tmp = macro_modifier_name[j];
272 for (j = 0; *str && *str != '\r'; j++)
273 key_code[j] = *str++;
287 for (i = 0; i < max_macrotrigger; i++) {
288 if (!angband_stricmp(key_code, macro_trigger_keycode[0][i]) || !angband_stricmp(key_code, macro_trigger_keycode[1][i]))
292 if (i == max_macrotrigger)
295 tmp = macro_trigger_name[i];
307 * Hack -- convert a string into a printable form
309 void ascii_to_text(char *buf, std::string_view sv)
312 auto str = sv.data();
314 byte i = (byte)(*str++);
316 if (!trigger_ascii_to_text(&s, &str)) {
324 } else if (i == ' ') {
327 } else if (i == '\b') {
330 } else if (i == '\t') {
333 } else if (i == '\n') {
336 } else if (i == '\r') {
339 } else if (i == '^') {
342 } else if (i == '\\') {
348 } else if (i < 127) {
353 *s++ = octify(i / 8);
354 *s++ = octify(i % 8);
358 *s++ = hexify(i / 16);
359 *s++ = hexify(i % 16);
368 * The angband_strcpy() function copies up to 'bufsize'-1 characters from 'src'
369 * to 'buf' and NUL-terminates the result. The 'buf' and 'src' strings may
372 * angband_strcpy() returns strlen(src). This makes checking for truncation
373 * easy. Example: if (angband_strcpy(buf, src, sizeof(buf)) >= sizeof(buf)) ...;
375 * This function should be equivalent to the strlcpy() function in BSD.
377 size_t angband_strcpy(char *buf, concptr src, size_t bufsize)
385 /* reserve for NUL termination */
388 /* Copy as many bytes as will fit */
389 while (*s && (len < bufsize)) {
391 if (len + 1 >= bufsize || !*(s + 1))
409 size_t len = strlen(src);
417 (void)memcpy(buf, src, len);
424 * The angband_strcat() tries to append a string to an existing NUL-terminated string.
425 * It never writes more characters into the buffer than indicated by 'bufsize' and
426 * NUL-terminates the buffer. The 'buf' and 'src' strings may not overlap.
428 * angband_strcat() returns strlen(buf) + strlen(src). This makes checking for
429 * truncation easy. Example:
430 * if (angband_strcat(buf, src, sizeof(buf)) >= sizeof(buf)) ...;
432 * This function should be equivalent to the strlcat() function in BSD.
434 size_t angband_strcat(char *buf, concptr src, size_t bufsize)
436 size_t dlen = strlen(buf);
437 if (dlen < bufsize - 1) {
438 return (dlen + angband_strcpy(buf + dlen, src, bufsize - dlen));
440 return (dlen + strlen(src));
445 * A copy of ANSI strstr()
447 * angband_strstr() can handle Kanji strings correctly.
449 char *angband_strstr(concptr haystack, concptr needle)
451 int l1 = strlen(haystack);
452 int l2 = strlen(needle);
455 for (int i = 0; i <= l1 - l2; i++) {
456 if (!strncmp(haystack + i, needle, l2))
457 return (char *)haystack + i;
460 if (iskanji(*(haystack + i)))
470 * A copy of ANSI strchr()
472 * angband_strchr() can handle Kanji strings correctly.
474 char *angband_strchr(concptr ptr, char ch)
476 for (; *ptr != '\0'; ptr++) {
491 * @param p char型のポインタ
503 * @param p char型のポインタ
508 int i = strlen(p) - 1;
515 * @brief 文字列の後方から一致するかどうか比較する
516 * @param s1 比較元文字列ポインタ
517 * @param s2 比較先文字列ポインタ
519 * @return 等しい場合は0、p1が大きい場合は-1、p2が大きい場合は1
523 int strrncmp(const char *s1, const char *s2, int len)
529 for (i = 1; i <= len; i++) {
553 * @brief 文字列の両端の空白を削除する
555 * 文字列 str の両端にある空白(スペースおよびタブ)を削除し、
556 * 削除した文字列を std::string 型のオブジェクトとして返す。
557 * 文字列全体が空白の場合は空文字列を返す。
559 * @param str 操作の対象とする文字列
560 * @return std::string strの両端の空白を削除した文字列
562 std::string str_trim(std::string_view str)
564 const auto start_pos = str.find_first_not_of(" \t");
565 const auto end_pos = str.find_last_not_of(" \t");
567 if (start_pos == std::string_view::npos || end_pos == std::string_view::npos) {
568 return std::string();
571 return std::string(str.substr(start_pos, end_pos - start_pos + 1));
575 * @brief 文字列の右端の空白を削除する
577 * 文字列 str の右端にある空白(スペースおよびタブ)を削除し、
578 * 削除した文字列を std::string 型のオブジェクトとして返す。
579 * 文字列全体が空白の場合は空文字列を返す。
581 * @param str 操作の対象とする文字列
582 * @return std::string strの右端の空白を削除した文字列
584 std::string str_rtrim(std::string_view str)
586 const auto end_pos = str.find_last_not_of(" \t");
588 if (end_pos == std::string_view::npos) {
589 return std::string();
592 return std::string(str.substr(0, end_pos + 1));
596 * @brief 文字列の左端の空白を削除する
598 * 文字列 str の左端にある空白(スペースおよびタブ)を削除し、
599 * 削除した文字列を std::string 型のオブジェクトとして返す。
600 * 文字列全体が空白の場合は空文字列を返す。
602 * @param str 操作の対象とする文字列
603 * @return std::string strの左端の空白を削除した文字列
605 std::string str_ltrim(std::string_view str)
607 const auto start_pos = str.find_first_not_of(" \t");
609 if (start_pos == std::string_view::npos) {
610 return std::string();
613 return std::string(str.substr(start_pos));
617 * @brief 文字列を指定した文字で分割する
619 * 文字列 str を delim で指定した文字で分割し、分割した文字列を要素とする配列を
620 * std::vector<std::string> 型のオブジェクトとして返す。
622 * @param str 操作の対象とする文字列
623 * @param delim 文字列を分割する文字
624 * @param trim trueの場合、分割した文字列の両端の空白を削除する
625 * @param num 1以上の場合、事前にvectorサイズを予約しておく(速度向上)
626 * @return std::vector<std::string> 分割した文字列を要素とする配列
628 std::vector<std::string> str_split(std::string_view str, char delim, bool trim, int num)
630 std::vector<std::string> result;
634 auto make_str = [trim](std::string_view sv) { return trim ? str_trim(sv) : std::string(sv); };
638 for (size_t i = 0; i < str.size(); ++i) {
639 if (str[i] == delim) {
640 result.push_back(make_str(str.substr(0, i)));
641 str.remove_prefix(i + 1);
651 result.push_back(make_str(str));
658 * @brief 文字列から指定した文字を取り除く
660 * 文字列 str から文字列 erase_chars に含まれる文字をすべて削除し、
661 * 削除した文字列を std::string 型のオブジェクトとして返す。
663 * @param str 操作の対象とする文字列
664 * @param erase_chars 削除する文字を指定する文字列
665 * @return std::string 指定した文字をすべて削除した文字列
667 std::string str_erase(std::string str, std::string_view erase_chars)
669 for (auto it = str.begin(); it != str.end();) {
670 if (erase_chars.find(*it) != std::string_view::npos) {