implement locale file loading and state for remaining locale categories
[musl] / src / locale / setlocale.c
1 #include <locale.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "locale_impl.h"
5 #include "libc.h"
6 #include "atomic.h"
7
8 static char buf[2+4*(LOCALE_NAME_MAX+1)];
9
10 char *setlocale(int cat, const char *name)
11 {
12         struct __locale_map *lm;
13         int i, j;
14
15         if (!libc.global_locale.messages_name) {
16                 libc.global_locale.messages_name =
17                         buf + 2 + 3*(LOCALE_NAME_MAX+1);
18         }
19
20         if ((unsigned)cat > LC_ALL) return 0;
21
22         /* For LC_ALL, setlocale is required to return a string which
23          * encodes the current setting for all categories. The format of
24          * this string is unspecified, and only the following code, which
25          * performs both the serialization and deserialization, depends
26          * on the format, so it can easily be changed if needed. */
27         if (cat == LC_ALL) {
28                 if (name) {
29                         char part[LOCALE_NAME_MAX+1];
30                         if (name[0] && name[1]==';'
31                             && strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) {
32                                 part[0] = name[0];
33                                 part[1] = 0;
34                                 setlocale(LC_CTYPE, part);
35                                 part[LOCALE_NAME_MAX] = 0;
36                                 for (i=LC_TIME; i<LC_MESSAGES; i++) {
37                                         memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX);
38                                         for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--)
39                                                 part[j] = 0;
40                                         setlocale(i, part);
41                                 }
42                                 setlocale(LC_MESSAGES, name + 2 + 3*(LOCALE_NAME_MAX+1));
43                         } else {
44                                 for (i=0; i<LC_ALL; i++)
45                                         setlocale(i, name);
46                         }
47                 }
48                 memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1));
49                 buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C';
50                 for (i=LC_TIME; i<LC_MESSAGES; i++) {
51                         lm = libc.global_locale.cat[i-2];
52                         if (lm) memcpy(buf + 2 + (i-2)*(LOCALE_NAME_MAX+1),
53                                 lm->name, strlen(lm->name));
54                 }
55                 return buf;
56         }
57
58         if (name) {
59                 int adj = libc.global_locale.ctype_utf8;
60                 __setlocalecat(&libc.global_locale, cat, name);
61                 adj -= libc.global_locale.ctype_utf8;
62                 if (adj) a_fetch_add(&libc.bytelocale_cnt_minus_1, adj);
63         }
64
65         switch (cat) {
66         case LC_CTYPE:
67                 return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C";
68         case LC_NUMERIC:
69                 return "C";
70         case LC_MESSAGES:
71                 return libc.global_locale.messages_name[0]
72                         ? libc.global_locale.messages_name : "C";
73         default:
74                 lm = libc.global_locale.cat[cat-2];
75                 return lm ? lm->name : "C";
76         }
77 }