GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/locale/setlocale.c Lines: 36 82 43.9 %
Date: 2017-11-07 Branches: 20 70 28.6 %

Line Branch Exec Source
1
/*	$OpenBSD: setlocale.c,v 1.27 2017/09/05 03:16:13 schwarze Exp $	*/
2
/*
3
 * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <locale.h>
19
#include <stdlib.h>
20
#include <string.h>
21
22
#include "rune.h"
23
24
static void
25
freegl(char **oldgl)
26
{
27
	int ic;
28
29
7452
	if (oldgl == NULL)
30
3726
		return;
31
	for (ic = LC_ALL; ic < _LC_LAST; ic++)
32
		free(oldgl[ic]);
33
	free(oldgl);
34
3726
}
35
36
static char **
37
dupgl(char **oldgl)
38
{
39
	char **newgl;
40
	int ic;
41
42
7452
	if ((newgl = calloc(_LC_LAST, sizeof(*newgl))) == NULL)
43
		return NULL;
44
59616
	for (ic = LC_ALL; ic < _LC_LAST; ic++) {
45

104328
		if ((newgl[ic] = strdup(oldgl != NULL ?
46
52164
		    oldgl[ic] : ic == LC_ALL ? "" : "C")) == NULL) {
47
			freegl(newgl);
48
			return NULL;
49
		}
50
	}
51
3726
	return newgl;
52
3726
}
53
54
static int
55
changegl(int category, const char *locname, char **gl)
56
{
57
	char *cp;
58
59

11178
	if ((locname = _get_locname(category, locname)) == NULL ||
60
3726
	    (cp = strdup(locname)) == NULL)
61
		return -1;
62
63
3726
	free(gl[category]);
64
3726
	gl[category] = cp;
65
3726
	return 0;
66
3726
}
67
68
char *
69
setlocale(int category, const char *locname)
70
{
71
	/*
72
	 * Even though only LC_CTYPE has any effect in the OpenBSD
73
	 * base system, store complete information about the global
74
	 * locale, such that third-party software can access it,
75
	 * both via setlocale(3) and via locale(1).
76
	 */
77
	static char	  global_locname[256];
78
	static char	**global_locale;
79
80
7452
	char **newgl, *firstname, *nextname;
81
	int ic;
82
83
3726
	if (category < LC_ALL || category >= _LC_LAST)
84
		return NULL;
85
86
	/*
87
	 * Change the global locale.
88
	 */
89
3726
	if (locname != NULL) {
90
3726
		if ((newgl = dupgl(global_locale)) == NULL)
91
			return NULL;
92

3726
		if (category == LC_ALL && strchr(locname, '/') != NULL) {
93
94
			/* One value for each category. */
95
			if ((firstname = strdup(locname)) == NULL)
96
				return NULL;
97
			nextname = firstname;
98
			for (ic = 1; ic < _LC_LAST; ic++)
99
				if (nextname == NULL || changegl(ic,
100
				    strsep(&nextname, "/"), newgl) == -1)
101
					break;
102
			free(firstname);
103
			if (ic < _LC_LAST || nextname != NULL) {
104
				freegl(newgl);
105
				return NULL;
106
			}
107
		} else {
108
109
			/* One value only. */
110
3726
			if (changegl(category, locname, newgl) == -1) {
111
				freegl(newgl);
112
				return NULL;
113
			}
114
115
			/* One common value for all categories. */
116
3726
			if (category == LC_ALL) {
117
				for (ic = 1; ic < _LC_LAST; ic++) {
118
					if (changegl(ic, locname,
119
					    newgl) == -1) {
120
						freegl(newgl);
121
						return NULL;
122
					}
123
				}
124
			}
125
		}
126
	} else
127
		newgl = global_locale;
128
129
	/*
130
	 * Assemble a string representation of the globale locale.
131
	 */
132
133
	/* setlocale(3) was never called with a non-NULL argument. */
134
3726
	if (newgl == NULL) {
135
		(void)strlcpy(global_locname, "C", sizeof(global_locname));
136
		goto done;
137
	}
138
139
	/* Individual category. */
140
3726
	if (category > LC_ALL) {
141
7452
		if (strlcpy(global_locname, newgl[category],
142
3726
		    sizeof(global_locname)) >= sizeof(global_locname))
143
			global_locname[0] = '\0';
144
		goto done;
145
	}
146
147
	/* LC_ALL overrides everything else. */
148
	if (newgl[LC_ALL][0] != '\0') {
149
		if (strlcpy(global_locname, newgl[LC_ALL],
150
		    sizeof(global_locname)) >= sizeof(global_locname))
151
			global_locname[0] = '\0';
152
		goto done;
153
	}
154
155
	/*
156
	 * Check whether all categories agree and return either
157
	 * the single common name for all categories or a string
158
	 * listing the names for all categories.
159
	 */
160
	for (ic = 2; ic < _LC_LAST; ic++)
161
		if (strcmp(newgl[ic], newgl[1]) != 0)
162
			break;
163
	if (ic == _LC_LAST) {
164
		if (strlcpy(global_locname, newgl[1],
165
		    sizeof(global_locname)) >= sizeof(global_locname))
166
			global_locname[0] = '\0';
167
	} else {
168
		ic = snprintf(global_locname, sizeof(global_locname),
169
		    "%s/%s/%s/%s/%s/%s", newgl[1], newgl[2], newgl[3],
170
		    newgl[4], newgl[5], newgl[6]);
171
		if (ic == -1 || ic >= sizeof(global_locname))
172
			global_locname[0] = '\0';
173
	}
174
175
done:
176
3726
	if (locname != NULL) {
177
		/*
178
		 * We can't replace the global locale earlier
179
		 * because we first have to make sure that we
180
		 * also have the memory required to report success.
181
		 */
182
3726
		if (global_locname[0] != '\0') {
183
3726
			freegl(global_locale);
184
3726
			global_locale = newgl;
185
3726
			if (category == LC_ALL || category == LC_CTYPE)
186
3726
				_GlobalRuneLocale =
187
7452
				    strchr(global_locname, '.') == NULL ?
188
3726
				    &_DefaultRuneLocale : _Utf8RuneLocale;
189
		} else {
190
			freegl(newgl);
191
			return NULL;
192
		}
193
	}
194
3726
	return global_locname;
195
3726
}
196
DEF_STRONG(setlocale);