add locale framework
[oweals/musl.git] / 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         if (!libc.global_locale.messages_name) {
13                 libc.global_locale.messages_name =
14                         buf + 2 + 3*(LOCALE_NAME_MAX+1);
15         }
16
17         if ((unsigned)cat > LC_ALL) return 0;
18
19         /* For LC_ALL, setlocale is required to return a string which
20          * encodes the current setting for all categories. The format of
21          * this string is unspecified, and only the following code, which
22          * performs both the serialization and deserialization, depends
23          * on the format, so it can easily be changed if needed. */
24         if (cat == LC_ALL) {
25                 if (name) {
26                         char part[LOCALE_NAME_MAX+1];
27                         int i, j;
28                         if (name[0] && name[1]==';'
29                             && strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) {
30                                 part[0] = name[0];
31                                 part[1] = 0;
32                                 setlocale(LC_CTYPE, part);
33                                 part[LOCALE_NAME_MAX] = 0;
34                                 for (i=LC_TIME; i<LC_MESSAGES; i++) {
35                                         memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX);
36                                         for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--)
37                                                 part[j] = 0;
38                                         setlocale(i, part);
39                                 }
40                                 setlocale(LC_MESSAGES, name + 2 + 3*(LOCALE_NAME_MAX+1));
41                         } else {
42                                 for (i=0; i<LC_ALL; i++)
43                                         setlocale(i, name);
44                         }
45                 }
46                 memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1));
47                 buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C';
48                 return buf;
49         }
50
51         if (name) {
52                 int adj = libc.global_locale.ctype_utf8;
53                 __setlocalecat(&libc.global_locale, cat, name);
54                 adj -= libc.global_locale.ctype_utf8;
55                 if (adj) a_fetch_add(&libc.bytelocale_cnt_minus_1, adj);
56         }
57
58         switch (cat) {
59         case LC_CTYPE:
60                 return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C";
61         case LC_MESSAGES:
62                 return libc.global_locale.messages_name[0]
63                         ? libc.global_locale.messages_name : "C";
64         default:
65                 return "C";
66         }
67 }