FISH: added external script 'utime'
[midnight-commander.git] / lib / strutil / strutil8bit.c
blob48b6d885bae89325b611b8361f650fc5939dc6fb
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 int
165 str_8bit_iscombiningmark (const char *text)
167 (void) text;
168 return 0;
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;
322 remain -= width - length - ident;
325 else
327 if (IS_FIT (just_mode))
329 for (; pos + 1 <= (gsize) width / 2 && remain > 1; actual++, pos++, remain--)
332 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
335 if (remain <= 1)
336 goto finally;
337 actual[0] = '~';
338 actual++;
339 remain--;
341 pos += length - width + 1;
343 for (; pos < length && remain > 1; pos++, actual++, remain--)
345 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
348 else
350 ident = 0;
351 switch (HIDE_FIT (just_mode))
353 case J_CENTER:
354 ident = (length - width) / 2;
355 break;
356 case J_RIGHT:
357 ident = length - width;
358 break;
361 pos += ident;
362 for (; pos < (gsize) (ident + width) && remain > 1; pos++, actual++, remain--)
365 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
370 finally:
371 actual[0] = '\0';
372 return result;
375 static const char *
376 str_8bit_term_trim (const char *text, int width)
378 static char result[BUF_MEDIUM];
379 size_t remain;
380 char *actual;
381 size_t pos = 0;
382 size_t length;
384 length = strlen (text);
385 actual = result;
386 remain = sizeof (result);
388 if (width > 0)
390 if (width < (int) length)
392 if (width <= 3)
394 memset (actual, '.', width);
395 actual += width;
396 remain -= width;
398 else
400 memset (actual, '.', 3);
401 actual += 3;
402 remain -= 3;
404 pos += length - width + 3;
406 for (; pos < length && remain > 1; pos++, actual++, remain--)
407 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
410 else
412 for (; pos < length && remain > 1; pos++, actual++, remain--)
413 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
417 actual[0] = '\0';
418 return result;
421 static int
422 str_8bit_term_width2 (const char *text, size_t length)
424 return (length != (size_t) (-1)) ? min (strlen (text), length) : strlen (text);
427 static int
428 str_8bit_term_width1 (const char *text)
430 return str_8bit_term_width2 (text, (size_t) (-1));
433 static int
434 str_8bit_term_char_width (const char *text)
436 (void) text;
437 return 1;
440 static const char *
441 str_8bit_term_substring (const char *text, int start, int width)
443 static char result[BUF_MEDIUM];
444 size_t remain;
445 char *actual;
446 size_t pos = 0;
447 size_t length;
449 actual = result;
450 remain = sizeof (result);
451 length = strlen (text);
453 if (start < (int) length)
455 pos += start;
456 for (; pos < length && width > 0 && remain > 1; pos++, width--, actual++, remain--)
459 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
463 for (; width > 0 && remain > 1; actual++, remain--, width--)
465 actual[0] = ' ';
468 actual[0] = '\0';
469 return result;
472 static const char *
473 str_8bit_trunc (const char *text, int width)
475 static char result[MC_MAXPATHLEN];
476 int remain;
477 char *actual;
478 size_t pos = 0;
479 size_t length;
481 actual = result;
482 remain = sizeof (result);
483 length = strlen (text);
485 if ((int) length > width)
487 for (; pos + 1 <= (gsize) width / 2 && remain > 1; actual++, pos++, remain--)
489 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
492 if (remain <= 1)
493 goto finally;
494 actual[0] = '~';
495 actual++;
496 remain--;
498 pos += length - width + 1;
500 for (; pos < length && remain > 1; pos++, actual++, remain--)
502 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
505 else
507 for (; pos < length && remain > 1; pos++, actual++, remain--)
509 actual[0] = char_isprint (text[pos]) ? text[pos] : '.';
513 finally:
514 actual[0] = '\0';
515 return result;
518 static int
519 str_8bit_offset_to_pos (const char *text, size_t length)
521 (void) text;
522 return (int) length;
525 static int
526 str_8bit_column_to_pos (const char *text, size_t pos)
528 (void) text;
529 return (int) pos;
532 static char *
533 str_8bit_create_search_needle (const char *needle, int case_sen)
535 (void) case_sen;
536 return (char *) needle;
539 static void
540 str_8bit_release_search_needle (char *needle, int case_sen)
542 (void) case_sen;
543 (void) needle;
546 static char *
547 str_8bit_strdown (const char *str)
549 char *rets, *p;
551 rets = g_strdup (str);
552 if (rets == NULL)
553 return NULL;
555 for (p = rets; *p != '\0'; p++)
556 *p = char_tolower (*p);
558 return rets;
562 static const char *
563 str_8bit_search_first (const char *text, const char *search, int case_sen)
565 char *fold_text;
566 char *fold_search;
567 const char *match;
568 size_t offsset;
570 fold_text = (case_sen) ? (char *) text : str_8bit_strdown (text);
571 fold_search = (case_sen) ? (char *) search : str_8bit_strdown (search);
573 match = g_strstr_len (fold_text, -1, fold_search);
574 if (match != NULL)
576 offsset = match - fold_text;
577 match = text + offsset;
580 if (!case_sen)
582 g_free (fold_text);
583 g_free (fold_search);
586 return match;
589 static const char *
590 str_8bit_search_last (const char *text, const char *search, int case_sen)
592 char *fold_text;
593 char *fold_search;
594 const char *match;
595 size_t offsset;
597 fold_text = (case_sen) ? (char *) text : str_8bit_strdown (text);
598 fold_search = (case_sen) ? (char *) search : str_8bit_strdown (search);
600 match = g_strrstr_len (fold_text, -1, fold_search);
601 if (match != NULL)
603 offsset = match - fold_text;
604 match = text + offsset;
607 if (!case_sen)
609 g_free (fold_text);
610 g_free (fold_search);
613 return match;
616 static int
617 str_8bit_compare (const char *t1, const char *t2)
619 return strcmp (t1, t2);
622 static int
623 str_8bit_ncompare (const char *t1, const char *t2)
625 return strncmp (t1, t2, min (strlen (t1), strlen (t2)));
628 static int
629 str_8bit_casecmp (const char *s1, const char *s2)
631 /* code from GLib */
633 #ifdef HAVE_STRCASECMP
634 g_return_val_if_fail (s1 != NULL, 0);
635 g_return_val_if_fail (s2 != NULL, 0);
637 return strcasecmp (s1, s2);
638 #else
639 gint c1, c2;
641 g_return_val_if_fail (s1 != NULL, 0);
642 g_return_val_if_fail (s2 != NULL, 0);
644 while (*s1 != '\0' && *s2 != '\0')
646 /* According to A. Cox, some platforms have islower's that
647 * don't work right on non-uppercase
649 c1 = isupper ((guchar) * s1) ? tolower ((guchar) * s1) : *s1;
650 c2 = isupper ((guchar) * s2) ? tolower ((guchar) * s2) : *s2;
651 if (c1 != c2)
652 return (c1 - c2);
653 s1++;
654 s2++;
657 return (((gint) (guchar) * s1) - ((gint) (guchar) * s2));
658 #endif
661 static int
662 str_8bit_ncasecmp (const char *s1, const char *s2)
664 size_t n;
666 g_return_val_if_fail (s1 != NULL, 0);
667 g_return_val_if_fail (s2 != NULL, 0);
669 n = min (strlen (s1), strlen (s2));
671 /* code from GLib */
673 #ifdef HAVE_STRNCASECMP
674 return strncasecmp (s1, s2, n);
675 #else
676 gint c1, c2;
678 while (n != 0 && *s1 != '\0' && *s2 != '\0')
680 n -= 1;
681 /* According to A. Cox, some platforms have islower's that
682 * don't work right on non-uppercase
684 c1 = isupper ((guchar) * s1) ? tolower ((guchar) * s1) : *s1;
685 c2 = isupper ((guchar) * s2) ? tolower ((guchar) * s2) : *s2;
686 if (c1 != c2)
687 return (c1 - c2);
688 s1++;
689 s2++;
692 if (n != 0)
693 return (((gint) (guchar) * s1) - ((gint) (guchar) * s2));
694 else
695 return 0;
696 #endif
699 static int
700 str_8bit_prefix (const char *text, const char *prefix)
702 int result;
703 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
704 && text[result] == prefix[result]; result++);
705 return result;
708 static int
709 str_8bit_caseprefix (const char *text, const char *prefix)
711 int result;
712 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
713 && char_toupper (text[result]) == char_toupper (prefix[result]); result++);
714 return result;
719 static void
720 str_8bit_fix_string (char *text)
722 (void) text;
725 static char *
726 str_8bit_create_key (const char *text, int case_sen)
728 return (case_sen) ? (char *) text : str_8bit_strdown (text);
731 static int
732 str_8bit_key_collate (const char *t1, const char *t2, int case_sen)
734 if (case_sen)
735 return strcmp (t1, t2);
736 else
737 return strcoll (t1, t2);
740 static void
741 str_8bit_release_key (char *key, int case_sen)
743 if (!case_sen)
744 g_free (key);
747 struct str_class
748 str_8bit_init (void)
750 struct str_class result;
752 result.conv_gerror_message = str_8bit_conv_gerror_message;
753 result.vfs_convert_to = str_8bit_vfs_convert_to;
754 result.insert_replace_char = str_8bit_insert_replace_char;
755 result.is_valid_string = str_8bit_is_valid_string;
756 result.is_valid_char = str_8bit_is_valid_char;
757 result.cnext_char = str_8bit_cnext_char;
758 result.cprev_char = str_8bit_cprev_char;
759 result.cnext_char_safe = str_8bit_cnext_char;
760 result.cprev_char_safe = str_8bit_cprev_char;
761 result.cnext_noncomb_char = str_8bit_cnext_noncomb_char;
762 result.cprev_noncomb_char = str_8bit_cprev_noncomb_char;
763 result.isspace = str_8bit_isspace;
764 result.ispunct = str_8bit_ispunct;
765 result.isalnum = str_8bit_isalnum;
766 result.isdigit = str_8bit_isdigit;
767 result.isprint = str_8bit_isprint;
768 result.iscombiningmark = str_8bit_iscombiningmark;
769 result.toupper = str_8bit_toupper;
770 result.tolower = str_8bit_tolower;
771 result.length = str_8bit_length;
772 result.length2 = str_8bit_length2;
773 result.length_noncomb = str_8bit_length;
774 result.fix_string = str_8bit_fix_string;
775 result.term_form = str_8bit_term_form;
776 result.fit_to_term = str_8bit_fit_to_term;
777 result.term_trim = str_8bit_term_trim;
778 result.term_width2 = str_8bit_term_width2;
779 result.term_width1 = str_8bit_term_width1;
780 result.term_char_width = str_8bit_term_char_width;
781 result.term_substring = str_8bit_term_substring;
782 result.trunc = str_8bit_trunc;
783 result.offset_to_pos = str_8bit_offset_to_pos;
784 result.column_to_pos = str_8bit_column_to_pos;
785 result.create_search_needle = str_8bit_create_search_needle;
786 result.release_search_needle = str_8bit_release_search_needle;
787 result.search_first = str_8bit_search_first;
788 result.search_last = str_8bit_search_last;
789 result.compare = str_8bit_compare;
790 result.ncompare = str_8bit_ncompare;
791 result.casecmp = str_8bit_casecmp;
792 result.ncasecmp = str_8bit_ncasecmp;
793 result.prefix = str_8bit_prefix;
794 result.caseprefix = str_8bit_caseprefix;
795 result.create_key = str_8bit_create_key;
796 result.create_key_for_filename = str_8bit_create_key;
797 result.key_collate = str_8bit_key_collate;
798 result.release_key = str_8bit_release_key;
800 return result;