• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef _XOPEN_SOURCE
20 #define _XOPEN_SOURCE
21 #endif
22 #include "aconfig.h"
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <wchar.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include "utils.h"
30 #include "mem.h"
31 
32 /*
33  * mbs_at_width - compute screen position in a string
34  *
35  * For displaying strings on the screen, we have to know how many character
36  * cells are occupied.  This function calculates the position in a multibyte
37  * string that is at a desired position.
38  *
39  * Parameters:
40  * s:     the string
41  * width: on input, the desired number of character cells; on output, the actual
42  *        position, in character cells, of the return value
43  * dir:   -1 or 1; in which direction to round if a multi-column character goes
44  *        over the desired width
45  *
46  * Return value:
47  * Pointer to the place in the string that is as near the desired width as
48  * possible.  If the string is too short, the return value points to the
49  * terminating zero.  If the last character is a multi-column character that
50  * goes over the desired width, the return value may be one character cell
51  * earlier or later than desired, depending on the dir parameter.
52  * In any case, the return value points after any zero-width characters that
53  * follow the last character.
54  */
mbs_at_width(const char * s,int * width,int dir)55 const char *mbs_at_width(const char *s, int *width, int dir)
56 {
57 	size_t len;
58 	wchar_t wc;
59 	int bytes;
60 	int width_so_far, w;
61 
62 	if (*width <= 0)
63 		return s;
64 	mbtowc(NULL, NULL, 0); /* reset shift state */
65 	len = strlen(s);
66 	width_so_far = 0;
67 	while (len && (bytes = mbtowc(&wc, s, len)) > 0) {
68 		w = wcwidth(wc);
69 		if (width_so_far + w > *width && dir < 0)
70 			break;
71 		if (w >= 0)
72 			width_so_far += w;
73 		s += bytes;
74 		len -= bytes;
75 		if (width_so_far >= *width) {
76 			while (len && (bytes = mbtowc(&wc, s, len)) > 0) {
77 				w = wcwidth(wc);
78 				if (w != 0)
79 					break;
80 				s += bytes;
81 				len -= bytes;
82 			}
83 			break;
84 		}
85 	}
86 	*width = width_so_far;
87 	return s;
88 }
89 
90 /*
91  * get_mbs_width - compute screen width of a string
92  */
get_mbs_width(const char * s)93 unsigned int get_mbs_width(const char *s)
94 {
95 	int width;
96 
97 	width = INT_MAX;
98 	mbs_at_width(s, &width, 1);
99 	return width;
100 }
101 
102 /*
103  * get_max_mbs_width - get width of longest string in an array
104  */
get_max_mbs_width(const char * const * s,unsigned int count)105 unsigned int get_max_mbs_width(const char *const *s, unsigned int count)
106 {
107 	unsigned int max_width, i, len;
108 
109 	max_width = 0;
110 	for (i = 0; i < count; ++i) {
111 		len = get_mbs_width(s[i]);
112 		if (len > max_width)
113 			max_width = len;
114 	}
115 	return max_width;
116 }
117 
118 #define MAX_FILE_SIZE 1048576
read_file(const char * file_name,unsigned int * file_size)119 char *read_file(const char *file_name, unsigned int *file_size)
120 {
121 	FILE *f;
122 	int err;
123 	char *buf;
124 	unsigned int allocated = 2048;
125 	unsigned int bytes_read;
126 
127 	f = fopen(file_name, "r");
128 	if (!f) {
129 		err = errno;
130 		errno = err;
131 		return NULL;
132 	}
133 	*file_size = 0;
134 	buf = NULL;
135 	do {
136 		allocated *= 2;
137 		buf = crealloc(buf, allocated);
138 		bytes_read = fread(buf + *file_size, 1, allocated - *file_size, f);
139 		*file_size += bytes_read;
140 	} while (*file_size == allocated && allocated < MAX_FILE_SIZE);
141 	fclose(f);
142 	if (*file_size > 0 && buf[*file_size - 1] != '\n' && *file_size < allocated) {
143 		buf[*file_size] = '\n';
144 		++*file_size;
145 	}
146 	return buf;
147 }
148 
149