root/lib/strutil/strutil.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. str_test_not_convert
  2. _str_convert
  3. str_test_encoding_class
  4. str_choose_str_functions
  5. str_crt_conv_to
  6. str_crt_conv_from
  7. str_close_conv
  8. str_convert
  9. str_nconvert
  10. str_conv_gerror_message
  11. str_vfs_convert_from
  12. str_vfs_convert_to
  13. str_printf
  14. str_insert_replace_char
  15. str_translate_char
  16. str_detect_termencoding
  17. str_isutf8
  18. str_init_strings
  19. str_uninit_strings
  20. str_term_form
  21. str_fit_to_term
  22. str_term_trim
  23. str_term_substring
  24. str_get_next_char
  25. str_cget_next_char
  26. str_next_char
  27. str_cnext_char
  28. str_get_prev_char
  29. str_cget_prev_char
  30. str_prev_char
  31. str_cprev_char
  32. str_get_next_char_safe
  33. str_cget_next_char_safe
  34. str_next_char_safe
  35. str_cnext_char_safe
  36. str_get_prev_char_safe
  37. str_cget_prev_char_safe
  38. str_prev_char_safe
  39. str_cprev_char_safe
  40. str_next_noncomb_char
  41. str_cnext_noncomb_char
  42. str_prev_noncomb_char
  43. str_cprev_noncomb_char
  44. str_is_valid_char
  45. str_term_width1
  46. str_term_width2
  47. str_term_char_width
  48. str_offset_to_pos
  49. str_length
  50. str_length_char
  51. str_length2
  52. str_length_noncomb
  53. str_column_to_pos
  54. str_isspace
  55. str_ispunct
  56. str_isalnum
  57. str_isdigit
  58. str_toupper
  59. str_tolower
  60. str_isprint
  61. str_iscombiningmark
  62. str_trunc
  63. str_create_search_needle
  64. str_release_search_needle
  65. str_search_first
  66. str_search_last
  67. str_is_valid_string
  68. str_compare
  69. str_ncompare
  70. str_casecmp
  71. str_ncasecmp
  72. str_prefix
  73. str_caseprefix
  74. str_fix_string
  75. str_create_key
  76. str_create_key_for_filename
  77. str_key_collate
  78. str_release_key
  79. str_msg_term_size
  80. strrstr_skip_count
  81. parse_integer
  82. str_rstrip_eol

   1 /*
   2    Common strings utilities
   3 
   4    Copyright (C) 2007-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Rostislav Benes, 2007
   9 
  10    This file is part of the Midnight Commander.
  11 
  12    The Midnight Commander is free software: you can redistribute it
  13    and/or modify it under the terms of the GNU General Public License as
  14    published by the Free Software Foundation, either version 3 of the License,
  15    or (at your option) any later version.
  16 
  17    The Midnight Commander is distributed in the hope that it will be useful,
  18    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20    GNU General Public License for more details.
  21 
  22    You should have received a copy of the GNU General Public License
  23    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  24  */
  25 
  26 #include <config.h>
  27 
  28 #include <stdlib.h>
  29 #include <langinfo.h>
  30 #include <string.h>
  31 #include <errno.h>
  32 
  33 #include "lib/global.h"
  34 #include "lib/util.h"  // MC_PTR_FREE
  35 #include "lib/strutil.h"
  36 
  37 /*** global variables ****************************************************************************/
  38 
  39 GIConv str_cnv_to_term;
  40 GIConv str_cnv_from_term;
  41 GIConv str_cnv_not_convert = INVALID_CONV;
  42 
  43 /*** file scope macro definitions ****************************************************************/
  44 
  45 /*** file scope type declarations ****************************************************************/
  46 
  47 /*** forward declarations (file scope functions) *************************************************/
  48 
  49 /*** file scope variables ************************************************************************/
  50 
  51 /* names, that are used for utf-8 */
  52 static const char *const str_utf8_encodings[] = {
  53     "utf-8",
  54     "utf8",
  55     NULL,
  56 };
  57 
  58 /* standard 8bit encodings, no wide or multibytes characters */
  59 static const char *const str_8bit_encodings[] = {
  60     "cp-1251",
  61     "cp1251",
  62 
  63     // solaris
  64     "ansi-1251",
  65     "ansi1251",
  66 
  67     "cp-1250",
  68     "cp1250",
  69     "cp-866",
  70     "cp866",
  71 
  72     // glibc
  73     "ibm-866",
  74     "ibm866",
  75 
  76     "cp-850",
  77     "cp850",
  78     "cp-852",
  79     "cp852",
  80     "iso-8859",
  81     "iso8859",
  82     "koi8",
  83 
  84     NULL,
  85 };
  86 
  87 /* terminal encoding */
  88 static char *codeset = NULL;
  89 static char *term_encoding = NULL;
  90 /* function for encoding specific operations */
  91 static struct str_class used_class;
  92 
  93 /* --------------------------------------------------------------------------------------------- */
  94 /*** file scope functions ************************************************************************/
  95 /* --------------------------------------------------------------------------------------------- */
  96 
  97 /* if enc is same encoding like on terminal */
  98 static int
  99 str_test_not_convert (const char *enc)
     /* [previous][next][first][last][top][bottom][index][help]  */
 100 {
 101     return g_ascii_strcasecmp (enc, codeset) == 0;
 102 }
 103 
 104 /* --------------------------------------------------------------------------------------------- */
 105 
 106 static estr_t
 107 _str_convert (GIConv coder, const char *string, int size, GString *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 108 {
 109     estr_t state = ESTR_SUCCESS;
 110     gssize left;
 111     gsize bytes_read = 0;
 112     gsize bytes_written = 0;
 113 
 114     errno = 0;  // FIXME: is it really needed?
 115 
 116     if (coder == INVALID_CONV)
 117         return ESTR_FAILURE;
 118 
 119     if (string == NULL || buffer == NULL)
 120         return ESTR_FAILURE;
 121 
 122     /*
 123        if (! used_class.is_valid_string (string))
 124        {
 125        return ESTR_FAILURE;
 126        }
 127      */
 128     if (size < 0)
 129         size = strlen (string);
 130     else
 131     {
 132         left = strlen (string);
 133         if (left < size)
 134             size = left;
 135     }
 136 
 137     left = size;
 138     g_iconv (coder, NULL, NULL, NULL, NULL);
 139 
 140     while (left != 0)
 141     {
 142         gchar *tmp_buff;
 143         GError *mcerror = NULL;
 144 
 145         tmp_buff = g_convert_with_iconv ((const gchar *) string, left, coder, &bytes_read,
 146                                          &bytes_written, &mcerror);
 147         if (mcerror != NULL)
 148         {
 149             int code = mcerror->code;
 150 
 151             g_error_free (mcerror);
 152             mcerror = NULL;
 153 
 154             switch (code)
 155             {
 156             case G_CONVERT_ERROR_NO_CONVERSION:
 157                 // Conversion between the requested character sets is not supported.
 158                 g_free (tmp_buff);
 159                 mc_g_string_append_c_len (buffer, '?', strlen (string));
 160                 return ESTR_FAILURE;
 161 
 162             case G_CONVERT_ERROR_ILLEGAL_SEQUENCE:
 163                 // Invalid byte sequence in conversion input.
 164                 if ((tmp_buff == NULL) && (bytes_read != 0))
 165                     // recode valid byte sequence
 166                     tmp_buff = g_convert_with_iconv ((const gchar *) string, bytes_read, coder,
 167                                                      NULL, NULL, NULL);
 168 
 169                 if (tmp_buff != NULL)
 170                 {
 171                     g_string_append (buffer, tmp_buff);
 172                     g_free (tmp_buff);
 173                 }
 174 
 175                 if ((int) bytes_read >= left)
 176                     return ESTR_PROBLEM;
 177 
 178                 string += bytes_read + 1;
 179                 size -= (bytes_read + 1);
 180                 left -= (bytes_read + 1);
 181                 g_string_append_c (buffer, *(string - 1));
 182                 state = ESTR_PROBLEM;
 183                 break;
 184 
 185             case G_CONVERT_ERROR_PARTIAL_INPUT:
 186                 // Partial character sequence at end of input.
 187                 g_string_append (buffer, tmp_buff);
 188                 g_free (tmp_buff);
 189                 if ((int) bytes_read < left)
 190                     mc_g_string_append_c_len (buffer, '?', left - bytes_read);
 191                 return ESTR_PROBLEM;
 192 
 193             case G_CONVERT_ERROR_BAD_URI:            // Don't know how handle this error :(
 194             case G_CONVERT_ERROR_NOT_ABSOLUTE_PATH:  // Don't know how handle this error :(
 195             case G_CONVERT_ERROR_FAILED:             // Conversion failed for some reason.
 196             default:
 197                 g_free (tmp_buff);
 198                 return ESTR_FAILURE;
 199             }
 200         }
 201         else if (tmp_buff == NULL)
 202         {
 203             g_string_append (buffer, string);
 204             return ESTR_PROBLEM;
 205         }
 206         else if (*tmp_buff == '\0')
 207         {
 208             g_free (tmp_buff);
 209             g_string_append (buffer, string);
 210             return state;
 211         }
 212         else
 213         {
 214             g_string_append (buffer, tmp_buff);
 215             g_free (tmp_buff);
 216             string += bytes_read;
 217             left -= bytes_read;
 218         }
 219     }
 220 
 221     return state;
 222 }
 223 
 224 /* --------------------------------------------------------------------------------------------- */
 225 
 226 static int
 227 str_test_encoding_class (const char *encoding, const char *const *table)
     /* [previous][next][first][last][top][bottom][index][help]  */
 228 {
 229     int result = 0;
 230 
 231     if (encoding != NULL)
 232     {
 233         int t;
 234 
 235         for (t = 0; table[t] != NULL; t++)
 236             if (g_ascii_strncasecmp (encoding, table[t], strlen (table[t])) == 0)
 237                 result++;
 238     }
 239 
 240     return result;
 241 }
 242 
 243 /* --------------------------------------------------------------------------------------------- */
 244 
 245 static void
 246 str_choose_str_functions (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 247 {
 248     if (str_test_encoding_class (codeset, str_utf8_encodings))
 249         used_class = str_utf8_init ();
 250     else if (str_test_encoding_class (codeset, str_8bit_encodings))
 251         used_class = str_8bit_init ();
 252     else
 253         used_class = str_ascii_init ();
 254 }
 255 
 256 /* --------------------------------------------------------------------------------------------- */
 257 /*** public functions ****************************************************************************/
 258 /* --------------------------------------------------------------------------------------------- */
 259 
 260 GIConv
 261 str_crt_conv_to (const char *to_enc)
     /* [previous][next][first][last][top][bottom][index][help]  */
 262 {
 263     return (!str_test_not_convert (to_enc)) ? g_iconv_open (to_enc, codeset) : str_cnv_not_convert;
 264 }
 265 
 266 /* --------------------------------------------------------------------------------------------- */
 267 
 268 GIConv
 269 str_crt_conv_from (const char *from_enc)
     /* [previous][next][first][last][top][bottom][index][help]  */
 270 {
 271     return (!str_test_not_convert (from_enc)) ? g_iconv_open (codeset, from_enc)
 272                                               : str_cnv_not_convert;
 273 }
 274 
 275 /* --------------------------------------------------------------------------------------------- */
 276 
 277 void
 278 str_close_conv (GIConv conv)
     /* [previous][next][first][last][top][bottom][index][help]  */
 279 {
 280     if (conv != INVALID_CONV && conv != str_cnv_not_convert)
 281         g_iconv_close (conv);
 282 }
 283 
 284 /* --------------------------------------------------------------------------------------------- */
 285 
 286 estr_t
 287 str_convert (GIConv coder, const char *string, GString *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 288 {
 289     return _str_convert (coder, string, -1, buffer);
 290 }
 291 
 292 /* --------------------------------------------------------------------------------------------- */
 293 
 294 estr_t
 295 str_nconvert (GIConv coder, const char *string, int size, GString *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 296 {
 297     return _str_convert (coder, string, size, buffer);
 298 }
 299 
 300 /* --------------------------------------------------------------------------------------------- */
 301 
 302 gchar *
 303 str_conv_gerror_message (GError *mcerror, const char *def_msg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 304 {
 305     return used_class.conv_gerror_message (mcerror, def_msg);
 306 }
 307 
 308 /* --------------------------------------------------------------------------------------------- */
 309 
 310 estr_t
 311 str_vfs_convert_from (GIConv coder, const char *string, GString *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 312 {
 313     estr_t result = ESTR_SUCCESS;
 314 
 315     if (coder == str_cnv_not_convert)
 316         g_string_append (buffer, string != NULL ? string : "");
 317     else
 318         result = _str_convert (coder, string, -1, buffer);
 319 
 320     return result;
 321 }
 322 
 323 /* --------------------------------------------------------------------------------------------- */
 324 
 325 estr_t
 326 str_vfs_convert_to (GIConv coder, const char *string, int size, GString *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 327 {
 328     return used_class.vfs_convert_to (coder, string, size, buffer);
 329 }
 330 
 331 /* --------------------------------------------------------------------------------------------- */
 332 
 333 void
 334 str_printf (GString *buffer, const char *format, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 335 {
 336     va_list ap;
 337     va_start (ap, format);
 338 
 339     g_string_append_vprintf (buffer, format, ap);
 340     va_end (ap);
 341 }
 342 
 343 /* --------------------------------------------------------------------------------------------- */
 344 
 345 void
 346 str_insert_replace_char (GString *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 347 {
 348     used_class.insert_replace_char (buffer);
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */
 352 
 353 estr_t
 354 str_translate_char (GIConv conv, const char *keys, size_t ch_size, char *output, size_t out_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 355 {
 356     size_t left;
 357     size_t cnv;
 358 
 359     g_iconv (conv, NULL, NULL, NULL, NULL);
 360 
 361     left = (ch_size == (size_t) (-1)) ? strlen (keys) : ch_size;
 362 
 363     cnv = g_iconv (conv, (gchar **) &keys, &left, &output, &out_size);
 364     if (cnv == (size_t) (-1))
 365         return (errno == EINVAL) ? ESTR_PROBLEM : ESTR_FAILURE;
 366 
 367     output[0] = '\0';
 368     return ESTR_SUCCESS;
 369 }
 370 
 371 /* --------------------------------------------------------------------------------------------- */
 372 
 373 const char *
 374 str_detect_termencoding (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 375 {
 376     if (term_encoding == NULL)
 377     {
 378         /* On Linux, nl_langinfo (CODESET) returns upper case UTF-8 whether the LANG is set
 379            to utf-8 or UTF-8.
 380            On Mac OS X, it returns the same case as the LANG input.
 381            So let transform result of nl_langinfo (CODESET) to upper case  unconditionally. */
 382         term_encoding = g_ascii_strup (nl_langinfo (CODESET), -1);
 383     }
 384 
 385     return term_encoding;
 386 }
 387 
 388 /* --------------------------------------------------------------------------------------------- */
 389 
 390 gboolean
 391 str_isutf8 (const char *codeset_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 392 {
 393     return (str_test_encoding_class (codeset_name, str_utf8_encodings) != 0);
 394 }
 395 
 396 /* --------------------------------------------------------------------------------------------- */
 397 
 398 void
 399 str_init_strings (const char *termenc)
     /* [previous][next][first][last][top][bottom][index][help]  */
 400 {
 401     codeset = termenc != NULL ? g_ascii_strup (termenc, -1) : g_strdup (str_detect_termencoding ());
 402 
 403     str_cnv_not_convert = g_iconv_open (codeset, codeset);
 404     if (str_cnv_not_convert == INVALID_CONV)
 405     {
 406         if (termenc != NULL)
 407         {
 408             g_free (codeset);
 409             codeset = g_strdup (str_detect_termencoding ());
 410             str_cnv_not_convert = g_iconv_open (codeset, codeset);
 411         }
 412 
 413         if (str_cnv_not_convert == INVALID_CONV)
 414         {
 415             g_free (codeset);
 416             codeset = g_strdup (DEFAULT_CHARSET);
 417             str_cnv_not_convert = g_iconv_open (codeset, codeset);
 418         }
 419     }
 420 
 421     str_cnv_to_term = str_cnv_not_convert;
 422     str_cnv_from_term = str_cnv_not_convert;
 423 
 424     str_choose_str_functions ();
 425 }
 426 
 427 /* --------------------------------------------------------------------------------------------- */
 428 
 429 void
 430 str_uninit_strings (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 431 {
 432     if (str_cnv_not_convert != INVALID_CONV)
 433         g_iconv_close (str_cnv_not_convert);
 434     // NULL-ize pointers to avoid double free in unit tests
 435     MC_PTR_FREE (term_encoding);
 436     MC_PTR_FREE (codeset);
 437 }
 438 
 439 /* --------------------------------------------------------------------------------------------- */
 440 
 441 const char *
 442 str_term_form (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 443 {
 444     return used_class.term_form (text);
 445 }
 446 
 447 /* --------------------------------------------------------------------------------------------- */
 448 
 449 const char *
 450 str_fit_to_term (const char *text, int width, align_crt_t just_mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 451 {
 452     return used_class.fit_to_term (text, width, just_mode);
 453 }
 454 
 455 /* --------------------------------------------------------------------------------------------- */
 456 
 457 const char *
 458 str_term_trim (const char *text, int width)
     /* [previous][next][first][last][top][bottom][index][help]  */
 459 {
 460     return used_class.term_trim (text, width);
 461 }
 462 
 463 /* --------------------------------------------------------------------------------------------- */
 464 
 465 const char *
 466 str_term_substring (const char *text, int start, int width)
     /* [previous][next][first][last][top][bottom][index][help]  */
 467 {
 468     return used_class.term_substring (text, start, width);
 469 }
 470 
 471 /* --------------------------------------------------------------------------------------------- */
 472 
 473 char *
 474 str_get_next_char (char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 475 {
 476 
 477     used_class.cnext_char ((const char **) &text);
 478     return text;
 479 }
 480 
 481 /* --------------------------------------------------------------------------------------------- */
 482 
 483 const char *
 484 str_cget_next_char (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 485 {
 486     used_class.cnext_char (&text);
 487     return text;
 488 }
 489 
 490 /* --------------------------------------------------------------------------------------------- */
 491 
 492 void
 493 str_next_char (char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 494 {
 495     used_class.cnext_char ((const char **) text);
 496 }
 497 
 498 /* --------------------------------------------------------------------------------------------- */
 499 
 500 void
 501 str_cnext_char (const char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 502 {
 503     used_class.cnext_char (text);
 504 }
 505 
 506 /* --------------------------------------------------------------------------------------------- */
 507 
 508 char *
 509 str_get_prev_char (char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 510 {
 511     used_class.cprev_char ((const char **) &text);
 512     return text;
 513 }
 514 
 515 /* --------------------------------------------------------------------------------------------- */
 516 
 517 const char *
 518 str_cget_prev_char (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 519 {
 520     used_class.cprev_char (&text);
 521     return text;
 522 }
 523 
 524 /* --------------------------------------------------------------------------------------------- */
 525 
 526 void
 527 str_prev_char (char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 528 {
 529     used_class.cprev_char ((const char **) text);
 530 }
 531 
 532 /* --------------------------------------------------------------------------------------------- */
 533 
 534 void
 535 str_cprev_char (const char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 536 {
 537     used_class.cprev_char (text);
 538 }
 539 
 540 /* --------------------------------------------------------------------------------------------- */
 541 
 542 char *
 543 str_get_next_char_safe (char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 544 {
 545     used_class.cnext_char_safe ((const char **) &text);
 546     return text;
 547 }
 548 
 549 /* --------------------------------------------------------------------------------------------- */
 550 
 551 const char *
 552 str_cget_next_char_safe (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 553 {
 554     used_class.cnext_char_safe (&text);
 555     return text;
 556 }
 557 
 558 /* --------------------------------------------------------------------------------------------- */
 559 
 560 void
 561 str_next_char_safe (char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 562 {
 563     used_class.cnext_char_safe ((const char **) text);
 564 }
 565 
 566 /* --------------------------------------------------------------------------------------------- */
 567 
 568 void
 569 str_cnext_char_safe (const char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 570 {
 571     used_class.cnext_char_safe (text);
 572 }
 573 
 574 /* --------------------------------------------------------------------------------------------- */
 575 
 576 char *
 577 str_get_prev_char_safe (char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 578 {
 579     used_class.cprev_char_safe ((const char **) &text);
 580     return text;
 581 }
 582 
 583 /* --------------------------------------------------------------------------------------------- */
 584 
 585 const char *
 586 str_cget_prev_char_safe (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 587 {
 588     used_class.cprev_char_safe (&text);
 589     return text;
 590 }
 591 
 592 /* --------------------------------------------------------------------------------------------- */
 593 
 594 void
 595 str_prev_char_safe (char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 596 {
 597     used_class.cprev_char_safe ((const char **) text);
 598 }
 599 
 600 /* --------------------------------------------------------------------------------------------- */
 601 
 602 void
 603 str_cprev_char_safe (const char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 604 {
 605     used_class.cprev_char_safe (text);
 606 }
 607 
 608 /* --------------------------------------------------------------------------------------------- */
 609 
 610 int
 611 str_next_noncomb_char (char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 612 {
 613     return used_class.cnext_noncomb_char ((const char **) text);
 614 }
 615 
 616 /* --------------------------------------------------------------------------------------------- */
 617 
 618 int
 619 str_cnext_noncomb_char (const char **text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 620 {
 621     return used_class.cnext_noncomb_char (text);
 622 }
 623 
 624 /* --------------------------------------------------------------------------------------------- */
 625 
 626 int
 627 str_prev_noncomb_char (char **text, const char *begin)
     /* [previous][next][first][last][top][bottom][index][help]  */
 628 {
 629     return used_class.cprev_noncomb_char ((const char **) text, begin);
 630 }
 631 
 632 /* --------------------------------------------------------------------------------------------- */
 633 
 634 int
 635 str_cprev_noncomb_char (const char **text, const char *begin)
     /* [previous][next][first][last][top][bottom][index][help]  */
 636 {
 637     return used_class.cprev_noncomb_char (text, begin);
 638 }
 639 
 640 /* --------------------------------------------------------------------------------------------- */
 641 
 642 int
 643 str_is_valid_char (const char *ch, size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 644 {
 645     return used_class.is_valid_char (ch, size);
 646 }
 647 
 648 /* --------------------------------------------------------------------------------------------- */
 649 
 650 int
 651 str_term_width1 (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 652 {
 653     return used_class.term_width1 (text);
 654 }
 655 
 656 /* --------------------------------------------------------------------------------------------- */
 657 
 658 int
 659 str_term_width2 (const char *text, size_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
 660 {
 661     return used_class.term_width2 (text, length);
 662 }
 663 
 664 /* --------------------------------------------------------------------------------------------- */
 665 
 666 int
 667 str_term_char_width (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 668 {
 669     return used_class.term_char_width (text);
 670 }
 671 
 672 /* --------------------------------------------------------------------------------------------- */
 673 
 674 int
 675 str_offset_to_pos (const char *text, size_t length)
     /* [previous][next][first][last][top][bottom][index][help]  */
 676 {
 677     return used_class.offset_to_pos (text, length);
 678 }
 679 
 680 /* --------------------------------------------------------------------------------------------- */
 681 
 682 int
 683 str_length (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 684 {
 685     return used_class.length (text);
 686 }
 687 
 688 /* --------------------------------------------------------------------------------------------- */
 689 
 690 int
 691 str_length_char (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 692 {
 693     return str_cget_next_char_safe (text) - text;
 694 }
 695 
 696 /* --------------------------------------------------------------------------------------------- */
 697 
 698 int
 699 str_length2 (const char *text, int size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 700 {
 701     return used_class.length2 (text, size);
 702 }
 703 
 704 /* --------------------------------------------------------------------------------------------- */
 705 
 706 int
 707 str_length_noncomb (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 708 {
 709     return used_class.length_noncomb (text);
 710 }
 711 
 712 /* --------------------------------------------------------------------------------------------- */
 713 
 714 int
 715 str_column_to_pos (const char *text, size_t pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 716 {
 717     return used_class.column_to_pos (text, pos);
 718 }
 719 
 720 /* --------------------------------------------------------------------------------------------- */
 721 
 722 gboolean
 723 str_isspace (const char *ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 724 {
 725     return used_class.char_isspace (ch);
 726 }
 727 
 728 /* --------------------------------------------------------------------------------------------- */
 729 
 730 gboolean
 731 str_ispunct (const char *ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 732 {
 733     return used_class.char_ispunct (ch);
 734 }
 735 
 736 /* --------------------------------------------------------------------------------------------- */
 737 
 738 gboolean
 739 str_isalnum (const char *ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 740 {
 741     return used_class.char_isalnum (ch);
 742 }
 743 
 744 /* --------------------------------------------------------------------------------------------- */
 745 
 746 gboolean
 747 str_isdigit (const char *ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 748 {
 749     return used_class.char_isdigit (ch);
 750 }
 751 
 752 /* --------------------------------------------------------------------------------------------- */
 753 
 754 gboolean
 755 str_toupper (const char *ch, char **out, size_t *remain)
     /* [previous][next][first][last][top][bottom][index][help]  */
 756 {
 757     return used_class.char_toupper (ch, out, remain);
 758 }
 759 
 760 /* --------------------------------------------------------------------------------------------- */
 761 
 762 gboolean
 763 str_tolower (const char *ch, char **out, size_t *remain)
     /* [previous][next][first][last][top][bottom][index][help]  */
 764 {
 765     return used_class.char_tolower (ch, out, remain);
 766 }
 767 
 768 /* --------------------------------------------------------------------------------------------- */
 769 
 770 gboolean
 771 str_isprint (const char *ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 772 {
 773     return used_class.char_isprint (ch);
 774 }
 775 
 776 /* --------------------------------------------------------------------------------------------- */
 777 
 778 gboolean
 779 str_iscombiningmark (const char *ch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 780 {
 781     return used_class.char_iscombiningmark (ch);
 782 }
 783 
 784 /* --------------------------------------------------------------------------------------------- */
 785 
 786 const char *
 787 str_trunc (const char *text, int width)
     /* [previous][next][first][last][top][bottom][index][help]  */
 788 {
 789     return used_class.trunc (text, width);
 790 }
 791 
 792 /* --------------------------------------------------------------------------------------------- */
 793 
 794 char *
 795 str_create_search_needle (const char *needle, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 796 {
 797     return used_class.create_search_needle (needle, case_sen);
 798 }
 799 
 800 /* --------------------------------------------------------------------------------------------- */
 801 
 802 void
 803 str_release_search_needle (char *needle, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 804 {
 805     used_class.release_search_needle (needle, case_sen);
 806 }
 807 
 808 /* --------------------------------------------------------------------------------------------- */
 809 
 810 const char *
 811 str_search_first (const char *text, const char *search, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 812 {
 813     return used_class.search_first (text, search, case_sen);
 814 }
 815 
 816 /* --------------------------------------------------------------------------------------------- */
 817 
 818 const char *
 819 str_search_last (const char *text, const char *search, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 820 {
 821     return used_class.search_last (text, search, case_sen);
 822 }
 823 
 824 /* --------------------------------------------------------------------------------------------- */
 825 
 826 gboolean
 827 str_is_valid_string (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 828 {
 829     return used_class.is_valid_string (text);
 830 }
 831 
 832 /* --------------------------------------------------------------------------------------------- */
 833 
 834 int
 835 str_compare (const char *t1, const char *t2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 836 {
 837     return used_class.compare (t1, t2);
 838 }
 839 
 840 /* --------------------------------------------------------------------------------------------- */
 841 
 842 int
 843 str_ncompare (const char *t1, const char *t2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 844 {
 845     return used_class.ncompare (t1, t2);
 846 }
 847 
 848 /* --------------------------------------------------------------------------------------------- */
 849 
 850 int
 851 str_casecmp (const char *t1, const char *t2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 852 {
 853     return used_class.casecmp (t1, t2);
 854 }
 855 
 856 /* --------------------------------------------------------------------------------------------- */
 857 
 858 int
 859 str_ncasecmp (const char *t1, const char *t2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 860 {
 861     return used_class.ncasecmp (t1, t2);
 862 }
 863 
 864 /* --------------------------------------------------------------------------------------------- */
 865 
 866 int
 867 str_prefix (const char *text, const char *prefix)
     /* [previous][next][first][last][top][bottom][index][help]  */
 868 {
 869     return used_class.prefix (text, prefix);
 870 }
 871 
 872 /* --------------------------------------------------------------------------------------------- */
 873 
 874 int
 875 str_caseprefix (const char *text, const char *prefix)
     /* [previous][next][first][last][top][bottom][index][help]  */
 876 {
 877     return used_class.caseprefix (text, prefix);
 878 }
 879 
 880 /* --------------------------------------------------------------------------------------------- */
 881 
 882 void
 883 str_fix_string (char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 884 {
 885     used_class.fix_string (text);
 886 }
 887 
 888 /* --------------------------------------------------------------------------------------------- */
 889 
 890 char *
 891 str_create_key (const char *text, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 892 {
 893     return used_class.create_key (text, case_sen);
 894 }
 895 
 896 /* --------------------------------------------------------------------------------------------- */
 897 
 898 char *
 899 str_create_key_for_filename (const char *text, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 900 {
 901     return used_class.create_key_for_filename (text, case_sen);
 902 }
 903 
 904 /* --------------------------------------------------------------------------------------------- */
 905 
 906 int
 907 str_key_collate (const char *t1, const char *t2, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 908 {
 909     return used_class.key_collate (t1, t2, case_sen);
 910 }
 911 
 912 /* --------------------------------------------------------------------------------------------- */
 913 
 914 void
 915 str_release_key (char *key, gboolean case_sen)
     /* [previous][next][first][last][top][bottom][index][help]  */
 916 {
 917     used_class.release_key (key, case_sen);
 918 }
 919 
 920 /* --------------------------------------------------------------------------------------------- */
 921 
 922 void
 923 str_msg_term_size (const char *text, int *lines, int *columns)
     /* [previous][next][first][last][top][bottom][index][help]  */
 924 {
 925     char *p, *tmp;
 926     char *q;
 927     char c = '\0';
 928 
 929     *lines = 1;
 930     *columns = 0;
 931 
 932     tmp = g_strdup (text);
 933     p = tmp;
 934 
 935     while (TRUE)
 936     {
 937         int width;
 938 
 939         q = strchr (p, '\n');
 940         if (q != NULL)
 941         {
 942             c = q[0];
 943             q[0] = '\0';
 944         }
 945 
 946         width = str_term_width1 (p);
 947         if (width > *columns)
 948             *columns = width;
 949 
 950         if (q == NULL)
 951             break;
 952 
 953         q[0] = c;
 954         p = q + 1;
 955         (*lines)++;
 956     }
 957 
 958     g_free (tmp);
 959 }
 960 
 961 /* --------------------------------------------------------------------------------------------- */
 962 
 963 char *
 964 strrstr_skip_count (const char *haystack, const char *needle, size_t skip_count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 965 {
 966     char *semi;
 967     ssize_t len;
 968 
 969     len = strlen (haystack);
 970 
 971     do
 972     {
 973         semi = g_strrstr_len (haystack, len, needle);
 974         if (semi == NULL)
 975             return NULL;
 976         len = semi - haystack - 1;
 977     }
 978     while (skip_count-- != 0);
 979 
 980     return semi;
 981 }
 982 
 983 /* --------------------------------------------------------------------------------------------- */
 984 /* Interpret string as a non-negative decimal integer, optionally multiplied by various values.
 985  *
 986  * @param str input value
 987  * @param invalid set to TRUE if "str" does not represent a number in this format
 988  *
 989  * @return non-negative integer representation of "str", 0 in case of error.
 990  */
 991 
 992 uintmax_t
 993 parse_integer (const char *str, gboolean *invalid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 994 {
 995     uintmax_t n;
 996     char *suffix;
 997     strtol_error_t e;
 998 
 999     e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0");
1000     if (e == LONGINT_INVALID_SUFFIX_CHAR && *suffix == 'x')
1001     {
1002         uintmax_t multiplier;
1003 
1004         multiplier = parse_integer (suffix + 1, invalid);
1005         if (multiplier != 0 && n * multiplier / multiplier != n)
1006         {
1007             *invalid = TRUE;
1008             return 0;
1009         }
1010 
1011         n *= multiplier;
1012     }
1013     else if (e != LONGINT_OK)
1014     {
1015         *invalid = TRUE;
1016         n = 0;
1017     }
1018 
1019     return n;
1020 }
1021 
1022 /* --------------------------------------------------------------------------------------------- */
1023 
1024 /**
1025  * Strips single right (trailing) EOL (\\r, \\n, or \\r\\n), NULL-safe
1026  */
1027 void
1028 str_rstrip_eol (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
1029 {
1030     if (s == NULL || *s == '\0')
1031         return;
1032 
1033     const size_t len = strlen (s);
1034     if (len >= 2 && s[len - 2] == '\r' && s[len - 1] == '\n')  // removes \r\n
1035         s[len - 2] = '\0';
1036     else if (len >= 1 && (s[len - 1] == '\n' || s[len - 1] == '\r'))  // removes \n or \r
1037         s[len - 1] = '\0';
1038 }
1039 
1040 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */