Update.
[platform/upstream/glibc.git] / locale / loadlocale.c
1 /* Functions to read locale data files.
2    Copyright (C) 1996, 1997, 1998, 1999 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 <errno.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/mman.h>
27 #include <sys/stat.h>
28
29 #include "localeinfo.h"
30
31
32 static const size_t _nl_category_num_items[] =
33 {
34 #define DEFINE_CATEGORY(category, category_name, items, a) \
35   [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
36 #include "categories.def"
37 #undef  DEFINE_CATEGORY
38 };
39
40
41 #define NO_PAREN(arg, rest...) arg, ##rest
42
43 #define DEFINE_CATEGORY(category, category_name, items, a) \
44 static const enum value_type _nl_value_type_##category[] = { NO_PAREN items };
45 #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
46   [_NL_ITEM_INDEX (element)] = type,
47 #include "categories.def"
48 #undef DEFINE_CATEGORY
49
50 static const enum value_type *_nl_value_types[] =
51 {
52 #define DEFINE_CATEGORY(category, category_name, items, a) \
53   [category] = _nl_value_type_##category,
54 #include "categories.def"
55 #undef DEFINE_CATEGORY
56 };
57
58
59 void
60 _nl_load_locale (struct loaded_l10nfile *file, int category)
61 {
62   int fd;
63   struct
64     {
65       unsigned int magic;
66       unsigned int nstrings;
67       unsigned int strindex[0];
68     } *filedata;
69   struct stat st;
70   struct locale_data *newdata;
71   int save_err;
72   int mmaped = 1;
73   size_t cnt;
74
75   file->decided = 1;
76   file->data = NULL;
77
78   fd = __open (file->filename, O_RDONLY);
79   if (fd < 0)
80     /* Cannot open the file.  */
81     return;
82
83   if (__fstat (fd, &st) < 0)
84     goto puntfd;
85   if (S_ISDIR (st.st_mode))
86     {
87       /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
88            instead.  */
89       char *newp;
90
91       __close (fd);
92
93       newp = (char *) alloca (strlen (file->filename)
94                               + 5 + _nl_category_name_sizes[category] + 1);
95       __stpcpy (__stpcpy (__stpcpy (newp, file->filename), "/SYS_"),
96                 _nl_category_names[category]);
97
98       fd = __open (newp, O_RDONLY);
99       if (fd < 0)
100         return;
101
102       if (__fstat (fd, &st) < 0)
103         goto puntfd;
104     }
105
106   /* Map in the file's data.  */
107   save_err = errno;
108 #ifndef MAP_COPY
109   /* Linux seems to lack read-only copy-on-write.  */
110 #define MAP_COPY MAP_PRIVATE
111 #endif
112 #ifndef MAP_FILE
113   /* Some systems do not have this flag; it is superfluous.  */
114 #define MAP_FILE 0
115 #endif
116 #ifndef MAP_INHERIT
117   /* Some systems might lack this; they lose.  */
118 #define MAP_INHERIT 0
119 #endif
120   filedata = (void *) __mmap ((caddr_t) 0, st.st_size, PROT_READ,
121                               MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0);
122   if ((void *) filedata == MAP_FAILED)
123     {
124       if (errno == ENOSYS)
125         {
126           /* No mmap; allocate a buffer and read from the file.  */
127           mmaped = 0;
128           filedata = malloc (st.st_size);
129           if (filedata != NULL)
130             {
131               off_t to_read = st.st_size;
132               ssize_t nread;
133               char *p = (char *) filedata;
134               while (to_read > 0)
135                 {
136                   nread = __read (fd, p, to_read);
137                   if (nread <= 0)
138                     {
139                       free (filedata);
140                       if (nread == 0)
141                         __set_errno (EINVAL); /* Bizarreness going on.  */
142                       goto puntfd;
143                     }
144                   p += nread;
145                   to_read -= nread;
146                 }
147             }
148           else
149             goto puntfd;
150           __set_errno (save_err);
151         }
152       else
153         goto puntfd;
154     }
155   else if (st.st_size < sizeof (*filedata))
156     /* This cannot be a locale data file since it's too small.  */
157     goto puntfd;
158
159   if (filedata->magic != LIMAGIC (category))
160     /* Bad data file in either byte order.  */
161     {
162     puntmap:
163       __munmap ((caddr_t) filedata, st.st_size);
164     puntfd:
165       __close (fd);
166       return;
167     }
168
169   if (filedata->nstrings < _nl_category_num_items[category] ||
170       (sizeof *filedata + filedata->nstrings * sizeof (unsigned int)
171        >= (size_t) st.st_size))
172     {
173       /* Insufficient data.  */
174       __set_errno (EINVAL);
175       goto puntmap;
176     }
177
178   newdata = malloc (sizeof *newdata
179                     + (_nl_category_num_items[category]
180                        * sizeof (union locale_data_value)));
181   if (! newdata)
182     goto puntmap;
183
184   newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
185   newdata->filedata = (void *) filedata;
186   newdata->filesize = st.st_size;
187   newdata->mmaped = mmaped;
188   newdata->usage_count = 0;
189   newdata->nstrings = _nl_category_num_items[category];
190   for (cnt = 0; cnt < newdata->nstrings; ++cnt)
191     {
192       off_t idx = filedata->strindex[cnt];
193       if (idx >= newdata->filesize)
194         {
195           free (newdata);
196           __set_errno (EINVAL);
197           goto puntmap;
198         }
199       if (_nl_value_types[category][cnt] == word)
200         newdata->values[cnt].word = *((u_int32_t *) (newdata->filedata + idx));
201       else
202         newdata->values[cnt].string = newdata->filedata + idx;
203     }
204
205   __close (fd);
206   file->data = newdata;
207 }
208
209 void
210 _nl_unload_locale (struct locale_data *locale)
211 {
212   if (locale->mmaped)
213     __munmap ((caddr_t) locale->filedata, locale->filesize);
214   else
215     free ((void *) locale->filedata);
216
217   free (locale);
218 }