OSDN Git Service

Merge pull request #1829 from sikabane-works/feature/refactor-rbe_type
[hengbandforosx/hengbandosx.git] / src / util / string-processor.cpp
1 #include "util/string-processor.h"
2 #include "util/int-char-converter.h"
3
4 /*!
5  * 10進数から16進数への変換テーブル /
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
8  */
9 const char hexsym[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
10
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]; /*!< マクロの内容 */
17
18 /*
19  * Convert a decimal to a single digit octal number
20  */
21 static char octify(uint i)
22 {
23     return (hexsym[i % 8]);
24 }
25
26 /*
27  * Convert a decimal to a single digit hex number
28  */
29 static char hexify(uint i)
30 {
31     return (hexsym[i % 16]);
32 }
33
34 /*
35  * Convert a octal-digit into a decimal
36  */
37 static int deoct(char c)
38 {
39     if (isdigit(c))
40         return (D2I(c));
41     return 0;
42 }
43
44 /*
45  * Convert a hexidecimal-digit into a decimal
46  */
47 static int dehex(char c)
48 {
49     if (isdigit(c))
50         return (D2I(c));
51     if (islower(c))
52         return (A2I(c) + 10);
53     if (isupper(c))
54         return (A2I(tolower(c)) + 10);
55     return 0;
56 }
57
58 static char force_upper(char a)
59 {
60     return (islower(a)) ? toupper(a) : a;
61 }
62
63 static int angband_stricmp(concptr a, concptr b)
64 {
65     for (concptr s1 = a, s2 = b; true; s1++, s2++) {
66         char z1 = force_upper(*s1);
67         char z2 = force_upper(*s2);
68         if (z1 < z2)
69             return -1;
70         if (z1 > z2)
71             return 1;
72         if (!z1)
73             return 0;
74     }
75 }
76
77 static int angband_strnicmp(concptr a, concptr b, int n)
78 {
79     for (concptr s1 = a, s2 = b; n > 0; s1++, s2++, n--) {
80         char z1 = force_upper(*s1);
81         char z2 = force_upper(*s2);
82         if (z1 < z2)
83             return -1;
84         if (z1 > z2)
85             return 1;
86         if (!z1)
87             return 0;
88     }
89
90     return 0;
91 }
92
93 static void trigger_text_to_ascii(char **bufptr, concptr *strptr)
94 {
95     char *s = *bufptr;
96     concptr str = *strptr;
97     bool mod_status[MAX_MACRO_MOD];
98
99     int i, len = 0;
100     int shiftstatus = 0;
101     concptr key_code;
102
103     if (macro_template == nullptr)
104         return;
105
106     for (i = 0; macro_modifier_chr[i]; i++)
107         mod_status[i] = false;
108     str++;
109
110     /* Examine modifier keys */
111     while (true) {
112         for (i = 0; macro_modifier_chr[i]; i++) {
113             len = strlen(macro_modifier_name[i]);
114
115             if (!angband_strnicmp(str, macro_modifier_name[i], len))
116                 break;
117         }
118
119         if (!macro_modifier_chr[i])
120             break;
121         str += len;
122         mod_status[i] = true;
123         if ('S' == macro_modifier_chr[i])
124             shiftstatus = 1;
125     }
126
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]) {
130             break;
131         }
132     }
133
134     if (i == max_macrotrigger) {
135         str = angband_strchr(str, ']');
136         if (str) {
137             *s++ = (char)31;
138             *s++ = '\r';
139             *bufptr = s;
140             *strptr = str; /* where **strptr == ']' */
141         }
142
143         return;
144     }
145
146     key_code = macro_trigger_keycode[shiftstatus][i];
147     str += len;
148
149     *s++ = (char)31;
150     for (i = 0; macro_template[i]; i++) {
151         char ch = macro_template[i];
152         switch (ch) {
153         case '&':
154             for (int j = 0; macro_modifier_chr[j]; j++) {
155                 if (mod_status[j])
156                     *s++ = macro_modifier_chr[j];
157             }
158
159             break;
160         case '#':
161             strcpy(s, key_code);
162             s += strlen(key_code);
163             break;
164         default:
165             *s++ = ch;
166             break;
167         }
168     }
169
170     *s++ = '\r';
171
172     *bufptr = s;
173     *strptr = str; /* where **strptr == ']' */
174     return;
175 }
176
177 /*
178  * Hack -- convert a printable string into real ascii
179  *
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.
183  */
184 void text_to_ascii(char *buf, std::string_view sv)
185 {
186     char *s = buf;
187     auto str = sv.data();
188     while (*str) {
189         if (*str == '\\') {
190             str++;
191             if (!(*str))
192                 break;
193
194             if (*str == '[') {
195                 trigger_text_to_ascii(&s, &str);
196             } else {
197                 if (*str == 'x') {
198                     *s = 16 * (char)dehex(*++str);
199                     *s++ += (char)dehex(*++str);
200                 } else if (*str == '\\') {
201                     *s++ = '\\';
202                 } else if (*str == '^') {
203                     *s++ = '^';
204                 } else if (*str == 's') {
205                     *s++ = ' ';
206                 } else if (*str == 'e') {
207                     *s++ = ESCAPE;
208                 } else if (*str == 'b') {
209                     *s++ = '\b';
210                 } else if (*str == 'n') {
211                     *s++ = '\n';
212                 } else if (*str == 'r') {
213                     *s++ = '\r';
214                 } else if (*str == 't') {
215                     *s++ = '\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);
228                 }
229             }
230
231             str++;
232         } else if (*str == '^') {
233             str++;
234             *s++ = (*str++ & 037);
235         } else {
236             *s++ = *str++;
237         }
238     }
239
240     *s = '\0';
241 }
242
243 static bool trigger_ascii_to_text(char **bufptr, concptr *strptr)
244 {
245     char *s = *bufptr;
246     concptr str = *strptr;
247     char key_code[100];
248     int i;
249     if (macro_template == nullptr)
250         return false;
251
252     *s++ = '\\';
253     *s++ = '[';
254
255     concptr tmp;
256     for (i = 0; macro_template[i]; i++) {
257         char ch = macro_template[i];
258
259         switch (ch) {
260         case '&':
261             while ((tmp = angband_strchr(macro_modifier_chr, *str)) != 0) {
262                 int j = (int)(tmp - macro_modifier_chr);
263                 tmp = macro_modifier_name[j];
264                 while (*tmp)
265                     *s++ = *tmp++;
266                 str++;
267             }
268
269             break;
270         case '#': {
271             int j;
272             for (j = 0; *str && *str != '\r'; j++)
273                 key_code[j] = *str++;
274             key_code[j] = '\0';
275             break;
276         }
277         default:
278             if (ch != *str)
279                 return false;
280             str++;
281         }
282     }
283
284     if (*str++ != '\r')
285         return false;
286
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]))
289             break;
290     }
291
292     if (i == max_macrotrigger)
293         return false;
294
295     tmp = macro_trigger_name[i];
296     while (*tmp)
297         *s++ = *tmp++;
298
299     *s++ = ']';
300
301     *bufptr = s;
302     *strptr = str;
303     return true;
304 }
305
306 /*
307  * Hack -- convert a string into a printable form
308  */
309 void ascii_to_text(char *buf, std::string_view sv)
310 {
311     char *s = buf;
312     auto str = sv.data();
313     while (*str) {
314         byte i = (byte)(*str++);
315         if (i == 31) {
316             if (!trigger_ascii_to_text(&s, &str)) {
317                 *s++ = '^';
318                 *s++ = '_';
319             }
320         } else {
321             if (i == ESCAPE) {
322                 *s++ = '\\';
323                 *s++ = 'e';
324             } else if (i == ' ') {
325                 *s++ = '\\';
326                 *s++ = 's';
327             } else if (i == '\b') {
328                 *s++ = '\\';
329                 *s++ = 'b';
330             } else if (i == '\t') {
331                 *s++ = '\\';
332                 *s++ = 't';
333             } else if (i == '\n') {
334                 *s++ = '\\';
335                 *s++ = 'n';
336             } else if (i == '\r') {
337                 *s++ = '\\';
338                 *s++ = 'r';
339             } else if (i == '^') {
340                 *s++ = '\\';
341                 *s++ = '^';
342             } else if (i == '\\') {
343                 *s++ = '\\';
344                 *s++ = '\\';
345             } else if (i < 32) {
346                 *s++ = '^';
347                 *s++ = i + 64;
348             } else if (i < 127) {
349                 *s++ = i;
350             } else if (i < 64) {
351                 *s++ = '\\';
352                 *s++ = '0';
353                 *s++ = octify(i / 8);
354                 *s++ = octify(i % 8);
355             } else {
356                 *s++ = '\\';
357                 *s++ = 'x';
358                 *s++ = hexify(i / 16);
359                 *s++ = hexify(i % 16);
360             }
361         }
362     }
363
364     *s = '\0';
365 }
366
367 /*
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
370  * not overlap.
371  *
372  * angband_strcpy() returns strlen(src).  This makes checking for truncation
373  * easy.  Example: if (angband_strcpy(buf, src, sizeof(buf)) >= sizeof(buf)) ...;
374  *
375  * This function should be equivalent to the strlcpy() function in BSD.
376  */
377 size_t angband_strcpy(char *buf, concptr src, size_t bufsize)
378 {
379 #ifdef JP
380     char *d = buf;
381     concptr s = src;
382     size_t len = 0;
383
384     if (bufsize > 0) {
385         /* reserve for NUL termination */
386         bufsize--;
387
388         /* Copy as many bytes as will fit */
389         while (*s && (len < bufsize)) {
390             if (iskanji(*s)) {
391                 if (len + 1 >= bufsize || !*(s + 1))
392                     break;
393                 *d++ = *s++;
394                 *d++ = *s++;
395                 len += 2;
396             } else {
397                 *d++ = *s++;
398                 len++;
399             }
400         }
401         *d = '\0';
402     }
403
404     while (*s++)
405         len++;
406     return len;
407
408 #else
409     size_t len = strlen(src);
410     size_t ret = len;
411     if (bufsize == 0)
412         return ret;
413
414     if (len >= bufsize)
415         len = bufsize - 1;
416
417     (void)memcpy(buf, src, len);
418     buf[len] = '\0';
419     return ret;
420 #endif
421 }
422
423 /*
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.
427  *
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)) ...;
431  *
432  * This function should be equivalent to the strlcat() function in BSD.
433  */
434 size_t angband_strcat(char *buf, concptr src, size_t bufsize)
435 {
436     size_t dlen = strlen(buf);
437     if (dlen < bufsize - 1) {
438         return (dlen + angband_strcpy(buf + dlen, src, bufsize - dlen));
439     } else {
440         return (dlen + strlen(src));
441     }
442 }
443
444 /*
445  * A copy of ANSI strstr()
446  *
447  * angband_strstr() can handle Kanji strings correctly.
448  */
449 char *angband_strstr(concptr haystack, concptr needle)
450 {
451     int l1 = strlen(haystack);
452     int l2 = strlen(needle);
453
454     if (l1 >= l2) {
455         for (int i = 0; i <= l1 - l2; i++) {
456             if (!strncmp(haystack + i, needle, l2))
457                 return (char *)haystack + i;
458
459 #ifdef JP
460             if (iskanji(*(haystack + i)))
461                 i++;
462 #endif
463         }
464     }
465
466     return nullptr;
467 }
468
469 /*
470  * A copy of ANSI strchr()
471  *
472  * angband_strchr() can handle Kanji strings correctly.
473  */
474 char *angband_strchr(concptr ptr, char ch)
475 {
476     for (; *ptr != '\0'; ptr++) {
477         if (*ptr == ch)
478             return (char *)ptr;
479
480 #ifdef JP
481         if (iskanji(*ptr))
482             ptr++;
483 #endif
484     }
485
486     return nullptr;
487 }
488
489 /*!
490  * @brief 左側の空白を除去
491  * @param p char型のポインタ
492  * @return 除去後のポインタ
493  */
494 char *ltrim(char *p)
495 {
496     while (p[0] == ' ')
497         p++;
498     return p;
499 }
500
501 /*!
502  * @brief 右側の空白を除去
503  * @param p char型のポインタ
504  * @return 除去後のポインタ
505  */
506 char *rtrim(char *p)
507 {
508     int i = strlen(p) - 1;
509     while (p[i] == ' ')
510         p[i--] = '\0';
511     return p;
512 }
513
514 /*!
515  * @brief 文字列の後方から一致するかどうか比較する
516  * @param s1 比較元文字列ポインタ
517  * @param s2 比較先文字列ポインタ
518  * @param len 比較する長さ
519  * @return 等しい場合は0、p1が大きい場合は-1、p2が大きい場合は1
520  * @details
521  * strncmpの後方から比較する版
522  */
523 int strrncmp(const char *s1, const char *s2, int len)
524 {
525     int i;
526     int l1 = strlen(s1);
527     int l2 = strlen(s2);
528
529     for (i = 1; i <= len; i++) {
530         int p1 = l1 - i;
531         int p2 = l2 - i;
532
533         if (l1 != l2) {
534             if (p1 < 0)
535                 return (-1);
536             if (p2 < 0)
537                 return (1);
538         } else {
539             if (p1 < 0)
540                 return (0);
541         }
542
543         if (s1[p1] < s2[p2])
544             return (-1);
545         if (s1[p1] > s2[p2])
546             return (-1);
547     }
548
549     return (0);
550 }
551
552 /**
553  * @brief 文字列の両端の空白を削除する
554  *
555  * 文字列 str の両端にある空白(スペースおよびタブ)を削除し、
556  * 削除した文字列を std::string 型のオブジェクトとして返す。
557  * 文字列全体が空白の場合は空文字列を返す。
558  *
559  * @param str 操作の対象とする文字列
560  * @return std::string strの両端の空白を削除した文字列
561  */
562 std::string str_trim(std::string_view str)
563 {
564     const auto start_pos = str.find_first_not_of(" \t");
565     const auto end_pos = str.find_last_not_of(" \t");
566
567     if (start_pos == std::string_view::npos || end_pos == std::string_view::npos) {
568         return std::string();
569     }
570
571     return std::string(str.substr(start_pos, end_pos - start_pos + 1));
572 }
573
574 /**
575  * @brief 文字列の右端の空白を削除する
576  *
577  * 文字列 str の右端にある空白(スペースおよびタブ)を削除し、
578  * 削除した文字列を std::string 型のオブジェクトとして返す。
579  * 文字列全体が空白の場合は空文字列を返す。
580  *
581  * @param str 操作の対象とする文字列
582  * @return std::string strの右端の空白を削除した文字列
583  */
584 std::string str_rtrim(std::string_view str)
585 {
586     const auto end_pos = str.find_last_not_of(" \t");
587
588     if (end_pos == std::string_view::npos) {
589         return std::string();
590     }
591
592     return std::string(str.substr(0, end_pos + 1));
593 }
594
595 /**
596  * @brief 文字列の左端の空白を削除する
597  *
598  * 文字列 str の左端にある空白(スペースおよびタブ)を削除し、
599  * 削除した文字列を std::string 型のオブジェクトとして返す。
600  * 文字列全体が空白の場合は空文字列を返す。
601  *
602  * @param str 操作の対象とする文字列
603  * @return std::string strの左端の空白を削除した文字列
604  */
605 std::string str_ltrim(std::string_view str)
606 {
607     const auto start_pos = str.find_first_not_of(" \t");
608
609     if (start_pos == std::string_view::npos) {
610         return std::string();
611     }
612
613     return std::string(str.substr(start_pos));
614 }
615
616 /**
617  * @brief 文字列を指定した文字で分割する
618  *
619  * 文字列 str を delim で指定した文字で分割し、分割した文字列を要素とする配列を
620  * std::vector<std::string> 型のオブジェクトとして返す。
621  *
622  * @param str 操作の対象とする文字列
623  * @param delim 文字列を分割する文字
624  * @param trim trueの場合、分割した文字列の両端の空白を削除する
625  * @param num 1以上の場合、事前にvectorサイズを予約しておく(速度向上)
626  * @return std::vector<std::string> 分割した文字列を要素とする配列
627  */
628 std::vector<std::string> str_split(std::string_view str, char delim, bool trim, int num)
629 {
630     std::vector<std::string> result;
631     if (num > 0)
632         result.reserve(num);
633
634     auto make_str = [trim](std::string_view sv) { return trim ? str_trim(sv) : std::string(sv); };
635
636     while (true) {
637         bool found = false;
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);
642                 found = true;
643                 break;
644             }
645 #ifdef JP
646             if (iskanji(str[i]))
647                 ++i;
648 #endif
649         }
650         if (!found) {
651             result.push_back(make_str(str));
652             return result;
653         }
654     }
655 }
656
657 /**
658  * @brief 文字列から指定した文字を取り除く
659  *
660  * 文字列 str から文字列 erase_chars に含まれる文字をすべて削除し、
661  * 削除した文字列を std::string 型のオブジェクトとして返す。
662  *
663  * @param str 操作の対象とする文字列
664  * @param erase_chars 削除する文字を指定する文字列
665  * @return std::string 指定した文字をすべて削除した文字列
666  */
667 std::string str_erase(std::string str, std::string_view erase_chars)
668 {
669     for (auto it = str.begin(); it != str.end();) {
670         if (erase_chars.find(*it) != std::string_view::npos) {
671             it = str.erase(it);
672             continue;
673         }
674 #ifdef JP
675         if (iskanji(*it))
676             ++it;
677 #endif
678         ++it;
679     }
680
681     return str;
682 }