aboutsummaryrefslogtreecommitdiffstats
path: root/libcharconv_control_character_representations.c
blob: 57c1c935fc49a5dc64c64d6c69439b2babec06af (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/* See LICENSE file for copyright and license details. */
#include "lib-common.h"
#include <string.h>


static const char *texts[] = {
	"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
	"BS",  "HT",  "LF", " VT",  "FF",  "CR",  "SS",  "SI",
	"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
	"CAN", "EM",  "SUB", "ESC", "FS",  "GS",  "RS",  "US",
	"SP",  "DEL"
};


enum libcharconv_result
libcharconv_control_character_representations(const char *s, size_t slen, size_t *n, uint_least32_t *cp, size_t *ncp)
{
	size_t i, len, found, found_len;
	int indeterminate;
	*n = 0;
	for (; slen; s++, slen--, ++*n) {
		if (*(const unsigned char *)s <= ' ') {
			found = *(const unsigned char *)s;
			goto conv_byte;
		} else if (*(const unsigned char *)s == 0x7Fu) {
			found = 0x21u;
			goto conv_byte;
		}
		indeterminate = 0;
		found = SIZE_MAX;
		found_len = 0u;
		for (i = 0u; i < sizeof(texts) / sizeof(*texts); i++) {
			len = strlen(texts[i]);
			if (strncmp(s, texts[i], len < slen ? len : slen))
				continue;
			if (slen < len) {
				indeterminate = 1;
				continue;
			}
			if (len > found_len) {
				found = i;
				found_len = len;
			}
		}
		if (found_len)
			goto conv;
		if (*n)
			goto no_conv;
		if (indeterminate)
			return LIBCHARCONV_INDETERMINATE;
	}
no_conv:
	return LIBCHARCONV_NO_CONVERT;

conv_byte:
	found_len = 1u;
conv:
	if (*n)
		goto no_conv;
	if (*ncp)
		*cp = (uint_least32_t)UINT32_C(0x2400) | (uint_least32_t)found;
	*n += found_len;
	*ncp = 1u;
	return indeterminate ? LIBCHARCONV_CONVERT_IF_END : LIBCHARCONV_CONVERTED;
}