GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libedit/chartype.c Lines: 49 165 29.7 %
Date: 2017-11-13 Branches: 28 108 25.9 %

Line Branch Exec Source
1
/*	$OpenBSD: chartype.c,v 1.15 2016/04/11 21:17:29 schwarze Exp $	*/
2
/*	$NetBSD: chartype.c,v 1.6 2011/07/28 00:48:21 christos Exp $	*/
3
4
/*-
5
 * Copyright (c) 2009 The NetBSD Foundation, Inc.
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
 * POSSIBILITY OF SUCH DAMAGE.
28
 */
29
30
/*
31
 * chartype.c: character classification and meta information
32
 */
33
#include "config.h"
34
35
#include <ctype.h>
36
#include <stdlib.h>
37
#include <string.h>
38
39
#include "el.h"
40
41
#define CT_BUFSIZ 1024
42
43
static void ct_conv_buff_resize(ct_buffer_t *, size_t, size_t);
44
45
static void
46
ct_conv_buff_resize(ct_buffer_t *conv, size_t mincsize, size_t minwsize)
47
{
48
	void *p;
49
42
	if (mincsize > conv->csize) {
50
3
		conv->csize = mincsize;
51
3
		p = reallocarray(conv->cbuff, conv->csize, sizeof(char));
52
3
		if (p == NULL) {
53
			conv->csize = 0;
54
			free(conv->cbuff);
55
			conv->cbuff = NULL;
56
		} else
57
			conv->cbuff = p;
58
3
	}
59
60
21
	if (minwsize > conv->wsize) {
61
6
		conv->wsize = minwsize;
62
6
		p = reallocarray(conv->wbuff, conv->wsize, sizeof(wchar_t));
63
6
		if (p == NULL) {
64
			conv->wsize = 0;
65
			free(conv->wbuff);
66
			conv->wbuff = NULL;
67
		} else
68
6
			conv->wbuff = p;
69
6
	}
70
21
}
71
72
73
char *
74
ct_encode_string(const wchar_t *s, ct_buffer_t *conv)
75
{
76
	char *dst;
77
	ssize_t used = 0;
78
79
12
	if (!s)
80
		return NULL;
81
6
	if (!conv->cbuff)
82
3
		ct_conv_buff_resize(conv, CT_BUFSIZ, 0);
83
6
	if (!conv->cbuff)
84
		return NULL;
85
86
	dst = conv->cbuff;
87
12
	while (*s) {
88
		used = conv->csize - (dst - conv->cbuff);
89
		if (used < 5) {
90
			used = dst - conv->cbuff;
91
			ct_conv_buff_resize(conv, conv->csize + CT_BUFSIZ, 0);
92
			if (!conv->cbuff)
93
				return NULL;
94
			dst = conv->cbuff + used;
95
		}
96
		used = ct_encode_char(dst, 5, *s);
97
		if (used == -1) /* failed to encode, need more buffer space */
98
			abort();
99
		++s;
100
		dst += used;
101
	}
102
6
	*dst = '\0';
103
6
	return conv->cbuff;
104
6
}
105
106
wchar_t *
107
ct_decode_string(const char *s, ct_buffer_t *conv)
108
{
109
	size_t len = 0;
110
111
12
	if (!s)
112
		return NULL;
113
6
	if (!conv->wbuff)
114
6
		ct_conv_buff_resize(conv, 0, CT_BUFSIZ);
115
6
	if (!conv->wbuff)
116
		return NULL;
117
118
6
	len = mbstowcs(NULL, s, 0);
119
6
	if (len == (size_t)-1)
120
		return NULL;
121
6
	if (len > conv->wsize)
122
		ct_conv_buff_resize(conv, 0, len + 1);
123
6
	if (!conv->wbuff)
124
		return NULL;
125
126
6
	mbstowcs(conv->wbuff, s, conv->wsize);
127
6
	return conv->wbuff;
128
6
}
129
130
131
protected wchar_t **
132
ct_decode_argv(int argc, const char *argv[], ct_buffer_t *conv)
133
{
134
	size_t bufspace;
135
	int i;
136
	wchar_t *p;
137
	wchar_t **wargv;
138
	size_t wlen;
139
140
	/* Make sure we have enough space in the conversion buffer to store all
141
	 * the argv strings. */
142
108
	for (i = 0, bufspace = 0; i < argc; ++i)
143
96
		bufspace += argv[i] ? strlen(argv[i]) + 1 : 0;
144
12
	ct_conv_buff_resize(conv, 0, bufspace * sizeof(*p));
145
12
	if (!conv->wsize)
146
		return NULL;
147
148
12
	wargv = reallocarray(NULL, argc, sizeof(*wargv));
149
150
96
	for (i = 0, p = conv->wbuff; i < argc; ++i) {
151
36
		if (!argv[i]) {   /* don't pass null pointers to mbstowcs */
152
12
			wargv[i] = NULL;
153
12
			continue;
154
		} else {
155
24
			wargv[i] = p;
156
24
			wlen = mbstowcs(p, argv[i], bufspace);
157
		}
158

48
		if (wlen == (size_t)-1 || wlen == bufspace) {
159
			/* Encoding error or not enough room for NUL. */
160
			free(wargv);
161
			return NULL;
162
		} else
163
24
			wlen++; /* include NUL in the count */
164
24
		bufspace -= wlen;
165
24
		p += wlen;
166
24
	}
167
168
12
	return wargv;
169
12
}
170
171
172
protected size_t
173
ct_enc_width(wchar_t c)
174
{
175
	/* UTF-8 encoding specific values */
176
	if (c < 0x80)
177
		return 1;
178
	else if (c < 0x0800)
179
		return 2;
180
	else if (c < 0x10000)
181
		return 3;
182
	else if (c < 0x110000)
183
		return 4;
184
	else
185
		return 0; /* not a valid codepoint */
186
}
187
188
protected ssize_t
189
ct_encode_char(char *dst, size_t len, wchar_t c)
190
{
191
	ssize_t l = 0;
192
	if (len < ct_enc_width(c))
193
		return -1;
194
	l = wctomb(dst, c);
195
196
	if (l < 0) {
197
		wctomb(NULL, L'\0');
198
		l = 0;
199
	}
200
	return l;
201
}
202
203
protected const wchar_t *
204
ct_visual_string(const wchar_t *s)
205
{
206
	static wchar_t *buff = NULL;
207
	static size_t buffsize = 0;
208
	void *p;
209
	wchar_t *dst;
210
	ssize_t used = 0;
211
212
	if (!s)
213
		return NULL;
214
	if (!buff) {
215
	    buffsize = CT_BUFSIZ;
216
	    buff = reallocarray(NULL, buffsize, sizeof(*buff));
217
	}
218
	dst = buff;
219
	while (*s) {
220
		used = ct_visual_char(dst, buffsize - (dst - buff), *s);
221
		if (used == -1) { /* failed to encode, need more buffer space */
222
			used = dst - buff;
223
			buffsize += CT_BUFSIZ;
224
			p = reallocarray(buff, buffsize, sizeof(*buff));
225
			if (p == NULL)
226
				goto out;
227
			buff = p;
228
			dst = buff + used;
229
			/* don't increment s here - we want to retry it! */
230
		}
231
		else
232
		    ++s;
233
		dst += used;
234
	}
235
	if (dst >= (buff + buffsize)) { /* sigh */
236
		buffsize += 1;
237
		p = reallocarray(buff, buffsize, sizeof(*buff));
238
		if (p == NULL)
239
			goto out;
240
		buff = p;
241
		dst = buff + buffsize - 1;
242
	}
243
	*dst = 0;
244
	return buff;
245
out:
246
	free(buff);
247
	buffsize = 0;
248
	return NULL;
249
}
250
251
252
253
protected int
254
ct_visual_width(wchar_t c)
255
{
256
	int t = ct_chr_class(c);
257
	int w;
258
	switch (t) {
259
	case CHTYPE_ASCIICTL:
260
		return 2; /* ^@ ^? etc. */
261
	case CHTYPE_TAB:
262
		return 1; /* Hmm, this really need to be handled outside! */
263
	case CHTYPE_NL:
264
		return 0; /* Should this be 1 instead? */
265
	case CHTYPE_PRINT:
266
		w = wcwidth(c);
267
		return (w == -1 ? 0 : w);
268
	case CHTYPE_NONPRINT:
269
		if (c > 0xffff) /* prefer standard 4-byte display over 5-byte */
270
			return 8; /* \U+12345 */
271
		else
272
			return 7; /* \U+1234 */
273
	default:
274
		return 0; /* should not happen */
275
	}
276
}
277
278
279
protected ssize_t
280
ct_visual_char(wchar_t *dst, size_t len, wchar_t c)
281
{
282
	int t = ct_chr_class(c);
283
	switch (t) {
284
	case CHTYPE_TAB:
285
	case CHTYPE_NL:
286
	case CHTYPE_ASCIICTL:
287
		if (len < 2)
288
			return -1;   /* insufficient space */
289
		*dst++ = '^';
290
		if (c == '\177')
291
			*dst = '?'; /* DEL -> ^? */
292
		else
293
			*dst = c | 0100;    /* uncontrolify it */
294
		return 2;
295
	case CHTYPE_PRINT:
296
		if (len < 1)
297
			return -1;  /* insufficient space */
298
		*dst = c;
299
		return 1;
300
	case CHTYPE_NONPRINT:
301
		/* we only use single-width glyphs for display,
302
		 * so this is right */
303
		if ((ssize_t)len < ct_visual_width(c))
304
			return -1;   /* insufficient space */
305
		*dst++ = '\\';
306
		*dst++ = 'U';
307
		*dst++ = '+';
308
#define tohexdigit(v) "0123456789ABCDEF"[v]
309
		if (c > 0xffff) /* prefer standard 4-byte display over 5-byte */
310
			*dst++ = tohexdigit(((unsigned int) c >> 16) & 0xf);
311
		*dst++ = tohexdigit(((unsigned int) c >> 12) & 0xf);
312
		*dst++ = tohexdigit(((unsigned int) c >>  8) & 0xf);
313
		*dst++ = tohexdigit(((unsigned int) c >>  4) & 0xf);
314
		*dst   = tohexdigit(((unsigned int) c      ) & 0xf);
315
		return (c > 0xffff) ? 8 : 7;
316
		/*FALLTHROUGH*/
317
	/* these two should be handled outside this function */
318
	default:            /* we should never hit the default */
319
		return 0;
320
	}
321
}
322
323
324
325
326
protected int
327
ct_chr_class(wchar_t c)
328
{
329
	if (c == '\t')
330
		return CHTYPE_TAB;
331
	else if (c == '\n')
332
		return CHTYPE_NL;
333
	else if (c < 0x100 && iswcntrl(c))
334
		return CHTYPE_ASCIICTL;
335
	else if (iswprint(c))
336
		return CHTYPE_PRINT;
337
	else
338
		return CHTYPE_NONPRINT;
339
}