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

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

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