strutil: character test functions return gboolean instead of int.
[midnight-commander.git] / lib / strutil / strutil8bit.c
blobb4b34af8753ab28cfbe7b2392afc60dc63542fd8
1 /*
2 8bit strings utilities
4 Copyright (C) 2007-2018
5 Free Software Foundation, Inc.
7 Written by:
8 Rostislav Benes, 2007
10 This file is part of the Midnight Commander.
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.
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.
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/>.
26 #include <config.h>
28 #include <ctype.h>
29 #include <stdlib.h>
31 #include "lib/global.h"
32 #include "lib/strutil.h"
34 /* Functions for singlebyte encodings, all characters have width 1
35 * using standard system functions.
36 * There are only small differences between functions in strutil8bit.c
37 * and strutilascii.c.
40 /*** global variables ****************************************************************************/
42 /*** file scope macro definitions ****************************************************************/
45 * Inlines to equalize 'char' signedness for single 'char' encodings.
46 * Instead of writing
47 * isspace ((unsigned char) c);
48 * you can write
49 * char_isspace (c);
51 #define DECLARE_CTYPE_WRAPPER(func_name) \
52 static inline int char_##func_name(char c) \
53 { \
54 return func_name((int)(unsigned char)c); \
57 /*** file scope type declarations ****************************************************************/
59 /*** file scope variables ************************************************************************/
61 static const char replch = '?';
63 /* --------------------------------------------------------------------------------------------- */
64 /*** file scope functions ************************************************************************/
65 /* --------------------------------------------------------------------------------------------- */
67 /* *INDENT-OFF* */
68 DECLARE_CTYPE_WRAPPER (isalnum)
69 DECLARE_CTYPE_WRAPPER (isdigit)
70 DECLARE_CTYPE_WRAPPER (isprint)
71 DECLARE_CTYPE_WRAPPER (ispunct)
72 DECLARE_CTYPE_WRAPPER (isspace)
73 DECLARE_CTYPE_WRAPPER (toupper)
74 DECLARE_CTYPE_WRAPPER (tolower)
75 /* *INDENT-ON* */
77 /* --------------------------------------------------------------------------------------------- */
79 static void
80 str_8bit_insert_replace_char (GString * buffer)
82 g_string_append_c (buffer, replch);
85 /* --------------------------------------------------------------------------------------------- */
87 static gboolean
88 str_8bit_is_valid_string (const char *text)
90 (void) text;
91 return TRUE;
94 /* --------------------------------------------------------------------------------------------- */
96 static int
97 str_8bit_is_valid_char (const char *ch, size_t size)
99 (void) ch;
100 (void) size;
101 return 1;
104 /* --------------------------------------------------------------------------------------------- */
106 static void
107 str_8bit_cnext_char (const char **text)
109 (*text)++;
112 /* --------------------------------------------------------------------------------------------- */
114 static void
115 str_8bit_cprev_char (const char **text)
117 (*text)--;
120 /* --------------------------------------------------------------------------------------------- */
122 static int
123 str_8bit_cnext_noncomb_char (const char **text)
125 if (*text[0] == '\0')
126 return 0;
128 (*text)++;
129 return 1;
132 /* --------------------------------------------------------------------------------------------- */
134 static int
135 str_8bit_cprev_noncomb_char (const char **text, const char *begin)
137 if ((*text) == begin)
138 return 0;
140 (*text)--;
141 return 1;
144 /* --------------------------------------------------------------------------------------------- */
146 static gboolean
147 str_8bit_isspace (const char *text)
149 return char_isspace (text[0]) != 0;
152 /* --------------------------------------------------------------------------------------------- */
154 static gboolean
155 str_8bit_ispunct (const char *text)
157 return char_ispunct (text[0]) != 0;
160 /* --------------------------------------------------------------------------------------------- */
162 static gboolean
163 str_8bit_isalnum (const char *text)
165 return char_isalnum (text[0]) != 0;
168 /* --------------------------------------------------------------------------------------------- */
170 static gboolean
171 str_8bit_isdigit (const char *text)
173 return char_isdigit (text[0]) != 0;
176 /* --------------------------------------------------------------------------------------------- */
178 static gboolean
179 str_8bit_isprint (const char *text)
181 return char_isprint (text[0]) != 0;
184 /* --------------------------------------------------------------------------------------------- */
186 static gboolean
187 str_8bit_iscombiningmark (const char *text)
189 (void) text;
190 return FALSE;
193 /* --------------------------------------------------------------------------------------------- */
195 static int
196 str_8bit_toupper (const char *text, char **out, size_t * remain)
198 if (*remain <= 1)
199 return FALSE;
201 (*out)[0] = char_toupper (text[0]);
202 (*out)++;
203 (*remain)--;
204 return TRUE;
207 /* --------------------------------------------------------------------------------------------- */
209 static gboolean
210 str_8bit_tolower (const char *text, char **out, size_t * remain)
212 if (*remain <= 1)
213 return FALSE;
215 (*out)[0] = char_tolower (text[0]);
216 (*out)++;
217 (*remain)--;
218 return TRUE;
221 /* --------------------------------------------------------------------------------------------- */
223 static int
224 str_8bit_length (const char *text)
226 return strlen (text);
229 /* --------------------------------------------------------------------------------------------- */
231 static int
232 str_8bit_length2 (const char *text, int size)
234 return (size >= 0) ? MIN (strlen (text), (gsize) size) : strlen (text);
237 /* --------------------------------------------------------------------------------------------- */
239 static gchar *
240 str_8bit_conv_gerror_message (GError * mcerror, const char *def_msg)
242 GIConv conv;
243 gchar *ret;
245 /* glib messages are in UTF-8 charset */
246 conv = str_crt_conv_from ("UTF-8");
248 if (conv == INVALID_CONV)
249 ret = g_strdup (def_msg != NULL ? def_msg : "");
250 else
252 GString *buf;
254 buf = g_string_new ("");
256 if (str_convert (conv, mcerror->message, buf) != ESTR_FAILURE)
257 ret = g_string_free (buf, FALSE);
258 else
260 ret = g_strdup (def_msg != NULL ? def_msg : "");
261 g_string_free (buf, TRUE);
264 str_close_conv (conv);
267 return ret;
270 /* --------------------------------------------------------------------------------------------- */
272 static estr_t
273 str_8bit_vfs_convert_to (GIConv coder, const char *string, int size, GString * buffer)
275 estr_t result = ESTR_SUCCESS;
277 if (coder == str_cnv_not_convert)
278 g_string_append_len (buffer, string, size);
279 else
280 result = str_nconvert (coder, string, size, buffer);
282 return result;
285 /* --------------------------------------------------------------------------------------------- */
287 static const char *
288 str_8bit_term_form (const char *text)
290 static char result[BUF_MEDIUM];
291 char *actual;
292 size_t remain;
293 size_t length;
294 size_t pos = 0;
296 actual = result;
297 remain = sizeof (result);
298 length = strlen (text);
300 for (; pos < length && remain > 1; pos++, actual++, remain--)
301 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
303 actual[0] = '\0';
304 return result;
307 /* --------------------------------------------------------------------------------------------- */
309 static const char *
310 str_8bit_fit_to_term (const char *text, int width, align_crt_t just_mode)
312 static char result[BUF_MEDIUM];
313 char *actual;
314 size_t remain;
315 int ident = 0;
316 size_t length;
317 size_t pos = 0;
319 length = strlen (text);
320 actual = result;
321 remain = sizeof (result);
323 if ((int) length <= width)
325 switch (HIDE_FIT (just_mode))
327 case J_CENTER_LEFT:
328 case J_CENTER:
329 ident = (width - length) / 2;
330 break;
331 case J_RIGHT:
332 ident = width - length;
333 break;
334 default:
335 break;
338 if ((int) remain <= ident)
339 goto finally;
340 memset (actual, ' ', ident);
341 actual += ident;
342 remain -= ident;
344 for (; pos < length && remain > 1; pos++, actual++, remain--)
345 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
347 if (width - length - ident > 0)
349 if (remain <= width - length - ident)
350 goto finally;
351 memset (actual, ' ', width - length - ident);
352 actual += width - length - ident;
355 else if (IS_FIT (just_mode))
357 for (; pos + 1 <= (gsize) width / 2 && remain > 1; actual++, pos++, remain--)
358 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
360 if (remain <= 1)
361 goto finally;
362 actual[0] = '~';
363 actual++;
364 remain--;
366 pos += length - width + 1;
367 for (; pos < length && remain > 1; pos++, actual++, remain--)
368 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
370 else
372 switch (HIDE_FIT (just_mode))
374 case J_CENTER:
375 ident = (length - width) / 2;
376 break;
377 case J_RIGHT:
378 ident = length - width;
379 break;
380 default:
381 break;
384 pos += ident;
385 for (; pos < (gsize) (ident + width) && remain > 1; pos++, actual++, remain--)
386 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
389 finally:
390 if (actual >= result + sizeof (result))
391 actual = result + sizeof (result) - 1;
392 actual[0] = '\0';
393 return result;
396 /* --------------------------------------------------------------------------------------------- */
398 static const char *
399 str_8bit_term_trim (const char *text, int width)
401 static char result[BUF_MEDIUM];
402 size_t remain;
403 char *actual;
404 size_t length;
406 length = strlen (text);
407 actual = result;
408 remain = sizeof (result);
410 if (width > 0)
412 size_t pos;
414 if (width >= (int) length)
416 for (pos = 0; pos < length && remain > 1; pos++, actual++, remain--)
417 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
419 else if (width <= 3)
421 memset (actual, '.', width);
422 actual += width;
424 else
426 memset (actual, '.', 3);
427 actual += 3;
428 remain -= 3;
430 for (pos = length - width + 3; pos < length && remain > 1; pos++, actual++, remain--)
431 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
435 actual[0] = '\0';
436 return result;
439 /* --------------------------------------------------------------------------------------------- */
441 static int
442 str_8bit_term_width2 (const char *text, size_t length)
444 return (length != (size_t) (-1)) ? MIN (strlen (text), length) : strlen (text);
447 /* --------------------------------------------------------------------------------------------- */
449 static int
450 str_8bit_term_width1 (const char *text)
452 return str_8bit_term_width2 (text, (size_t) (-1));
455 /* --------------------------------------------------------------------------------------------- */
457 static int
458 str_8bit_term_char_width (const char *text)
460 (void) text;
461 return 1;
464 /* --------------------------------------------------------------------------------------------- */
466 static const char *
467 str_8bit_term_substring (const char *text, int start, int width)
469 static char result[BUF_MEDIUM];
470 size_t remain;
471 char *actual;
472 size_t length;
474 actual = result;
475 remain = sizeof (result);
476 length = strlen (text);
478 if (start < (int) length)
480 size_t pos;
482 for (pos = start; pos < length && width > 0 && remain > 1;
483 pos++, width--, actual++, remain--)
484 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
487 for (; width > 0 && remain > 1; actual++, remain--, width--)
488 actual[0] = ' ';
490 actual[0] = '\0';
491 return result;
494 /* --------------------------------------------------------------------------------------------- */
496 static const char *
497 str_8bit_trunc (const char *text, int width)
499 static char result[MC_MAXPATHLEN];
500 int remain;
501 char *actual;
502 size_t pos = 0;
503 size_t length;
505 actual = result;
506 remain = sizeof (result);
507 length = strlen (text);
509 if ((int) length > width)
511 for (; pos + 1 <= (gsize) width / 2 && remain > 1; actual++, pos++, remain--)
512 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
514 if (remain <= 1)
515 goto finally;
516 actual[0] = '~';
517 actual++;
518 remain--;
520 pos += length - width + 1;
521 for (; pos < length && remain > 1; pos++, actual++, remain--)
522 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
524 else
526 for (; pos < length && remain > 1; pos++, actual++, remain--)
527 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
530 finally:
531 actual[0] = '\0';
532 return result;
535 /* --------------------------------------------------------------------------------------------- */
537 static int
538 str_8bit_offset_to_pos (const char *text, size_t length)
540 (void) text;
541 return (int) length;
544 /* --------------------------------------------------------------------------------------------- */
546 static int
547 str_8bit_column_to_pos (const char *text, size_t pos)
549 (void) text;
550 return (int) pos;
553 /* --------------------------------------------------------------------------------------------- */
555 static char *
556 str_8bit_create_search_needle (const char *needle, gboolean case_sen)
558 (void) case_sen;
559 return (char *) needle;
562 /* --------------------------------------------------------------------------------------------- */
564 static void
565 str_8bit_release_search_needle (char *needle, gboolean case_sen)
567 (void) case_sen;
568 (void) needle;
571 /* --------------------------------------------------------------------------------------------- */
573 static char *
574 str_8bit_strdown (const char *str)
576 char *rets, *p;
578 if (str == NULL)
579 return NULL;
581 rets = g_strdup (str);
583 for (p = rets; *p != '\0'; p++)
584 *p = char_tolower (*p);
586 return rets;
589 /* --------------------------------------------------------------------------------------------- */
591 static const char *
592 str_8bit_search_first (const char *text, const char *search, gboolean case_sen)
594 char *fold_text;
595 char *fold_search;
596 const char *match;
598 fold_text = case_sen ? (char *) text : str_8bit_strdown (text);
599 fold_search = case_sen ? (char *) search : str_8bit_strdown (search);
601 match = g_strstr_len (fold_text, -1, fold_search);
602 if (match != NULL)
604 size_t offset;
606 offset = match - fold_text;
607 match = text + offset;
610 if (!case_sen)
612 g_free (fold_text);
613 g_free (fold_search);
616 return match;
619 /* --------------------------------------------------------------------------------------------- */
621 static const char *
622 str_8bit_search_last (const char *text, const char *search, gboolean case_sen)
624 char *fold_text;
625 char *fold_search;
626 const char *match;
628 fold_text = case_sen ? (char *) text : str_8bit_strdown (text);
629 fold_search = case_sen ? (char *) search : str_8bit_strdown (search);
631 match = g_strrstr_len (fold_text, -1, fold_search);
632 if (match != NULL)
634 size_t offset;
636 offset = match - fold_text;
637 match = text + offset;
640 if (!case_sen)
642 g_free (fold_text);
643 g_free (fold_search);
646 return match;
649 /* --------------------------------------------------------------------------------------------- */
651 static int
652 str_8bit_compare (const char *t1, const char *t2)
654 return strcmp (t1, t2);
657 /* --------------------------------------------------------------------------------------------- */
659 static int
660 str_8bit_ncompare (const char *t1, const char *t2)
662 return strncmp (t1, t2, MIN (strlen (t1), strlen (t2)));
665 /* --------------------------------------------------------------------------------------------- */
667 static int
668 str_8bit_casecmp (const char *s1, const char *s2)
670 /* code from GLib */
672 #ifdef HAVE_STRCASECMP
673 g_return_val_if_fail (s1 != NULL, 0);
674 g_return_val_if_fail (s2 != NULL, 0);
676 return strcasecmp (s1, s2);
677 #else
678 gint c1, c2;
680 g_return_val_if_fail (s1 != NULL, 0);
681 g_return_val_if_fail (s2 != NULL, 0);
683 while (*s1 != '\0' && *s2 != '\0')
685 /* According to A. Cox, some platforms have islower's that
686 * don't work right on non-uppercase
688 c1 = isupper ((guchar) * s1) ? tolower ((guchar) * s1) : *s1;
689 c2 = isupper ((guchar) * s2) ? tolower ((guchar) * s2) : *s2;
690 if (c1 != c2)
691 return (c1 - c2);
692 s1++;
693 s2++;
696 return (((gint) (guchar) * s1) - ((gint) (guchar) * s2));
697 #endif
700 /* --------------------------------------------------------------------------------------------- */
702 static int
703 str_8bit_ncasecmp (const char *s1, const char *s2)
705 size_t n;
707 g_return_val_if_fail (s1 != NULL, 0);
708 g_return_val_if_fail (s2 != NULL, 0);
710 n = MIN (strlen (s1), strlen (s2));
712 /* code from GLib */
714 #ifdef HAVE_STRNCASECMP
715 return strncasecmp (s1, s2, n);
716 #else
717 gint c1, c2;
719 while (n != 0 && *s1 != '\0' && *s2 != '\0')
721 n -= 1;
722 /* According to A. Cox, some platforms have islower's that
723 * don't work right on non-uppercase
725 c1 = isupper ((guchar) * s1) ? tolower ((guchar) * s1) : *s1;
726 c2 = isupper ((guchar) * s2) ? tolower ((guchar) * s2) : *s2;
727 if (c1 != c2)
728 return (c1 - c2);
729 s1++;
730 s2++;
733 if (n == 0)
734 return 0;
736 return (((gint) (guchar) * s1) - ((gint) (guchar) * s2));
738 #endif
741 /* --------------------------------------------------------------------------------------------- */
743 static int
744 str_8bit_prefix (const char *text, const char *prefix)
746 int result;
748 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
749 && text[result] == prefix[result]; result++);
751 return result;
754 /* --------------------------------------------------------------------------------------------- */
756 static int
757 str_8bit_caseprefix (const char *text, const char *prefix)
759 int result;
761 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
762 && char_toupper (text[result]) == char_toupper (prefix[result]); result++);
764 return result;
767 /* --------------------------------------------------------------------------------------------- */
769 static void
770 str_8bit_fix_string (char *text)
772 (void) text;
775 /* --------------------------------------------------------------------------------------------- */
777 static char *
778 str_8bit_create_key (const char *text, gboolean case_sen)
780 return case_sen ? (char *) text : str_8bit_strdown (text);
783 /* --------------------------------------------------------------------------------------------- */
785 static int
786 str_8bit_key_collate (const char *t1, const char *t2, gboolean case_sen)
788 return case_sen ? strcmp (t1, t2) : strcoll (t1, t2);
791 /* --------------------------------------------------------------------------------------------- */
793 static void
794 str_8bit_release_key (char *key, gboolean case_sen)
796 if (!case_sen)
797 g_free (key);
800 /* --------------------------------------------------------------------------------------------- */
801 /*** public functions ****************************************************************************/
802 /* --------------------------------------------------------------------------------------------- */
804 struct str_class
805 str_8bit_init (void)
807 struct str_class result;
809 result.conv_gerror_message = str_8bit_conv_gerror_message;
810 result.vfs_convert_to = str_8bit_vfs_convert_to;
811 result.insert_replace_char = str_8bit_insert_replace_char;
812 result.is_valid_string = str_8bit_is_valid_string;
813 result.is_valid_char = str_8bit_is_valid_char;
814 result.cnext_char = str_8bit_cnext_char;
815 result.cprev_char = str_8bit_cprev_char;
816 result.cnext_char_safe = str_8bit_cnext_char;
817 result.cprev_char_safe = str_8bit_cprev_char;
818 result.cnext_noncomb_char = str_8bit_cnext_noncomb_char;
819 result.cprev_noncomb_char = str_8bit_cprev_noncomb_char;
820 result.char_isspace = str_8bit_isspace;
821 result.char_ispunct = str_8bit_ispunct;
822 result.char_isalnum = str_8bit_isalnum;
823 result.char_isdigit = str_8bit_isdigit;
824 result.char_isprint = str_8bit_isprint;
825 result.char_iscombiningmark = str_8bit_iscombiningmark;
826 result.char_toupper = str_8bit_toupper;
827 result.char_tolower = str_8bit_tolower;
828 result.length = str_8bit_length;
829 result.length2 = str_8bit_length2;
830 result.length_noncomb = str_8bit_length;
831 result.fix_string = str_8bit_fix_string;
832 result.term_form = str_8bit_term_form;
833 result.fit_to_term = str_8bit_fit_to_term;
834 result.term_trim = str_8bit_term_trim;
835 result.term_width2 = str_8bit_term_width2;
836 result.term_width1 = str_8bit_term_width1;
837 result.term_char_width = str_8bit_term_char_width;
838 result.term_substring = str_8bit_term_substring;
839 result.trunc = str_8bit_trunc;
840 result.offset_to_pos = str_8bit_offset_to_pos;
841 result.column_to_pos = str_8bit_column_to_pos;
842 result.create_search_needle = str_8bit_create_search_needle;
843 result.release_search_needle = str_8bit_release_search_needle;
844 result.search_first = str_8bit_search_first;
845 result.search_last = str_8bit_search_last;
846 result.compare = str_8bit_compare;
847 result.ncompare = str_8bit_ncompare;
848 result.casecmp = str_8bit_casecmp;
849 result.ncasecmp = str_8bit_ncasecmp;
850 result.prefix = str_8bit_prefix;
851 result.caseprefix = str_8bit_caseprefix;
852 result.create_key = str_8bit_create_key;
853 result.create_key_for_filename = str_8bit_create_key;
854 result.key_collate = str_8bit_key_collate;
855 result.release_key = str_8bit_release_key;
857 return result;
860 /* --------------------------------------------------------------------------------------------- */