tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

utils.c (3097B)


      1 /*
      2  * utils.c - multibyte-string helpers
      3  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      4  *
      5  * This program is free software: you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation, either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #define _XOPEN_SOURCE
     20 #include "aconfig.h"
     21 #include <limits.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <wchar.h>
     25 #include "utils.h"
     26 
     27 /*
     28  * mbs_at_width - compute screen position in a string
     29  *
     30  * For displaying strings on the screen, we have to know how many character
     31  * cells are occupied.  This function calculates the position in a multibyte
     32  * string that is at a desired position.
     33  *
     34  * Parameters:
     35  * s:     the string
     36  * width: on input, the desired number of character cells; on output, the actual
     37  *        position, in character cells, of the return value
     38  * dir:   -1 or 1; in which direction to round if a multi-column character goes
     39  *        over the desired width
     40  *
     41  * Return value:
     42  * Pointer to the place in the string that is as near the desired width as
     43  * possible.  If the string is too short, the return value points to the
     44  * terminating zero.  If the last character is a multi-column character that
     45  * goes over the desired width, the return value may be one character cell
     46  * earlier or later than desired, depending on the dir parameter.
     47  * In any case, the return value points after any zero-width characters that
     48  * follow the last character.
     49  */
     50 const char *mbs_at_width(const char *s, int *width, int dir)
     51 {
     52 	size_t len;
     53 	wchar_t wc;
     54 	int bytes;
     55 	int width_so_far, w;
     56 
     57 	if (*width <= 0)
     58 		return s;
     59 	mbtowc(NULL, NULL, 0); /* reset shift state */
     60 	len = strlen(s);
     61 	width_so_far = 0;
     62 	while (len && (bytes = mbtowc(&wc, s, len)) > 0) {
     63 		w = wcwidth(wc);
     64 		if (width_so_far + w > *width && dir < 0)
     65 			break;
     66 		if (w >= 0)
     67 			width_so_far += w;
     68 		s += bytes;
     69 		len -= bytes;
     70 		if (width_so_far >= *width) {
     71 			while (len && (bytes = mbtowc(&wc, s, len)) > 0) {
     72 				w = wcwidth(wc);
     73 				if (w != 0)
     74 					break;
     75 				s += bytes;
     76 				len -= bytes;
     77 			}
     78 			break;
     79 		}
     80 	}
     81 	*width = width_so_far;
     82 	return s;
     83 }
     84 
     85 /*
     86  * get_mbs_width - compute screen width of a string
     87  */
     88 unsigned int get_mbs_width(const char *s)
     89 {
     90 	int width;
     91 
     92 	width = INT_MAX;
     93 	mbs_at_width(s, &width, 1);
     94 	return width;
     95 }
     96 
     97 /*
     98  * get_max_mbs_width - get width of longest string in an array
     99  */
    100 unsigned int get_max_mbs_width(const char *const *s, unsigned int count)
    101 {
    102 	unsigned int max_width, i, len;
    103 
    104 	max_width = 0;
    105 	for (i = 0; i < count; ++i) {
    106 		len = get_mbs_width(s[i]);
    107 		if (len > max_width)
    108 			max_width = len;
    109 	}
    110 	return max_width;
    111 }