1 /* Copyright (C) 1995-2016, 2020 Free Software Foundation, Inc.
2 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/types.h>
24
25 #include "loadinfo.h"
26
27 /* On some strange systems still no definition of NULL is found. Sigh! */
28 #ifndef NULL
29 # if defined __STDC__ && __STDC__
30 # define NULL ((void *) 0)
31 # else
32 # define NULL 0
33 # endif
34 #endif
35
36 /* @@ end of prolog @@ */
37
38
39 int
40 _nl_explode_name (char *name,
41 const char **language, const char **modifier,
42 const char **territory, const char **codeset,
43 const char **normalized_codeset)
44 {
45 char *cp;
46 int mask;
47
48 *modifier = NULL;
49 *territory = NULL;
50 *codeset = NULL;
51 *normalized_codeset = NULL;
52
53 /* Determine the individual parts of the locale name.
54 Accept the XPG syntax
55
56 language[_territory][.codeset][@modifier]
57
58 On AIX systems, also accept the same syntax with an uppercased language,
59 and a syntax similar to RFC 5646:
60
61 language[_script]_territory[.codeset]
62
63 where script is a four-letter code for a script, per ISO 15924.
64 */
65
66 mask = 0;
67
68 /* First look for the language. Termination symbols are `_', '.', and `@'. */
69 *language = name;
70
71 cp = name;
72 while (cp[0] != '\0' && cp[0] != '_' && cp[0] != '@' && cp[0] != '.')
73 ++cp;
74
75 if (cp == name)
76 /* This does not make sense: language has to be specified. Use
77 this entry as it is without exploding. Perhaps it is an alias. */
78 cp = strchr (name, '\0');
79 else
80 {
81 if (cp[0] == '_')
82 {
83 *cp++ = '\0';
84 #if defined _AIX
85 /* Lowercase the language. */
86 {
87 char *lcp;
88
89 for (lcp = name; lcp < cp; lcp++)
90 if (*lcp >= 'A' && *lcp <= 'Z')
91 *lcp += 'a' - 'A';
92 }
93
94 /* Next is the script or the territory. It depends on whether
95 there is another '_'. */
96 char *next = cp;
97
98 while (cp[0] != '\0' && cp[0] != '_' && cp[0] != '@' && cp[0] != '.')
99 ++cp;
100
101 if (cp[0] == '_')
102 {
103 *cp++ = '\0';
104
105 /* Next is the script. Translate the script to a modifier.
106 We don't need to support all of ISO 15924 here, only those
107 scripts that actually occur:
108 Latn -> latin
109 Cyrl -> cyrillic
110 Guru -> gurmukhi
111 Hans -> (omitted, redundant with the territory CN or SG)
112 Hant -> (omitted, redundant with the territory TW or HK) */
113 if (strcmp (next, "Latn") == 0)
114 *modifier = "latin";
115 else if (strcmp (next, "Cyrl") == 0)
116 *modifier = "cyrillic";
117 else if (strcmp (next, "Guru") == 0)
118 *modifier = "gurmukhi";
119 else if (!(strcmp (next, "Hans") == 0
120 || strcmp (next, "Hant") == 0))
121 *modifier = next;
122 if (*modifier != NULL && (*modifier)[0] != '\0')
123 mask |= XPG_MODIFIER;
124 }
125 else
126 cp = next;
127 #endif
128
129 /* Next is the territory. */
130 *territory = cp;
131
132 while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@')
133 ++cp;
134
135 mask |= XPG_TERRITORY;
136 }
137
138 if (cp[0] == '.')
139 {
140 /* Next is the codeset. */
141 *cp++ = '\0';
142 *codeset = cp;
143
144 while (cp[0] != '\0' && cp[0] != '@')
145 ++cp;
146
147 mask |= XPG_CODESET;
148
149 if (*codeset != cp && (*codeset)[0] != '\0')
150 {
151 *normalized_codeset = _nl_normalize_codeset (*codeset,
152 cp - *codeset);
153 if (*normalized_codeset == NULL)
154 return -1;
155 else if (strcmp (*codeset, *normalized_codeset) == 0)
156 free ((char *) *normalized_codeset);
157 else
158 mask |= XPG_NORM_CODESET;
159 }
160 }
161
162 if (cp[0] == '@')
163 {
164 /* Next is the modifier. */
165 *cp++ = '\0';
166 *modifier = cp;
167
168 if (cp[0] != '\0')
169 mask |= XPG_MODIFIER;
170 }
171 }
172
173 if (*territory != NULL && (*territory)[0] == '\0')
174 mask &= ~XPG_TERRITORY;
175
176 if (*codeset != NULL && (*codeset)[0] == '\0')
177 mask &= ~XPG_CODESET;
178
179 return mask;
180 }