]>
Commit | Line | Data |
---|---|---|
933e73fa RM |
1 | /* Functions to read locale data files. |
2 | Copyright (C) 1995 Free Software Foundation, Inc. | |
3 | This file is part of the GNU C Library. | |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Library General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Library General Public | |
16 | License along with the GNU C Library; see the file COPYING.LIB. If | |
17 | not, write to the Free Software Foundation, Inc., 675 Mass Ave, | |
18 | Cambridge, MA 02139, USA. */ | |
19 | ||
20 | #include <errno.h> | |
21 | #include <fcntl.h> | |
22 | #include <unistd.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <stdio.h> | |
26 | #include <sys/stat.h> | |
27 | #include <sys/mman.h> | |
28 | #include "localeinfo.h" | |
29 | ||
30 | const size_t _nl_category_num_items[] = | |
31 | { | |
32 | #define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \ | |
f0bf9cb9 | 33 | [category] = _NL_ITEM_INDEX (_NL_NUM_##category), |
933e73fa RM |
34 | #include "categories.def" |
35 | #undef DEFINE_CATEGORY | |
36 | }; | |
37 | ||
38 | struct locale_data * | |
39 | _nl_load_locale (int category, char **name) | |
40 | { | |
933e73fa RM |
41 | int fd; |
42 | struct | |
43 | { | |
44 | unsigned int magic; | |
45 | unsigned int nstrings; | |
46 | unsigned int strindex[0]; | |
47 | } *filedata; | |
48 | struct stat st; | |
49 | struct locale_data *newdata; | |
50 | int swap = 0; | |
51 | inline unsigned int SWAP (const unsigned int *inw) | |
52 | { | |
53 | const unsigned char *inc = (const unsigned char *) inw; | |
54 | if (!swap) | |
55 | return *inw; | |
56 | return (inc[3] << 24) | (inc[2] << 16) | (inc[1] << 8) | inc[0]; | |
57 | } | |
58 | unsigned int i; | |
59 | ||
60 | if ((*name)[0] == '\0') | |
61 | { | |
f0bf9cb9 RM |
62 | *name = getenv ("LC_ALL"); |
63 | if (! *name || (*name)[0] == '\0') | |
64 | *name = getenv (_nl_category_names[category]); | |
65 | if (! *name || (*name)[0] == '\0') | |
933e73fa | 66 | *name = getenv ("LANG"); |
f0bf9cb9 | 67 | if (! *name || (*name)[0] == '\0') |
933e73fa RM |
68 | *name = (char *) "local"; |
69 | } | |
70 | ||
49e522bf | 71 | { |
49e522bf RM |
72 | const char *catname = _nl_category_names[category]; |
73 | size_t namelen = strlen (*name); | |
74 | size_t catlen = strlen (catname); | |
2b83a2a4 RM |
75 | char file[sizeof LOCALE_PATH + 1 + namelen + catlen * 2 + 4]; |
76 | if (strchr (*name, '/') != NULL) | |
77 | sprintf (file, "%s/%s", *name, catname); | |
78 | else | |
79 | sprintf (file, "%s/%s/%s", LOCALE_PATH, *name, catname); | |
49e522bf RM |
80 | fd = __open (file, O_RDONLY); |
81 | if (fd < 0) | |
82 | return NULL; | |
83 | if (__fstat (fd, &st) < 0) | |
84 | goto puntfd; | |
85 | if (S_ISDIR (st.st_mode)) | |
86 | { | |
1474b80f RM |
87 | /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo |
88 | instead. */ | |
49e522bf RM |
89 | __close (fd); |
90 | memcpy (stpcpy (strchr (file, '\0'), "SYS_"), catname, catlen); | |
91 | fd = __open (file, O_RDONLY); | |
92 | if (fd < 0) | |
93 | return NULL; | |
94 | if (__fstat (fd, &st) < 0) | |
95 | goto puntfd; | |
96 | } | |
97 | } | |
933e73fa RM |
98 | |
99 | { | |
100 | /* Map in the file's data. */ | |
101 | int save = errno; | |
df4999e1 RM |
102 | #ifndef MAP_COPY |
103 | /* Linux seems to lack read-only copy-on-write. */ | |
104 | #define MAP_COPY MAP_PRIVATE | |
2f8033d6 RM |
105 | #endif |
106 | #ifndef MAP_FILE | |
107 | /* Some systems do not have this flag; it is superfluous. */ | |
108 | #define MAP_FILE 0 | |
883bc19b RM |
109 | #endif |
110 | #ifndef MAP_INHERIT | |
111 | /* Some systems might lack this; they lose. */ | |
112 | #define MAP_INHERIT 0 | |
df4999e1 | 113 | #endif |
933e73fa | 114 | filedata = (void *) __mmap ((caddr_t) 0, st.st_size, |
883bc19b RM |
115 | PROT_READ, MAP_FILE|MAP_COPY|MAP_INHERIT, |
116 | fd, 0); | |
933e73fa RM |
117 | if (filedata == (void *) -1) |
118 | { | |
119 | if (errno == ENOSYS) | |
120 | { | |
121 | /* No mmap; allocate a buffer and read from the file. */ | |
122 | filedata = malloc (st.st_size); | |
123 | if (filedata) | |
124 | { | |
125 | off_t to_read = st.st_size; | |
126 | ssize_t nread; | |
127 | char *p = (char *) filedata; | |
128 | while (to_read > 0) | |
129 | { | |
130 | nread = __read (fd, p, to_read); | |
131 | if (nread <= 0) | |
132 | { | |
133 | free (filedata); | |
134 | if (nread == 0) | |
135 | errno = EINVAL; /* Bizarreness going on. */ | |
136 | goto puntfd; | |
137 | } | |
138 | p += nread; | |
139 | to_read -= nread; | |
140 | } | |
141 | } | |
142 | else | |
143 | goto puntfd; | |
144 | errno = save; | |
145 | } | |
146 | else | |
147 | goto puntfd; | |
148 | } | |
149 | } | |
150 | ||
151 | if (filedata->magic == LIMAGIC (category)) | |
152 | /* Good data file in our byte order. */ | |
153 | swap = 0; | |
154 | else | |
155 | { | |
156 | /* Try the other byte order. */ | |
157 | swap = 1; | |
158 | if (SWAP (&filedata->magic) != LIMAGIC (category)) | |
159 | /* Bad data file in either byte order. */ | |
160 | { | |
161 | puntmap: | |
162 | __munmap ((caddr_t) filedata, st.st_size); | |
163 | puntfd: | |
164 | __close (fd); | |
165 | return NULL; | |
166 | } | |
167 | } | |
168 | ||
169 | #define W(word) SWAP (&(word)) | |
170 | ||
171 | if (W (filedata->nstrings) < _nl_category_num_items[category] || | |
172 | (sizeof *filedata + W (filedata->nstrings) * sizeof (unsigned int) | |
173 | >= st.st_size)) | |
174 | { | |
175 | /* Insufficient data. */ | |
176 | errno = EINVAL; | |
177 | goto puntmap; | |
178 | } | |
179 | ||
180 | newdata = malloc (sizeof *newdata + | |
181 | W (filedata->nstrings) * sizeof (char *)); | |
182 | if (! newdata) | |
183 | goto puntmap; | |
184 | ||
185 | newdata->filedata = (void *) filedata; | |
186 | newdata->filesize = st.st_size; | |
187 | newdata->nstrings = W (filedata->nstrings); | |
188 | for (i = 0; i < newdata->nstrings; ++i) | |
189 | { | |
190 | unsigned int idx = W (filedata->strindex[i]); | |
191 | if (idx >= newdata->filesize) | |
192 | { | |
193 | free (newdata); | |
194 | errno = EINVAL; | |
195 | goto puntmap; | |
196 | } | |
197 | newdata->strings[i] = newdata->filedata + idx; | |
198 | } | |
199 | ||
f0bf9cb9 | 200 | __close (fd); |
933e73fa RM |
201 | return newdata; |
202 | } | |
203 | \f | |
204 | void | |
205 | _nl_free_locale (struct locale_data *data) | |
206 | { | |
207 | int save = errno; | |
8b575de1 RM |
208 | if (! data) |
209 | /* Ignore a null pointer, like free does. */ | |
210 | return; | |
933e73fa RM |
211 | if (__munmap ((caddr_t) data->filedata, data->filesize) < 0) |
212 | { | |
213 | if (errno == ENOSYS) | |
214 | free ((void *) data->filedata); | |
215 | errno = save; | |
216 | } | |
217 | free (data); | |
218 | } | |
219 |