fix: incorret draw files in 8-bit codeset after recode
[midnight-commander.git] / src / strutilascii.c
blob0920929c98aedef749bc1e667c0b668b6b977273
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.
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <config.h>
28 #include <errno.h>
29 #include <iconv.h>
31 #include "global.h"
32 #include "strutil.h"
34 /* using g_ascii function from glib
35 * on terminal are showed only ascii characters (lower then 0x80)
38 static const char replch = '?';
40 static void
41 str_ascii_insert_replace_char (GString * buffer)
43 g_string_append_c (buffer, replch);
46 static int
47 str_ascii_is_valid_string (const char *text)
49 return 1;
52 static int
53 str_ascii_is_valid_char (const char *ch, size_t size)
55 return 1;
58 static void
59 str_ascii_cnext_char (const char **text)
61 (*text)++;
64 static void
65 str_ascii_cprev_char (const char **text)
67 (*text)--;
70 static int
71 str_ascii_cnext_noncomb_char (const char **text)
73 if (*text[0] != '\0')
75 (*text)++;
76 return 1;
78 else
79 return 0;
82 static int
83 str_ascii_cprev_noncomb_char (const char **text, const char *begin)
85 if ((*text) != begin)
87 (*text)--;
88 return 1;
90 else
91 return 0;
94 static int
95 str_ascii_isspace (const char *text)
97 return g_ascii_isspace ((gchar) text[0]);
100 static int
101 str_ascii_ispunct (const char *text)
103 return g_ascii_ispunct ((gchar) text[0]);
106 static int
107 str_ascii_isalnum (const char *text)
109 return g_ascii_isalnum ((gchar) text[0]);
112 static int
113 str_ascii_isdigit (const char *text)
115 return g_ascii_isdigit ((gchar) text[0]);
118 static int
119 str_ascii_isprint (const char *text)
121 return g_ascii_isprint ((gchar) text[0]);
124 static int
125 str_ascii_iscombiningmark (const char *text)
127 return 0;
130 static int
131 str_ascii_toupper (const char *text, char **out, size_t * remain)
133 if (*remain <= 1)
134 return 0;
135 (*out)[0] = (char) g_ascii_toupper ((gchar) text[0]);
136 (*out)++;
137 (*remain)--;
138 return 1;
141 static int
142 str_ascii_tolower (const char *text, char **out, size_t * remain)
144 if (*remain <= 1)
145 return 0;
146 (*out)[0] = (char) g_ascii_tolower ((gchar) text[0]);
147 (*out)++;
148 (*remain)--;
149 return 1;
152 static int
153 str_ascii_length (const char *text)
155 return strlen (text);
158 static int
159 str_ascii_length2 (const char *text, int size)
161 return (size >= 0) ? min (strlen (text), size) : strlen (text);
165 str_ascii_vfs_convert_to (GIConv coder, const char *string,
166 int size, GString * buffer)
168 g_string_append_len (buffer, string, size);
169 return 0;
173 static const char *
174 str_ascii_term_form (const char *text)
176 static char result[BUF_MEDIUM];
177 char *actual;
178 size_t remain;
179 size_t length;
180 size_t pos = 0;
182 actual = result;
183 remain = sizeof (result);
184 length = strlen (text);
186 /* go throw all characters and check, if they are ascii and printable */
187 for (; pos < length && remain > 1; pos++, actual++, remain--)
189 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
190 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
193 actual[0] = '\0';
194 return result;
197 static const char *
198 str_ascii_fit_to_term (const char *text, int width, int just_mode)
200 static char result[BUF_MEDIUM];
201 char *actual;
202 size_t remain;
203 int ident;
204 size_t length;
205 size_t pos = 0;
207 length = strlen (text);
208 actual = result;
209 remain = sizeof (result);
211 if (length <= width)
213 ident = 0;
214 switch (HIDE_FIT (just_mode))
216 case J_CENTER_LEFT:
217 case J_CENTER:
218 ident = (width - length) / 2;
219 break;
220 case J_RIGHT:
221 ident = width - length;
222 break;
225 /* add space before text */
226 if (remain <= ident)
227 goto finally;
228 memset (actual, ' ', ident);
229 actual += ident;
230 remain -= ident;
232 /* copy all characters */
233 for (; pos < length && remain > 1; pos++, actual++, remain--)
235 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
236 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
239 /* add space after text */
240 if (width - length - ident > 0)
242 if (remain <= width - length - ident)
243 goto finally;
244 memset (actual, ' ', width - length - ident);
245 actual += width - length - ident;
246 remain -= width - length - ident;
249 else
251 if (IS_FIT (just_mode))
253 /* copy prefix of text, that is not wider than width / 2 */
254 for (; pos + 1 <= width / 2 && remain > 1;
255 actual++, pos++, remain--)
257 actual[0] = isascii ((unsigned char) text[pos])
258 ? text[pos] : '?';
259 actual[0] = g_ascii_isprint ((gchar) actual[0])
260 ? actual[0] : '.';
263 if (remain <= 1)
264 goto finally;
265 actual[0] = '~';
266 actual++;
267 remain--;
269 pos += length - width + 1;
271 /* copy suffix of text */
272 for (; pos < length && remain > 1; pos++, actual++, remain--)
274 actual[0] = isascii ((unsigned char) text[pos])
275 ? text[pos] : '?';
276 actual[0] = g_ascii_isprint ((gchar) actual[0])
277 ? actual[0] : '.';
280 else
282 ident = 0;
283 switch (HIDE_FIT (just_mode))
285 case J_CENTER:
286 ident = (length - width) / 2;
287 break;
288 case J_RIGHT:
289 ident = length - width;
290 break;
293 /* copy substring text, substring start from ident and take width
294 * characters from text */
295 pos += ident;
296 for (; pos < ident + width && remain > 1;
297 pos++, actual++, remain--)
299 actual[0] = isascii ((unsigned char) text[pos])
300 ? text[pos] : '?';
301 actual[0] = g_ascii_isprint ((gchar) actual[0])
302 ? actual[0] : '.';
307 finally:
308 actual[0] = '\0';
309 return result;
312 static const char *
313 str_ascii_term_trim (const char *text, int width)
315 static char result[BUF_MEDIUM];
316 size_t remain;
317 char *actual;
318 size_t pos = 0;
319 size_t length;
321 length = strlen (text);
322 actual = result;
323 remain = sizeof (result);
325 if (width < length)
327 if (width <= 3)
329 memset (actual, '.', width);
330 actual += width;
331 remain -= width;
333 else
335 memset (actual, '.', 3);
336 actual += 3;
337 remain -= 3;
339 pos += length - width + 3;
341 /* copy suffix of text */
342 for (; pos < length && remain > 1; pos++, actual++, remain--)
344 actual[0] = isascii ((unsigned char) text[pos])
345 ? text[pos] : '?';
346 actual[0] = g_ascii_isprint ((gchar) actual[0])
347 ? actual[0] : '.';
351 else
353 /* copy all characters */
354 for (; pos < length && remain > 1; pos++, actual++, remain--)
356 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
357 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
361 actual[0] = '\0';
362 return result;
365 static int
366 str_ascii_term_width2 (const char *text, size_t length)
368 return (length != (size_t) (-1))
369 ? min (strlen (text), length) : strlen (text);
372 static int
373 str_ascii_term_width1 (const char *text)
375 return str_ascii_term_width2 (text, (size_t) (-1));
378 static int
379 str_ascii_term_char_width (const char *text)
381 return 1;
384 static void
385 str_ascii_msg_term_size (const char *text, int *lines, int *columns)
387 (*lines) = 1;
388 (*columns) = 0;
390 char *p, *tmp = g_strdup (text);
391 char *q;
392 char c = '\0';
393 int width;
394 p = tmp;
396 for (;;)
398 q = strchr (p, '\n');
399 if (q != NULL)
401 c = q[0];
402 q[0] = '\0';
405 width = str_ascii_term_width1 (p);
406 if (width > (*columns))
407 (*columns) = width;
409 if (q == NULL)
410 break;
411 q[0] = c;
412 p = q + 1;
413 (*lines)++;
415 g_free (tmp);
418 static const char *
419 str_ascii_term_substring (const char *text, int start, int width)
421 static char result[BUF_MEDIUM];
422 size_t remain;
423 char *actual;
424 size_t pos = 0;
425 size_t length;
427 actual = result;
428 remain = sizeof (result);
429 length = strlen (text);
431 if (start < length)
433 pos += start;
434 /* copy at most width characters from text from start */
435 for (; pos < length && width > 0 && remain > 1;
436 pos++, width--, actual++, remain--)
439 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
440 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
444 /* if text is shorter then width, add space to the end */
445 for (; width > 0 && remain > 1; actual++, remain--, width--)
447 actual[0] = ' ';
450 actual[0] = '\0';
451 return result;
454 static const char *
455 str_ascii_trunc (const char *text, int width)
457 static char result[MC_MAXPATHLEN];
458 int remain;
459 char *actual;
460 size_t pos = 0;
461 size_t length;
463 actual = result;
464 remain = sizeof (result);
465 length = strlen (text);
467 if (length > width)
469 /* copy prefix of text */
470 for (; pos + 1 <= width / 2 && remain > 1; actual++, pos++, remain--)
472 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
473 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
476 if (remain <= 1)
477 goto finally;
478 actual[0] = '~';
479 actual++;
480 remain--;
482 pos += length - width + 1;
484 /* copy suffix of text */
485 for (; pos < length && remain > 1; pos++, actual++, remain--)
487 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
488 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
491 else
493 /* copy all characters */
494 for (; pos < length && remain > 1; pos++, actual++, remain--)
496 actual[0] = isascii ((unsigned char) text[pos]) ? text[pos] : '?';
497 actual[0] = g_ascii_isprint ((gchar) actual[0]) ? actual[0] : '.';
501 finally:
502 actual[0] = '\0';
503 return result;
506 static int
507 str_ascii_offset_to_pos (const char *text, size_t length)
509 return (int) length;
512 static int
513 str_ascii_column_to_pos (const char *text, size_t pos)
515 return (int)pos;
518 static char *
519 str_ascii_create_search_needle (const char *needle, int case_sen)
521 return (char *) needle;
524 static void
525 str_ascii_release_search_needle (char *needle, int case_sen)
529 static const char *
530 str_ascii_search_first (const char *text, const char *search, int case_sen)
532 char *fold_text;
533 char *fold_search;
534 const char *match;
535 size_t offset;
537 fold_text = (case_sen) ? (char *) text : g_ascii_strdown (text, -1);
538 fold_search = (case_sen) ? (char *) search : g_ascii_strdown (search, -1);
540 match = g_strstr_len (fold_text, -1, fold_search);
541 if (match != NULL)
543 offset = match - fold_text;
544 match = text + offset;
547 if (!case_sen)
549 g_free (fold_text);
550 g_free (fold_search);
553 return match;
556 static const char *
557 str_ascii_search_last (const char *text, const char *search, int case_sen)
559 char *fold_text;
560 char *fold_search;
561 const char *match;
562 size_t offset;
564 fold_text = (case_sen) ? (char *) text : g_ascii_strdown (text, -1);
565 fold_search = (case_sen) ? (char *) search : g_ascii_strdown (search, -1);
567 match = g_strrstr_len (fold_text, -1, fold_search);
568 if (match != NULL)
570 offset = match - fold_text;
571 match = text + offset;
574 if (!case_sen)
576 g_free (fold_text);
577 g_free (fold_search);
580 return match;
583 static int
584 str_ascii_compare (const char *t1, const char *t2)
586 return strcmp (t1, t2);
589 static int
590 str_ascii_ncompare (const char *t1, const char *t2)
592 return strncmp (t1, t2, min (strlen (t1), strlen (t2)));
595 static int
596 str_ascii_casecmp (const char *t1, const char *t2)
598 return g_ascii_strcasecmp (t1, t2);
601 static int
602 str_ascii_ncasecmp (const char *t1, const char *t2)
604 return g_ascii_strncasecmp (t1, t2, min (strlen (t1), strlen (t2)));
607 static void
608 str_ascii_fix_string (char *text)
610 for (; text[0] != '\0'; text++)
612 text[0] = ((unsigned char) text[0] < 128) ? text[0] : '?';
616 static char *
617 str_ascii_create_key (const char *text, int case_sen)
619 return (char *) text;
622 static int
623 str_ascii_key_collate (const char *t1, const char *t2, int case_sen)
625 return (case_sen) ? strcmp (t1, t2) : g_ascii_strcasecmp (t1, t2);
628 static void
629 str_ascii_release_key (char *key, int case_sen)
633 static int
634 str_ascii_prefix (const char *text, const char *prefix)
636 int result;
637 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
638 && text[result] == prefix[result]; result++);
639 return result;
642 static int
643 str_ascii_caseprefix (const char *text, const char *prefix)
645 int result;
646 for (result = 0; text[result] != '\0' && prefix[result] != '\0'
647 && g_ascii_toupper (text[result]) ==
648 g_ascii_toupper (prefix[result]); result++);
649 return result;
653 struct str_class
654 str_ascii_init ()
656 struct str_class result;
658 result.vfs_convert_to = str_ascii_vfs_convert_to;
659 result.insert_replace_char = str_ascii_insert_replace_char;
660 result.is_valid_string = str_ascii_is_valid_string;
661 result.is_valid_char = str_ascii_is_valid_char;
662 result.cnext_char = str_ascii_cnext_char;
663 result.cprev_char = str_ascii_cprev_char;
664 result.cnext_char_safe = str_ascii_cnext_char;
665 result.cprev_char_safe = str_ascii_cprev_char;
666 result.cnext_noncomb_char = str_ascii_cnext_noncomb_char;
667 result.cprev_noncomb_char = str_ascii_cprev_noncomb_char;
668 result.isspace = str_ascii_isspace;
669 result.ispunct = str_ascii_ispunct;
670 result.isalnum = str_ascii_isalnum;
671 result.isdigit = str_ascii_isdigit;
672 result.isprint = str_ascii_isprint;
673 result.iscombiningmark = str_ascii_iscombiningmark;
674 result.toupper = str_ascii_toupper;
675 result.tolower = str_ascii_tolower;
676 result.length = str_ascii_length;
677 result.length2 = str_ascii_length2;
678 result.length_noncomb = str_ascii_length;
679 result.fix_string = str_ascii_fix_string;
680 result.term_form = str_ascii_term_form;
681 result.fit_to_term = str_ascii_fit_to_term;
682 result.term_trim = str_ascii_term_trim;
683 result.term_width2 = str_ascii_term_width2;
684 result.term_width1 = str_ascii_term_width1;
685 result.term_char_width = str_ascii_term_char_width;
686 result.msg_term_size = str_ascii_msg_term_size;
687 result.term_substring = str_ascii_term_substring;
688 result.trunc = str_ascii_trunc;
689 result.offset_to_pos = str_ascii_offset_to_pos;
690 result.column_to_pos = str_ascii_column_to_pos;
691 result.create_search_needle = str_ascii_create_search_needle;
692 result.release_search_needle = str_ascii_release_search_needle;
693 result.search_first = str_ascii_search_first;
694 result.search_last = str_ascii_search_last;
695 result.compare = str_ascii_compare;
696 result.ncompare = str_ascii_ncompare;
697 result.casecmp = str_ascii_casecmp;
698 result.ncasecmp = str_ascii_ncasecmp;
699 result.prefix = str_ascii_prefix;
700 result.caseprefix = str_ascii_caseprefix;
701 result.create_key = str_ascii_create_key;
702 result.create_key_for_filename = str_ascii_create_key;
703 result.key_collate = str_ascii_key_collate;
704 result.release_key = str_ascii_release_key;
706 return result;