31d9e4bfd2188a92c967dbcd95693757e6e138b8
[midnight-commander.git] / lib / strutil / strutil8bit.c
blob31d9e4bfd2188a92c967dbcd95693757e6e138b8
1 /*
2 8bit strings utilities
4 Copyright (C) 2007, 2011
5 The Free Software Foundation, Inc.
7 Written by:
8 Rostislav Benes, 2007
10 The file_date routine is mostly from GNU's fileutils package,
11 written by Richard Stallman and David MacKenzie.
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 #include <config.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <errno.h>
34 #include "lib/global.h"
35 #include "lib/strutil.h"
37 /* functions for singlebyte encodings, all characters have width 1
38 * using standard system functions
39 * there are only small differences between functions in strutil8bit.c
40 * and strutilascii.c
43 static const char replch = '?';
46 * Inlines to equalize 'char' signedness for single 'char' encodings.
47 * Instead of writing
48 * isspace((unsigned char)c);
49 * you can write
50 * char_isspace(c);
53 #define DECLARE_CTYPE_WRAPPER(func_name) \
54 static inline int char_##func_name(char c) \
55 { \
56 return func_name((int)(unsigned char)c); \
59 /* *INDENT-OFF* */
60 DECLARE_CTYPE_WRAPPER (isalnum)
61 DECLARE_CTYPE_WRAPPER (isalpha)
62 DECLARE_CTYPE_WRAPPER (isascii)
63 DECLARE_CTYPE_WRAPPER (isblank)
64 DECLARE_CTYPE_WRAPPER (iscntrl)
65 DECLARE_CTYPE_WRAPPER (isdigit)
66 DECLARE_CTYPE_WRAPPER (isgraph)
67 DECLARE_CTYPE_WRAPPER (islower)
68 DECLARE_CTYPE_WRAPPER (isprint)
69 DECLARE_CTYPE_WRAPPER (ispunct)
70 DECLARE_CTYPE_WRAPPER (isspace)
71 DECLARE_CTYPE_WRAPPER (isupper)
72 DECLARE_CTYPE_WRAPPER (isxdigit)
73 DECLARE_CTYPE_WRAPPER (toupper)
74 DECLARE_CTYPE_WRAPPER (tolower)
75 /* *INDENT-ON* */
77 static void
78 str_8bit_insert_replace_char (GString * buffer)
80 g_string_append_c (buffer, replch);
83 static int
84 str_8bit_is_valid_string (const char *text)
86 (void) text;
87 return 1;
90 static int
91 str_8bit_is_valid_char (const char *ch, size_t size)
93 (void) ch;
94 (void) size;
95 return 1;
98 static void
99 str_8bit_cnext_char (const char **text)
101 (*text)++;
104 static void
105 str_8bit_cprev_char (const char **text)
107 (*text)--;
110 static int
111 str_8bit_cnext_noncomb_char (const char **text)
113 if (*text[0] != '\0')
115 (*text)++;
116 return 1;
118 else
119 return 0;
122 static int
123 str_8bit_cprev_noncomb_char (const char **text, const char *begin)
125 if ((*text) != begin)
127 (*text)--;
128 return 1;
130 else
131 return 0;
134 static int
135 str_8bit_isspace (const char *text)
137 return char_isspace (text[0]);
140 static int
141 str_8bit_ispunct (const char *text)
143 return char_ispunct (text[0]);
146 static int
147 str_8bit_isalnum (const char *text)
149 return char_isalnum (text[0]);
152 static int
153 str_8bit_isdigit (const char *text)
155 return char_isdigit (text[0]);
158 static int
159 str_8bit_isprint (const char *text)
161 return char_isprint (text[0]);
164 static gboolean
165 str_8bit_iscombiningmark (const char *text)
167 (void) text;
168 return FALSE;
171 static int
172 str_8bit_toupper (const char *text, char **out, size_t * remain)
174 if (*remain <= 1)
175 return 0;
176 (*out)[0] = char_toupper (text[0]);
177 (*out)++;
178 (*remain)--;
179 return 1;
182 static int
183 str_8bit_tolower (const char *text, char **out, size_t * remain)
185 if (*remain <= 1)
186 return 0;
187 (*out)[0] = char_tolower (text[0]);
188 (*out)++;
189 (*remain)--;
190 return 1;
193 static int
194 str_8bit_length (const char *text)
196 return strlen (text);
199 static int
200 str_8bit_length2 (const char *text, int size)
202 return (size >= 0) ? min (strlen (text), (gsize) size) : strlen (text);
205 static gchar *
206 str_8bit_conv_gerror_message (GError * error, const char *def_msg)
208 GIConv conv;
209 gchar *ret;
211 /* glib messages are in UTF-8 charset */
212 conv = str_crt_conv_from ("UTF-8");
214 if (conv == INVALID_CONV)
215 ret = g_strdup (def_msg != NULL ? def_msg : "");
216 else
218 GString *buf;
220 buf = g_string_new ("");
222 if (str_convert (conv, error->message, buf) != ESTR_FAILURE)
224 ret = buf->str;
225 g_string_free (buf, FALSE);
227 else
229 ret = g_strdup (def_msg != NULL ? def_msg : "");
230 g_string_free (buf, TRUE);
233 str_close_conv (conv);
236 return ret;
239 static estr_t
240 str_8bit_vfs_convert_to (GIConv coder, const char *string, int size, GString * buffer)
242 estr_t result;
244 if (coder == str_cnv_not_convert)
246 g_string_append_len (buffer, string, size);
247 result = ESTR_SUCCESS;
249 else
250 result = str_nconvert (coder, (char *) string, size, buffer);
252 return result;
256 static const char *
257 str_8bit_term_form (const char *text)
259 static char result[BUF_MEDIUM];
260 char *actual;
261 size_t remain;
262 size_t length;
263 size_t pos = 0;
265 actual = result;
266 remain = sizeof (result);
267 length = strlen (text);
269 for (; pos < length && remain > 1; pos++, actual++, remain--)
271 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
274 actual[0] = '\0';
275 return result;
278 static const char *
279 str_8bit_fit_to_term (const char *text, int width, align_crt_t just_mode)
281 static char result[BUF_MEDIUM];
282 char *actual;
283 size_t remain;
284 int ident;
285 size_t length;
286 size_t pos = 0;
288 length = strlen (text);
289 actual = result;
290 remain = sizeof (result);
292 if ((int) length <= width)
294 ident = 0;
295 switch (HIDE_FIT (just_mode))
297 case J_CENTER_LEFT:
298 case J_CENTER:
299 ident = (width - length) / 2;
300 break;
301 case J_RIGHT:
302 ident = width - length;
303 break;
306 if ((int) remain <= ident)
307 goto finally;
308 memset (actual, ' ', ident);
309 actual += ident;
310 remain -= ident;
312 for (; pos < length && remain > 1; pos++, actual++, remain--)
314 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
316 if (width - length - ident > 0)
318 if (remain <= width - length - ident)
319 goto finally;
320 memset (actual, ' ', width - length - ident);
321 actual += width - length - ident;
324 else
326 if (IS_FIT (just_mode))
328 for (; pos + 1 <= (gsize) width / 2 && remain > 1; actual++, pos++, remain--)
331 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
334 if (remain <= 1)
335 goto finally;
336 actual[0] = '~';
337 actual++;
338 remain--;
340 pos += length - width + 1;
342 for (; pos < length && remain > 1; pos++, actual++, remain--)
344 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
347 else
349 ident = 0;
350 switch (HIDE_FIT (just_mode))
352 case J_CENTER:
353 ident = (length - width) / 2;
354 break;
355 case J_RIGHT:
356 ident = length - width;
357 break;
360 pos += ident;
361 for (; pos < (gsize) (ident + width) && remain > 1; pos++, actual++, remain--)
364 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
369 finally:
370 actual[0] = '\0';
371 return result;
374 static const char *
375 str_8bit_term_trim (const char *text, int width)
377 static char result[BUF_MEDIUM];
378 size_t remain;
379 char *actual;
380 size_t pos = 0;
381 size_t length;
383 length = strlen (text);
384 actual = result;
385 remain = sizeof (result);
387 if (width > 0)
389 if (width < (int) length)
391 if (width <= 3)
393 memset (actual, '.', width);
394 actual += width;
396 else
398 memset (actual, '.', 3);
399 actual += 3;
400 remain -= 3;
402 pos += length - width + 3;
404 for (; pos < length && remain > 1; pos++, actual++, remain--)
405 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
408 else
410 for (; pos < length && remain > 1; pos++, actual++, remain--)
411 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
415 actual[0] = '\0';
416 return result;
419 static int
420 str_8bit_term_width2 (const char *text, size_t length)
422 return (length != (size_t) (-1)) ? min (strlen (text), length) : strlen (text);
425 static int
426 str_8bit_term_width1 (const char *text)
428 return str_8bit_term_width2 (text, (size_t) (-1));
431 static int
432 str_8bit_term_char_width (const char *text)
434 (void) text;
435 return 1;
438 static const char *
439 str_8bit_term_substring (const char *text, int start, int width)
441 static char result[BUF_MEDIUM];
442 size_t remain;
443 char *actual;
444 size_t pos = 0;
445 size_t length;
447 actual = result;
448 remain = sizeof (result);
449 length = strlen (text);
451 if (start < (int) length)
453 pos += start;
454 for (; pos < length && width > 0 && remain > 1; pos++, width--, actual++, remain--)
457 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
461 for (; width > 0 && remain > 1; actual++, remain--, width--)
463 actual[0] = ' ';
466 actual[0] = '\0';
467 return result;
470 static const char *
471 str_8bit_trunc (const char *text, int width)
473 static char result[MC_MAXPATHLEN];
474 int remain;
475 char *actual;
476 size_t pos = 0;
477 size_t length;
479 actual = result;
480 remain = sizeof (result);
481 length = strlen (text);
483 if ((int) length > width)
485 for (; pos + 1 <= (gsize) width / 2 && remain > 1; actual++, pos++, remain--)
487 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
490 if (remain <= 1)
491 goto finally;
492 actual[0] = '~';
493 actual++;
494 remain--;
496 pos += length - width + 1;
498 for (; pos < length && remain > 1; pos++, actual++, remain--)
500 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
503 else
505 for (; pos < length && remain > 1; pos++, actual++, remain--)
507 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
511 finally:
512 actual[0] = '\0';
513 return result;
516 static int
517 str_8bit_offset_to_pos (const char *text, size_t length)
519 (void) text;
520 return (int) length;
523 static int
524 str_8bit_column_to_pos (const char *text, size_t pos)
526 (void) text;
527 return (int) pos;
530 static char *
531 str_8bit_create_search_needle (const char *needle, int case_sen)
533 (void) case_sen;
534 return (char *) needle;
537 static void
538 str_8bit_release_search_needle (char *needle, int case_sen)
540 (void) case_sen;
541 (void) needle;
544 static char *
545 str_8bit_strdown (const char *str)
547 char *rets, *p;
549 rets = g_strdup (str);
550 if (rets == NULL)
551 return NULL;
553 for (p = rets; *p != '\0'; p++)
554 *p = char_tolower (*p);
556 return rets;
560 static const char *
561 str_8bit_search_first (const char *text, const char *search, int case_sen)
563 char *fold_text;
564 char *fold_search;
565 const char *match;
566 size_t offsset;
568 fold_text = (case_sen) ? (char *) text : str_8bit_strdown (text);
569 fold_search = (case_sen) ? (char *) search : str_8bit_strdown (search);
571 match = g_strstr_len (fold_text, -1, fold_search);
572 if (match != NULL)
574 offsset = match - fold_text;
575 match = text + offsset;
578 if (!case_sen)
580 g_free (fold_text);
581 g_free (fold_search);
584 return match;
587 static const char *
588 str_8bit_search_last (const char *text, const char *search, int case_sen)
590 char *fold_text;
591 char *fold_search;
592 const char *match;
593 size_t offsset;
595 fold_text = (case_sen) ? (char *) text : str_8bit_strdown (text);
596 fold_search = (case_sen) ? (char *) search : str_8bit_strdown (search);
598 match = g_strrstr_len (fold_text, -1, fold_search);
599 if (match != NULL)
601 offsset = match - fold_text;
602 match = text + offsset;
605 if (!case_sen)
607 g_free (fold_text);
608 g_free (fold_search);
611 return match;
614 static int
615 str_8bit_compare (const char *t1, const char *t2)
617 return strcmp (t1, t2);
620 static int
621 str_8bit_ncompare (const char *t1, const char *t2)
623 return strncmp (t1, t2, min (strlen (t1), strlen (t2)));
626 static int
627 str_8bit_casecmp (const char *s1, const char *s2)
629 /* code from GLib */
631 #ifdef HAVE_STRCASECMP
632 g_return_val_if_fail (s1 != NULL, 0);
633 g_return_val_if_fail (s2 != NULL, 0);
635 return strcasecmp (s1, s2);
636 #else
637 gint c1, c2;
639 g_return_val_if_fail (s1 != NULL, 0);
640 g_return_val_if_fail (s2 != NULL, 0);
642 while (*s1 != '\0' && *s2 != '\0')
644 /* According to A. Cox, some platforms have islower's that
645 * don't work right on non-uppercase
647 c1 = isupper ((guchar) * s1) ? tolower ((guchar) * s1) : *s1;
648 c2 = isupper ((guchar) * s2) ? tolower ((guchar) * s2) : *s2;
649 if (c1 != c2)
650 return (c1 - c2);
651 s1++;
652 s2++;
655 return (((gint) (guchar) * s1) - ((gint) (guchar) * s2));
656 #endif
659 static int
660 str_8bit_ncasecmp (const char *s1, const char *s2)
662 size_t n;
664 g_return_val_if_fail (s1 != NULL, 0);
665 g_return_val_if_fail (s2 != NULL, 0);
667 n = min (strlen (s1), strlen (s2));
669 /* code from GLib */
671 #ifdef HAVE_STRNCASECMP
672 return strncasecmp (s1, s2, n);
673 #else
674 gint c1, c2;
676 while (n != 0 && *s1 != '\0' && *s2 != '\0')
678 n -= 1;
679 /* According to A. Cox, some platforms have islower's that
680 * don't work right on non-uppercase
682 c1 = isupper ((guchar) * s1) ? tolower ((guchar) * s1) : *s1;
683 c2 = isupper ((guchar) * s2) ? tolower ((guchar) * s2) : *s2;
684 if (c1 != c2)
685 return (c1 - c2);
686 s1++;
687 s2++;
690 if (n != 0)
691 return (((gint) (guchar) * s1) - ((gint) (guchar) * s2));
692 else
693 return 0;
694 #endif
697 static int
698 str_8bit_prefix (const char *text, const char *prefix)
700 int result;
701 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
702 && text[result] == prefix[result]; result++);
703 return result;
706 static int
707 str_8bit_caseprefix (const char *text, const char *prefix)
709 int result;
710 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
711 && char_toupper (text[result]) == char_toupper (prefix[result]); result++);
712 return result;
717 static void
718 str_8bit_fix_string (char *text)
720 (void) text;
723 static char *
724 str_8bit_create_key (const char *text, int case_sen)
726 return (case_sen) ? (char *) text : str_8bit_strdown (text);
729 static int
730 str_8bit_key_collate (const char *t1, const char *t2, int case_sen)
732 if (case_sen)
733 return strcmp (t1, t2);
734 else
735 return strcoll (t1, t2);
738 static void
739 str_8bit_release_key (char *key, int case_sen)
741 if (!case_sen)
742 g_free (key);
745 struct str_class
746 str_8bit_init (void)
748 struct str_class result;
750 result.conv_gerror_message = str_8bit_conv_gerror_message;
751 result.vfs_convert_to = str_8bit_vfs_convert_to;
752 result.insert_replace_char = str_8bit_insert_replace_char;
753 result.is_valid_string = str_8bit_is_valid_string;
754 result.is_valid_char = str_8bit_is_valid_char;
755 result.cnext_char = str_8bit_cnext_char;
756 result.cprev_char = str_8bit_cprev_char;
757 result.cnext_char_safe = str_8bit_cnext_char;
758 result.cprev_char_safe = str_8bit_cprev_char;
759 result.cnext_noncomb_char = str_8bit_cnext_noncomb_char;
760 result.cprev_noncomb_char = str_8bit_cprev_noncomb_char;
761 result.char_isspace = str_8bit_isspace;
762 result.char_ispunct = str_8bit_ispunct;
763 result.char_isalnum = str_8bit_isalnum;
764 result.char_isdigit = str_8bit_isdigit;
765 result.char_isprint = str_8bit_isprint;
766 result.char_iscombiningmark = str_8bit_iscombiningmark;
767 result.char_toupper = str_8bit_toupper;
768 result.char_tolower = str_8bit_tolower;
769 result.length = str_8bit_length;
770 result.length2 = str_8bit_length2;
771 result.length_noncomb = str_8bit_length;
772 result.fix_string = str_8bit_fix_string;
773 result.term_form = str_8bit_term_form;
774 result.fit_to_term = str_8bit_fit_to_term;
775 result.term_trim = str_8bit_term_trim;
776 result.term_width2 = str_8bit_term_width2;
777 result.term_width1 = str_8bit_term_width1;
778 result.term_char_width = str_8bit_term_char_width;
779 result.term_substring = str_8bit_term_substring;
780 result.trunc = str_8bit_trunc;
781 result.offset_to_pos = str_8bit_offset_to_pos;
782 result.column_to_pos = str_8bit_column_to_pos;
783 result.create_search_needle = str_8bit_create_search_needle;
784 result.release_search_needle = str_8bit_release_search_needle;
785 result.search_first = str_8bit_search_first;
786 result.search_last = str_8bit_search_last;
787 result.compare = str_8bit_compare;
788 result.ncompare = str_8bit_ncompare;
789 result.casecmp = str_8bit_casecmp;
790 result.ncasecmp = str_8bit_ncasecmp;
791 result.prefix = str_8bit_prefix;
792 result.caseprefix = str_8bit_caseprefix;
793 result.create_key = str_8bit_create_key;
794 result.create_key_for_filename = str_8bit_create_key;
795 result.key_collate = str_8bit_key_collate;
796 result.release_key = str_8bit_release_key;
798 return result;