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)
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)
46 * Convert a hexidecimal-digit into a decimal
48 static int dehex(char c)
57 return A2I(tolower(c)) + 10;
62 static char force_upper(char a)
64 return islower(a) ? toupper(a) : a;
67 static int angband_stricmp(concptr a, concptr b)
69 for (concptr s1 = a, s2 = b; true; s1++, s2++) {
70 char z1 = force_upper(*s1);
71 char z2 = force_upper(*s2);
84 static int angband_strnicmp(concptr a, concptr b, int n)
86 for (concptr s1 = a, s2 = b; n > 0; s1++, s2++, n--) {
87 char z1 = force_upper(*s1);
88 char z2 = force_upper(*s2);
103 static void trigger_text_to_ascii(char **bufptr, concptr *strptr)
106 concptr str = *strptr;
107 bool mod_status[MAX_MACRO_MOD];
113 if (macro_template == nullptr) {
117 for (i = 0; macro_modifier_chr[i]; i++) {
118 mod_status[i] = false;
122 /* Examine modifier keys */
124 for (i = 0; macro_modifier_chr[i]; i++) {
125 len = strlen(macro_modifier_name[i]);
127 if (!angband_strnicmp(str, macro_modifier_name[i], len)) {
132 if (!macro_modifier_chr[i]) {
136 mod_status[i] = true;
137 if ('S' == macro_modifier_chr[i]) {
142 for (i = 0; i < max_macrotrigger; i++) {
143 len = strlen(macro_trigger_name[i]);
144 if (!angband_strnicmp(str, macro_trigger_name[i], len) && ']' == str[len]) {
149 if (i == max_macrotrigger) {
150 str = angband_strchr(str, ']');
155 *strptr = str; /* where **strptr == ']' */
161 key_code = macro_trigger_keycode[shiftstatus][i];
165 for (i = 0; macro_template[i]; i++) {
166 char ch = macro_template[i];
169 for (int j = 0; macro_modifier_chr[j]; j++) {
171 *s++ = macro_modifier_chr[j];
178 s += strlen(key_code);
189 *strptr = str; /* where **strptr == ']' */
194 * Hack -- convert a printable string into real ascii
196 * I have no clue if this function correctly handles, for example,
197 * parsing "\xFF" into a (signed) char. Whoever thought of making
198 * the "sign" of a "char" undefined is a complete moron. Oh well.
200 void text_to_ascii(char *buf, std::string_view sv, size_t bufsize)
203 auto buffer_end = s + bufsize;
204 auto str = sv.data();
205 constexpr auto step_size = 1;
206 while (*str && (s + step_size < buffer_end)) {
214 trigger_text_to_ascii(&s, &str);
217 *s = 16 * (char)dehex(*++str);
218 *s++ += (char)dehex(*++str);
219 } else if (*str == '\\') {
221 } else if (*str == '^') {
223 } else if (*str == 's') {
225 } else if (*str == 'e') {
227 } else if (*str == 'b') {
229 } else if (*str == 'n') {
231 } else if (*str == 'r') {
233 } else if (*str == 't') {
235 } else if (*str == '0') {
236 *s = 8 * (char)deoct(*++str);
237 *s++ += (char)deoct(*++str);
238 } else if (*str == '1') {
239 *s = 64 + 8 * (char)deoct(*++str);
240 *s++ += (char)deoct(*++str);
241 } else if (*str == '2') {
242 *s = 64 * 2 + 8 * (char)deoct(*++str);
243 *s++ += (char)deoct(*++str);
244 } else if (*str == '3') {
245 *s = 64 * 3 + 8 * (char)deoct(*++str);
246 *s++ += (char)deoct(*++str);
251 } else if (*str == '^') {
253 *s++ = (*str++ & 037);
262 static bool trigger_ascii_to_text(char **bufptr, concptr *strptr)
265 concptr str = *strptr;
268 if (macro_template == nullptr) {
276 for (i = 0; macro_template[i]; i++) {
277 char ch = macro_template[i];
281 while ((tmp = angband_strchr(macro_modifier_chr, *str)) != 0) {
282 int j = (int)(tmp - macro_modifier_chr);
283 tmp = macro_modifier_name[j];
293 for (j = 0; *str && *str != '\r'; j++) {
294 key_code[j] = *str++;
307 if (*str++ != '\r') {
311 for (i = 0; i < max_macrotrigger; i++) {
312 if (!angband_stricmp(key_code, macro_trigger_keycode[0][i]) || !angband_stricmp(key_code, macro_trigger_keycode[1][i])) {
317 if (i == max_macrotrigger) {
321 tmp = macro_trigger_name[i];
334 * Hack -- convert a string into a printable form
336 void ascii_to_text(char *buf, std::string_view sv, size_t bufsize)
339 auto buffer_end = s + bufsize;
340 auto str = sv.data();
341 constexpr auto step_size = 4;
342 while (*str && (s + step_size < buffer_end)) {
343 byte i = (byte)(*str++);
345 if (!trigger_ascii_to_text(&s, &str)) {
353 } else if (i == ' ') {
356 } else if (i == '\b') {
359 } else if (i == '\t') {
362 } else if (i == '\n') {
365 } else if (i == '\r') {
368 } else if (i == '^') {
371 } else if (i == '\\') {
377 } else if (i < 127) {
382 *s++ = octify(i / 8);
383 *s++ = octify(i % 8);
387 *s++ = hexify(i / 16);
388 *s++ = hexify(i % 16);
397 * The angband_strcpy() function copies up to 'bufsize'-1 characters from 'src'
398 * to 'buf' and NUL-terminates the result. The 'buf' and 'src' strings may
401 * angband_strcpy() returns strlen(src). This makes checking for truncation
402 * easy. Example: if (angband_strcpy(buf, src, sizeof(buf)) >= sizeof(buf)) ...;
404 * This function should be equivalent to the strlcpy() function in BSD.
406 size_t angband_strcpy(char *buf, std::string_view src, size_t bufsize)
410 const char *s = src.data();
414 /* reserve for NUL termination */
417 /* Copy as many bytes as will fit */
418 while (*s && (len < bufsize)) {
420 if (len + 1 >= bufsize || !*(s + 1)) {
440 auto len = src.length();
445 if (len >= bufsize) {
449 (void)src.copy(buf, len);
456 * The angband_strcat() tries to append a string to an existing NUL-terminated string.
457 * It never writes more characters into the buffer than indicated by 'bufsize' and
458 * NUL-terminates the buffer. The 'buf' and 'src' strings may not overlap.
460 * angband_strcat() returns strlen(buf) + strlen(src). This makes checking for
461 * truncation easy. Example:
462 * if (angband_strcat(buf, src, sizeof(buf)) >= sizeof(buf)) ...;
464 * This function should be equivalent to the strlcat() function in BSD.
466 size_t angband_strcat(char *buf, std::string_view src, size_t bufsize)
468 size_t dlen = strlen(buf);
469 if (dlen < bufsize - 1) {
470 return dlen + angband_strcpy(buf + dlen, src, bufsize - dlen);
472 return dlen + src.length();
477 * A copy of ANSI strstr()
479 * angband_strstr() can handle Kanji strings correctly.
481 char *angband_strstr(const char *haystack, std::string_view needle)
483 std::string_view haystack_view(haystack);
484 auto l1 = haystack_view.length();
485 auto l2 = needle.length();
490 for (size_t i = 0; i <= l1 - l2; i++) {
491 const auto part = haystack_view.substr(i);
492 if (part.starts_with(needle)) {
493 return const_cast<char *>(haystack) + i;
497 if (iskanji(*(haystack + i))) {
507 * A copy of ANSI strchr()
509 * angband_strchr() can handle Kanji strings correctly.
511 char *angband_strchr(concptr ptr, char ch)
513 for (; *ptr != '\0'; ptr++) {
530 * @param p char型のポインタ
535 while (p[0] == ' ') {
543 * @param p char型のポインタ
548 int i = strlen(p) - 1;
549 while (p[i] == ' ') {
556 * @brief 文字列の後方から一致するかどうか比較する
557 * @param s1 比較元文字列ポインタ
558 * @param s2 比較先文字列ポインタ
560 * @return 等しい場合は0、p1が大きい場合は-1、p2が大きい場合は1
564 int strrncmp(const char *s1, const char *s2, int len)
570 for (i = 1; i <= len; i++) {
587 if (s1[p1] < s2[p2]) {
590 if (s1[p1] > s2[p2]) {
599 * @brief マルチバイト文字のダメ文字('\')を考慮しつつ文字列比較を行う
601 * @param find 比較したい文字列
603 bool str_find(const std::string &src, std::string_view find)
605 return angband_strstr(src.data(), find) != nullptr;
609 * @brief 文字列の両端の空白を削除する
611 * 文字列 str の両端にある空白(スペースおよびタブ)を削除し、
612 * 削除した文字列を std::string 型のオブジェクトとして返す。
613 * 文字列全体が空白の場合は空文字列を返す。
615 * @param str 操作の対象とする文字列
616 * @return std::string strの両端の空白を削除した文字列
618 std::string str_trim(std::string_view str)
620 const auto start_pos = str.find_first_not_of(" \t");
621 const auto end_pos = str.find_last_not_of(" \t");
623 if (start_pos == std::string_view::npos || end_pos == std::string_view::npos) {
624 return std::string();
627 return std::string(str.substr(start_pos, end_pos - start_pos + 1));
631 * @brief 文字列の右端の空白を削除する
633 * 文字列 str の右端にある空白(スペースおよびタブ)を削除し、
634 * 削除した文字列を std::string 型のオブジェクトとして返す。
635 * 文字列全体が空白の場合は空文字列を返す。
637 * @param str 操作の対象とする文字列
638 * @return std::string strの右端の空白を削除した文字列
640 std::string str_rtrim(std::string_view str)
642 const auto end_pos = str.find_last_not_of(" \t");
644 if (end_pos == std::string_view::npos) {
645 return std::string();
648 return std::string(str.substr(0, end_pos + 1));
652 * @brief 文字列の左端の空白を削除する
654 * 文字列 str の左端にある空白(スペースおよびタブ)を削除し、
655 * 削除した文字列を std::string 型のオブジェクトとして返す。
656 * 文字列全体が空白の場合は空文字列を返す。
658 * @param str 操作の対象とする文字列
659 * @return std::string strの左端の空白を削除した文字列
661 std::string str_ltrim(std::string_view str)
663 const auto start_pos = str.find_first_not_of(" \t");
665 if (start_pos == std::string_view::npos) {
666 return std::string();
669 return std::string(str.substr(start_pos));
673 * @brief 文字列を指定した文字で分割する
675 * 文字列 str を delim で指定した文字で分割し、分割した文字列を要素とする配列を
676 * std::vector<std::string> 型のオブジェクトとして返す。
678 * @param str 操作の対象とする文字列
679 * @param delim 文字列を分割する文字
680 * @param trim trueの場合、分割した文字列の両端の空白を削除する
681 * @param num 1以上の場合、事前にvectorサイズを予約しておく(速度向上)
682 * @return std::vector<std::string> 分割した文字列を要素とする配列
684 std::vector<std::string> str_split(std::string_view str, char delim, bool trim, int num)
686 std::vector<std::string> result;
691 auto make_str = [trim](std::string_view sv) { return trim ? str_trim(sv) : std::string(sv); };
695 for (size_t i = 0; i < str.size(); ++i) {
696 if (str[i] == delim) {
697 result.push_back(make_str(str.substr(0, i)));
698 str.remove_prefix(i + 1);
703 if (iskanji(str[i])) {
709 result.push_back(make_str(str));
716 * @brief 文字列を指定した文字数で分割する
718 * 文字列 str を len で指定した文字数で分割し、分割した文字列を要素とする配列を
719 * std::vector<std::string> 型のオブジェクトとして返す。
720 * 全角文字は2文字分として扱い、全角文字の前半で分割されてしまわないように処理される。
722 * @param str 操作の対象とする文字列
724 * @return std::vector<std::string> 分割した文字列を要素とする配列
726 std::vector<std::string> str_separate(std::string_view str, size_t len)
728 std::vector<std::string> result;
730 while (!str.empty()) {
731 result.push_back(str_substr(str, 0, len));
732 str.remove_prefix(result.back().size());
739 * @brief 文字列から指定した文字を取り除く
741 * 文字列 str から文字列 erase_chars に含まれる文字をすべて削除し、
742 * 削除した文字列を std::string 型のオブジェクトとして返す。
744 * @param str 操作の対象とする文字列
745 * @param erase_chars 削除する文字を指定する文字列
746 * @return std::string 指定した文字をすべて削除した文字列
748 std::string str_erase(std::string str, std::string_view erase_chars)
750 for (auto it = str.begin(); it != str.end();) {
751 if (erase_chars.find(*it) != std::string_view::npos) {
766 static std::pair<size_t, size_t> adjust_substr_pos(std::string_view sv, size_t pos, size_t n)
768 const auto start = std::min(pos, sv.length());
769 const auto end = n == std::string_view::npos ? sv.length() : std::min(pos + n, sv.length());
773 while (seek_pos < start) {
774 if (iskanji(sv[seek_pos])) {
779 const auto mb_pos = seek_pos;
781 while (seek_pos < end) {
782 if (iskanji(sv[seek_pos])) {
783 if (seek_pos == end - 1) {
790 const auto mb_n = seek_pos - mb_pos;
792 return { mb_pos, mb_n };
794 return { start, end - start };
799 * @brief 2バイト文字を考慮して部分文字列を取得する
801 * 引数で与えられた文字列から pos バイト目から n バイトの部分文字列を取得する。
802 * 但し、以下の通り2バイト文字の途中で分断されないようにする。
803 * - 開始位置 pos バイト目が2バイト文字の後半バイトの場合は pos+1 バイト目を開始位置とする。
804 * - 終了位置 pos+n バイト目が2バイト文字の前半バイトの場合は pos+n-1 バイト目を終了位置とする。
807 * @param pos 部分文字列の開始位置
811 std::string str_substr(std::string_view sv, size_t pos, size_t n)
813 const auto [mb_pos, mb_n] = adjust_substr_pos(sv, pos, n);
814 return std::string(sv.substr(mb_pos, mb_n));
818 * @brief 2バイト文字を考慮して部分文字列を取得する
820 * 引数で与えられた文字列を pos バイト目から n バイトの部分文字列にして返す。
821 * 但し、以下の通り2バイト文字の途中で分断されないようにする。
822 * - 開始位置 pos バイト目が2バイト文字の後半バイトの場合は pos+1 バイト目を開始位置とする。
823 * - 終了位置 pos+n バイト目が2バイト文字の前半バイトの場合は pos+n-1 バイト目を終了位置とする。
826 * @param pos 部分文字列の開始位置
830 std::string str_substr(std::string &&str, size_t pos, size_t n)
832 const auto [mb_pos, mb_n] = adjust_substr_pos(str, pos, n);
833 str.erase(mb_pos + mb_n);
834 str.erase(0, mb_pos);
835 return std::move(str);
838 std::string str_substr(const char *str, size_t pos, size_t n)
840 return str_substr(std::string_view(str), pos, n);