Ticket #1536: fix of case insensitive file sorting in UTF-8 locales.
[kaloumi3.git] / src / strutilascii.c
blobec6af9ec70102c2a609ebf5f7c47f9e0415882dd
1 /* ASCII strings utilities
2 Copyright (C) 2007 Free Software Foundation, Inc.
4 Written 2007 by:
5 Rostislav Benes
7 The file_date routine is mostly from GNU's fileutils package,
8 written by Richard Stallman and David MacKenzie.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <config.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <config.h>
29 #include <errno.h>
30 #include "global.h"
31 #include "strutil.h"
33 /* using g_ascii function from glib
34 * on terminal are showed only ascii characters (lower then 0x80)
37 static const char replch = '?';
39 static void
40 str_ascii_insert_replace_char (GString * buffer)
42 g_string_append_c (buffer, replch);
45 static int
46 str_ascii_is_valid_string (const char *text)
48 (void) text;
49 return 1;
52 static int
53 str_ascii_is_valid_char (const char *ch, size_t size)
55 (void) ch;
56 (void) size;
57 return 1;
60 static void
61 str_ascii_cnext_char (const char **text)
63 (*text)++;
66 static void
67 str_ascii_cprev_char (const char **text)
69 (*text)--;
72 static int
73 str_ascii_cnext_noncomb_char (const char **text)
75 if (*text[0] != '\0')
77 (*text)++;
78 return 1;
80 else
81 return 0;
84 static int
85 str_ascii_cprev_noncomb_char (const char **text, const char *begin)
87 if ((*text) != begin)
89 (*text)--;
90 return 1;
92 else
93 return 0;
96 static int
97 str_ascii_isspace (const char *text)
99 return g_ascii_isspace ((gchar) text[0]);
102 static int
103 str_ascii_ispunct (const char *text)
105 return g_ascii_ispunct ((gchar) text[0]);
108 static int
109 str_ascii_isalnum (const char *text)
111 return g_ascii_isalnum ((gchar) text[0]);
114 static int
115 str_ascii_isdigit (const char *text)
117 return g_ascii_isdigit ((gchar) text[0]);
120 static int
121 str_ascii_isprint (const char *text)
123 return g_ascii_isprint ((gchar) text[0]);
126 static int
127 str_ascii_iscombiningmark (const char *text)
129 (void) text;
130 return 0;
133 static int
134 str_ascii_toupper (const char *text, char **out, size_t * remain)
136 if (*remain <= 1)
137 return 0;
138 (*out)[0] = (char) g_ascii_toupper ((gchar) text[0]);
139 (*out)++;
140 (*remain)--;
141 return 1;
144 static int
145 str_ascii_tolower (const char *text, char **out, size_t * remain)
147 if (*remain <= 1)
148 return 0;
149 (*out)[0] = (char) g_ascii_tolower ((gchar) text[0]);
150 (*out)++;
151 (*remain)--;
152 return 1;
155 static int
156 str_ascii_length (const char *text)
158 return strlen (text);
161 static int
162 str_ascii_length2 (const char *text, int size)
164 return (size >= 0) ? min (strlen (text), (gsize) size) : strlen (text);
167 static gchar *
168 str_ascii_conv_gerror_message (GError *error, const char *def_msg)
170 /* the same as str_utf8_conv_gerror_message() */
171 if ((error != NULL) && (error->message != NULL))
172 return g_strdup (error->message);
174 return g_strdup (def_msg != NULL ? def_msg : "");
177 static estr_t
178 str_ascii_vfs_convert_to (GIConv coder, const char *string,
179 int size, GString * buffer)
181 (void) coder;
182 g_string_append_len (buffer, string, size);
183 return ESTR_SUCCESS;
187 static const char *
188 str_ascii_term_form (const char *text)
190 static char result[BUF_MEDIUM];
191 char *actual;
192 size_t remain;
193 size_t length;
194 size_t pos = 0;
196 actual = result;
197 remain = sizeof (result);
198 length = strlen (text);
200 /* go throw all characters and check, if they are ascii and printable */
201 for (; pos < length && remain > 1; pos++, actual++, remain--)
203 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
204 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
207 actual[0] = '\0';
208 return result;
211 static const char *
212 str_ascii_fit_to_term (const char *text, int width, align_crt_t just_mode)
214 static char result[BUF_MEDIUM];
215 char *actual;
216 size_t remain;
217 int ident;
218 size_t length;
219 size_t pos = 0;
221 length = strlen (text);
222 actual = result;
223 remain = sizeof (result);
225 if ((int)length <= width)
227 ident = 0;
228 switch (HIDE_FIT (just_mode))
230 case J_CENTER_LEFT:
231 case J_CENTER:
232 ident = (width - length) / 2;
233 break;
234 case J_RIGHT:
235 ident = width - length;
236 break;
239 /* add space before text */
240 if ((int)remain <= ident)
241 goto finally;
242 memset (actual, ' ', ident);
243 actual += ident;
244 remain -= ident;
246 /* copy all characters */
247 for (; pos < (gsize)length && remain > 1; pos++, actual++, remain--)
249 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
250 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
253 /* add space after text */
254 if (width - length - ident > 0)
256 if (remain <= width - length - ident)
257 goto finally;
258 memset (actual, ' ', width - length - ident);
259 actual += width - length - ident;
260 remain -= width - length - ident;
263 else
265 if (IS_FIT (just_mode))
267 /* copy prefix of text, that is not wider than width / 2 */
268 for (; pos + 1 <= (gsize)width / 2 && remain > 1;
269 actual++, pos++, remain--)
271 actual[0] = isascii ((unsigned char) text[pos])
272 ? text[pos] : '?';
273 actual[0] = g_ascii_isprint ((gchar) actual[0])
274 ? actual[0] : '.';
277 if (remain <= 1)
278 goto finally;
279 actual[0] = '~';
280 actual++;
281 remain--;
283 pos += length - width + 1;
285 /* copy suffix of text */
286 for (; pos < length && remain > 1; pos++, actual++, remain--)
288 actual[0] = isascii ((unsigned char) text[pos])
289 ? text[pos] : '?';
290 actual[0] = g_ascii_isprint ((gchar) actual[0])
291 ? actual[0] : '.';
294 else
296 ident = 0;
297 switch (HIDE_FIT (just_mode))
299 case J_CENTER:
300 ident = (length - width) / 2;
301 break;
302 case J_RIGHT:
303 ident = length - width;
304 break;
307 /* copy substring text, substring start from ident and take width
308 * characters from text */
309 pos += ident;
310 for (; pos < (gsize)(ident + width) && remain > 1;
311 pos++, actual++, remain--)
313 actual[0] = isascii ((unsigned char) text[pos])
314 ? text[pos] : '?';
315 actual[0] = g_ascii_isprint ((gchar) actual[0])
316 ? actual[0] : '.';
321 finally:
322 actual[0] = '\0';
323 return result;
326 static const char *
327 str_ascii_term_trim (const char *text, int width)
329 static char result[BUF_MEDIUM];
330 size_t remain;
331 char *actual;
332 size_t pos = 0;
333 size_t length;
335 length = strlen (text);
336 actual = result;
337 remain = sizeof (result);
339 if (width < (int)length)
341 if (width <= 3)
343 memset (actual, '.', width);
344 actual += width;
345 remain -= width;
347 else
349 memset (actual, '.', 3);
350 actual += 3;
351 remain -= 3;
353 pos += length - width + 3;
355 /* copy suffix of text */
356 for (; pos < length && remain > 1; pos++, actual++, remain--)
358 actual[0] = isascii ((unsigned char) text[pos])
359 ? text[pos] : '?';
360 actual[0] = g_ascii_isprint ((gchar) actual[0])
361 ? actual[0] : '.';
365 else
367 /* copy all characters */
368 for (; pos < length && remain > 1; pos++, actual++, remain--)
370 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
371 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
375 actual[0] = '\0';
376 return result;
379 static int
380 str_ascii_term_width2 (const char *text, size_t length)
382 return (length != (size_t) (-1))
383 ? min (strlen (text), length) : strlen (text);
386 static int
387 str_ascii_term_width1 (const char *text)
389 return str_ascii_term_width2 (text, (size_t) (-1));
392 static int
393 str_ascii_term_char_width (const char *text)
395 (void) text;
396 return 1;
399 static void
400 str_ascii_msg_term_size (const char *text, int *lines, int *columns)
402 char *p, *tmp;
403 char *q;
404 char c = '\0';
405 int width;
407 (*lines) = 1;
408 (*columns) = 0;
410 tmp = g_strdup (text);
411 p = tmp;
413 for (;;)
415 q = strchr (p, '\n');
416 if (q != NULL)
418 c = q[0];
419 q[0] = '\0';
422 width = str_ascii_term_width1 (p);
423 if (width > (*columns))
424 (*columns) = width;
426 if (q == NULL)
427 break;
428 q[0] = c;
429 p = q + 1;
430 (*lines)++;
432 g_free (tmp);
435 static const char *
436 str_ascii_term_substring (const char *text, int start, int width)
438 static char result[BUF_MEDIUM];
439 size_t remain;
440 char *actual;
441 size_t pos = 0;
442 size_t length;
444 actual = result;
445 remain = sizeof (result);
446 length = strlen (text);
448 if (start < (int)length)
450 pos += start;
451 /* copy at most width characters from text from start */
452 for (; pos < length && width > 0 && remain > 1;
453 pos++, width--, actual++, remain--)
456 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
457 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
461 /* if text is shorter then width, add space to the end */
462 for (; width > 0 && remain > 1; actual++, remain--, width--)
464 actual[0] = ' ';
467 actual[0] = '\0';
468 return result;
471 static const char *
472 str_ascii_trunc (const char *text, int width)
474 static char result[MC_MAXPATHLEN];
475 int remain;
476 char *actual;
477 size_t pos = 0;
478 size_t length;
480 actual = result;
481 remain = sizeof (result);
482 length = strlen (text);
484 if ((int)length > width)
486 /* copy prefix of text */
487 for (; pos + 1 <= (gsize)width / 2 && remain > 1; actual++, pos++, remain--)
489 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
490 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
493 if (remain <= 1)
494 goto finally;
495 actual[0] = '~';
496 actual++;
497 remain--;
499 pos += length - width + 1;
501 /* copy suffix of text */
502 for (; pos < length && remain > 1; pos++, actual++, remain--)
504 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
505 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
508 else
510 /* copy all characters */
511 for (; pos < length && remain > 1; pos++, actual++, remain--)
513 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
514 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
518 finally:
519 actual[0] = '\0';
520 return result;
523 static int
524 str_ascii_offset_to_pos (const char *text, size_t length)
526 (void) text;
527 return (int) length;
530 static int
531 str_ascii_column_to_pos (const char *text, size_t pos)
533 (void) text;
534 return (int)pos;
537 static char *
538 str_ascii_create_search_needle (const char *needle, int case_sen)
540 (void) case_sen;
541 return (char *) needle;
544 static void
545 str_ascii_release_search_needle (char *needle, int case_sen)
547 (void) case_sen;
548 (void) needle;
552 static const char *
553 str_ascii_search_first (const char *text, const char *search, int case_sen)
555 char *fold_text;
556 char *fold_search;
557 const char *match;
558 size_t offset;
560 fold_text = (case_sen) ? (char *) text : g_ascii_strdown (text, -1);
561 fold_search = (case_sen) ? (char *) search : g_ascii_strdown (search, -1);
563 match = g_strstr_len (fold_text, -1, fold_search);
564 if (match != NULL)
566 offset = match - fold_text;
567 match = text + offset;
570 if (!case_sen)
572 g_free (fold_text);
573 g_free (fold_search);
576 return match;
579 static const char *
580 str_ascii_search_last (const char *text, const char *search, int case_sen)
582 char *fold_text;
583 char *fold_search;
584 const char *match;
585 size_t offset;
587 fold_text = (case_sen) ? (char *) text : g_ascii_strdown (text, -1);
588 fold_search = (case_sen) ? (char *) search : g_ascii_strdown (search, -1);
590 match = g_strrstr_len (fold_text, -1, fold_search);
591 if (match != NULL)
593 offset = match - fold_text;
594 match = text + offset;
597 if (!case_sen)
599 g_free (fold_text);
600 g_free (fold_search);
603 return match;
606 static int
607 str_ascii_compare (const char *t1, const char *t2)
609 return strcmp (t1, t2);
612 static int
613 str_ascii_ncompare (const char *t1, const char *t2)
615 return strncmp (t1, t2, min (strlen (t1), strlen (t2)));
618 static int
619 str_ascii_casecmp (const char *t1, const char *t2)
621 return g_ascii_strcasecmp (t1, t2);
624 static int
625 str_ascii_ncasecmp (const char *t1, const char *t2)
627 return g_ascii_strncasecmp (t1, t2, min (strlen (t1), strlen (t2)));
630 static void
631 str_ascii_fix_string (char *text)
633 for (; text[0] != '\0'; text++)
635 text[0] = ((unsigned char) text[0] < 128) ? text[0] : '?';
639 static char *
640 str_ascii_create_key (const char *text, int case_sen)
642 (void) case_sen;
643 return (char *) text;
646 static int
647 str_ascii_key_collate (const char *t1, const char *t2, int case_sen)
649 return (case_sen) ? strcmp (t1, t2) : g_ascii_strcasecmp (t1, t2);
652 static void
653 str_ascii_release_key (char *key, int case_sen)
655 (void) key;
656 (void) case_sen;
659 static int
660 str_ascii_prefix (const char *text, const char *prefix)
662 int result;
663 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
664 && text[result] == prefix[result]; result++);
665 return result;
668 static int
669 str_ascii_caseprefix (const char *text, const char *prefix)
671 int result;
672 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
673 && g_ascii_toupper (text[result]) ==
674 g_ascii_toupper (prefix[result]); result++);
675 return result;
679 struct str_class
680 str_ascii_init (void)
682 struct str_class result;
684 result.conv_gerror_message = str_ascii_conv_gerror_message;
685 result.vfs_convert_to = str_ascii_vfs_convert_to;
686 result.insert_replace_char = str_ascii_insert_replace_char;
687 result.is_valid_string = str_ascii_is_valid_string;
688 result.is_valid_char = str_ascii_is_valid_char;
689 result.cnext_char = str_ascii_cnext_char;
690 result.cprev_char = str_ascii_cprev_char;
691 result.cnext_char_safe = str_ascii_cnext_char;
692 result.cprev_char_safe = str_ascii_cprev_char;
693 result.cnext_noncomb_char = str_ascii_cnext_noncomb_char;
694 result.cprev_noncomb_char = str_ascii_cprev_noncomb_char;
695 result.isspace = str_ascii_isspace;
696 result.ispunct = str_ascii_ispunct;
697 result.isalnum = str_ascii_isalnum;
698 result.isdigit = str_ascii_isdigit;
699 result.isprint = str_ascii_isprint;
700 result.iscombiningmark = str_ascii_iscombiningmark;
701 result.toupper = str_ascii_toupper;
702 result.tolower = str_ascii_tolower;
703 result.length = str_ascii_length;
704 result.length2 = str_ascii_length2;
705 result.length_noncomb = str_ascii_length;
706 result.fix_string = str_ascii_fix_string;
707 result.term_form = str_ascii_term_form;
708 result.fit_to_term = str_ascii_fit_to_term;
709 result.term_trim = str_ascii_term_trim;
710 result.term_width2 = str_ascii_term_width2;
711 result.term_width1 = str_ascii_term_width1;
712 result.term_char_width = str_ascii_term_char_width;
713 result.msg_term_size = str_ascii_msg_term_size;
714 result.term_substring = str_ascii_term_substring;
715 result.trunc = str_ascii_trunc;
716 result.offset_to_pos = str_ascii_offset_to_pos;
717 result.column_to_pos = str_ascii_column_to_pos;
718 result.create_search_needle = str_ascii_create_search_needle;
719 result.release_search_needle = str_ascii_release_search_needle;
720 result.search_first = str_ascii_search_first;
721 result.search_last = str_ascii_search_last;
722 result.compare = str_ascii_compare;
723 result.ncompare = str_ascii_ncompare;
724 result.casecmp = str_ascii_casecmp;
725 result.ncasecmp = str_ascii_ncasecmp;
726 result.prefix = str_ascii_prefix;
727 result.caseprefix = str_ascii_caseprefix;
728 result.create_key = str_ascii_create_key;
729 result.create_key_for_filename = str_ascii_create_key;
730 result.key_collate = str_ascii_key_collate;
731 result.release_key = str_ascii_release_key;
733 return result;