Update.
[platform/upstream/glibc.git] / locale / loadlocale.c
1 /* Functions to read locale data files.
2    Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #ifdef _POSIX_MAPPED_FILES
28 # include <sys/mman.h>
29 #endif
30 #include <sys/stat.h>
31
32 #include "localeinfo.h"
33
34
35 static const size_t _nl_category_num_items[] =
36 {
37 #define DEFINE_CATEGORY(category, category_name, items, a) \
38   [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
39 #include "categories.def"
40 #undef  DEFINE_CATEGORY
41 };
42
43
44 #define NO_PAREN(arg, rest...) arg, ##rest
45
46 #define DEFINE_CATEGORY(category, category_name, items, a) \
47 static const enum value_type _nl_value_type_##category[] = { NO_PAREN items };
48 #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
49   [_NL_ITEM_INDEX (element)] = type,
50 #include "categories.def"
51 #undef DEFINE_CATEGORY
52
53 static const enum value_type *_nl_value_types[] =
54 {
55 #define DEFINE_CATEGORY(category, category_name, items, a) \
56   [category] = _nl_value_type_##category,
57 #include "categories.def"
58 #undef DEFINE_CATEGORY
59 };
60
61
62 void
63 _nl_load_locale (struct loaded_l10nfile *file, int category)
64 {
65   int fd;
66   struct
67     {
68       unsigned int magic;
69       unsigned int nstrings;
70       unsigned int strindex[0];
71     } *filedata;
72   struct stat st;
73   struct locale_data *newdata;
74   int save_err;
75   int mmaped = 1;
76   size_t cnt;
77
78   file->decided = 1;
79   file->data = NULL;
80
81   fd = __open (file->filename, O_RDONLY);
82   if (__builtin_expect (fd, 0) < 0)
83     /* Cannot open the file.  */
84     return;
85
86   if (__builtin_expect (__fstat (fd, &st), 0) < 0)
87     goto puntfd;
88   if (__builtin_expect (S_ISDIR (st.st_mode), 0))
89     {
90       /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
91            instead.  */
92       char *newp;
93       size_t filenamelen;
94
95       __close (fd);
96
97       filenamelen = strlen (file->filename);
98       newp = (char *) alloca (filenamelen
99                               + 5 + _nl_category_name_sizes[category] + 1);
100       __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
101                             "/SYS_", 5),
102                  _nl_category_names[category],
103                  _nl_category_name_sizes[category] + 1);
104
105       fd = __open (newp, O_RDONLY);
106       if (__builtin_expect (fd, 0) < 0)
107         return;
108
109       if (__builtin_expect (__fstat (fd, &st), 0) < 0)
110         goto puntfd;
111     }
112
113   /* Map in the file's data.  */
114   save_err = errno;
115 #ifdef _POSIX_MAPPED_FILES
116 # ifndef MAP_COPY
117   /* Linux seems to lack read-only copy-on-write.  */
118 #  define MAP_COPY MAP_PRIVATE
119 # endif
120 # ifndef MAP_FILE
121   /* Some systems do not have this flag; it is superfluous.  */
122 #  define MAP_FILE 0
123 # endif
124 # ifndef MAP_INHERIT
125   /* Some systems might lack this; they lose.  */
126 #  define MAP_INHERIT 0
127 # endif
128   filedata = (void *) __mmap ((caddr_t) 0, st.st_size, PROT_READ,
129                               MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0);
130   if (__builtin_expect ((void *) filedata != MAP_FAILED, 1))
131     {
132       if (__builtin_expect (st.st_size < sizeof (*filedata), 0))
133         /* This cannot be a locale data file since it's too small.  */
134         goto puntfd;
135     }
136   else
137     {
138       if (__builtin_expect (errno, ENOSYS) == ENOSYS)
139         {
140 #endif  /* _POSIX_MAPPED_FILES */
141           /* No mmap; allocate a buffer and read from the file.  */
142           mmaped = 0;
143           filedata = malloc (st.st_size);
144           if (filedata != NULL)
145             {
146               off_t to_read = st.st_size;
147               ssize_t nread;
148               char *p = (char *) filedata;
149               while (to_read > 0)
150                 {
151                   nread = __read (fd, p, to_read);
152                   if (__builtin_expect (nread, 1) <= 0)
153                     {
154                       free (filedata);
155                       if (nread == 0)
156                         __set_errno (EINVAL); /* Bizarreness going on.  */
157                       goto puntfd;
158                     }
159                   p += nread;
160                   to_read -= nread;
161                 }
162             }
163           else
164             goto puntfd;
165           __set_errno (save_err);
166 #ifdef _POSIX_MAPPED_FILES
167         }
168       else
169         goto puntfd;
170     }
171 #endif  /* _POSIX_MAPPED_FILES */
172
173   if (__builtin_expect (filedata->magic != LIMAGIC (category), 0))
174     /* Bad data file in either byte order.  */
175     {
176     puntmap:
177 #ifdef _POSIX_MAPPED_FILES
178       __munmap ((caddr_t) filedata, st.st_size);
179 #endif
180     puntfd:
181       __close (fd);
182       return;
183     }
184
185   if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
186                         0)
187       || (__builtin_expect (sizeof *filedata
188                             + filedata->nstrings * sizeof (unsigned int)
189                             >= (size_t) st.st_size, 0)))
190     {
191       /* Insufficient data.  */
192       __set_errno (EINVAL);
193       goto puntmap;
194     }
195
196   newdata = malloc (sizeof *newdata
197                     + filedata->nstrings * sizeof (union locale_data_value));
198   if (newdata == NULL)
199     goto puntmap;
200
201   newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
202   newdata->filedata = (void *) filedata;
203   newdata->filesize = st.st_size;
204   newdata->mmaped = mmaped;
205   newdata->usage_count = 0;
206   newdata->use_translit = 0;
207   newdata->options = NULL;
208   newdata->nstrings = filedata->nstrings;
209   for (cnt = 0; cnt < newdata->nstrings; ++cnt)
210     {
211       off_t idx = filedata->strindex[cnt];
212       if (__builtin_expect (idx > newdata->filesize, 0))
213         {
214           free (newdata);
215           __set_errno (EINVAL);
216           goto puntmap;
217         }
218       if (__builtin_expect (_nl_value_types[category][cnt] == word, 0))
219         {
220           assert (idx % 4 == 0);
221           newdata->values[cnt].word =
222             *((u_int32_t *) (newdata->filedata + idx));
223         }
224       else
225         newdata->values[cnt].string = newdata->filedata + idx;
226     }
227
228   __close (fd);
229   file->data = newdata;
230 }
231
232 void
233 _nl_unload_locale (struct locale_data *locale)
234 {
235 #ifdef _POSIX_MAPPED_FILES
236   if (__builtin_expect (locale->mmaped, 1))
237     __munmap ((caddr_t) locale->filedata, locale->filesize);
238   else
239 #endif
240     free ((void *) locale->filedata);
241
242   free ((char *) locale->options);
243   free (locale);
244 }