Include <memcopy.h> and use reg_char for character to search, to help the compiler.
[kopensolaris-gnu/glibc.git] / catgets / open_catalog.c
1 /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper, <drepper@gnu.org>.
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 not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <byteswap.h>
21 #include <endian.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <stdlib.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 "catgetsinfo.h"
33
34
35 #define SWAPU32(w) bswap_32 (w)
36
37
38 void
39 __open_catalog (__nl_catd catalog)
40 {
41   int fd = -1;
42   struct stat st;
43   int swapping;
44   size_t cnt;
45   size_t max_offset;
46   size_t tab_size;
47   const char *lastp;
48
49   /* Make sure we are alone.  */
50   __libc_lock_lock (catalog->lock);
51
52   /* Check whether there was no other thread faster.  */
53   if (catalog->status != closed)
54     /* While we waited some other thread tried to open the catalog.  */
55     goto unlock_return;
56
57   if (strchr (catalog->cat_name, '/') != NULL || catalog->nlspath == NULL)
58     fd = __open (catalog->cat_name, O_RDONLY);
59   else
60     {
61       const char *run_nlspath = catalog->nlspath;
62 #define ENOUGH(n)                                                             \
63   if (bufact + (n) >=bufmax)                                                  \
64     {                                                                         \
65       char *old_buf = buf;                                                    \
66       bufmax += 256 + (n);                                                    \
67       buf = (char *) alloca (bufmax);                                         \
68       memcpy (buf, old_buf, bufact);                                          \
69     }
70
71       /* The RUN_NLSPATH variable contains a colon separated list of
72          descriptions where we expect to find catalogs.  We have to
73          recognize certain % substitutions and stop when we found the
74          first existing file.  */
75       char *buf;
76       size_t bufact;
77       size_t bufmax;
78       size_t len;
79
80       buf = NULL;
81       bufmax = 0;
82
83       fd = -1;
84       while (*run_nlspath != '\0')
85         {
86           bufact = 0;
87           while (*run_nlspath != ':' && *run_nlspath != '\0')
88             if (*run_nlspath == '%')
89               {
90                 const char *tmp;
91
92                 ++run_nlspath;  /* We have seen the `%'.  */
93                 switch (*run_nlspath++)
94                   {
95                   case 'N':
96                     /* Use the catalog name.  */
97                     len = strlen (catalog->cat_name);
98                     ENOUGH (len);
99                     memcpy (&buf[bufact], catalog->cat_name, len);
100                     bufact += len;
101                     break;
102                   case 'L':
103                     /* Use the current locale category value.  */
104                     len = strlen (catalog->env_var);
105                     ENOUGH (len);
106                     memcpy (&buf[bufact], catalog->env_var, len);
107                     bufact += len;
108                     break;
109                   case 'l':
110                     /* Use language element of locale category value.  */
111                     tmp = catalog->env_var;
112                     do
113                       {
114                         ENOUGH (1);
115                         buf[bufact++] = *tmp++;
116                       }
117                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
118                     break;
119                   case 't':
120                     /* Use territory element of locale category value.  */
121                     tmp = catalog->env_var;
122                     do
123                       ++tmp;
124                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
125                     if (*tmp == '_')
126                       {
127                         ++tmp;
128                         do
129                           {
130                             ENOUGH (1);
131                             buf[bufact++] = *tmp;
132                           }
133                         while (*tmp != '\0' && *tmp != '.');
134                       }
135                     break;
136                   case 'c':
137                     /* Use code set element of locale category value.  */
138                     tmp = catalog->env_var;
139                     do
140                       ++tmp;
141                     while (*tmp != '\0' && *tmp != '.');
142                     if (*tmp == '.')
143                       {
144                         ++tmp;
145                         do
146                           {
147                             ENOUGH (1);
148                             buf[bufact++] = *tmp;
149                           }
150                         while (*tmp != '\0');
151                       }
152                     break;
153                   case '%':
154                     ENOUGH (1);
155                     buf[bufact++] = '%';
156                     break;
157                   default:
158                     /* Unknown variable: ignore this path element.  */
159                     bufact = 0;
160                     while (*run_nlspath != '\0' && *run_nlspath != ':')
161                       ++run_nlspath;
162                     break;
163                   }
164               }
165             else
166               {
167                 ENOUGH (1);
168                 buf[bufact++] = *run_nlspath++;
169               }
170           ENOUGH (1);
171           buf[bufact] = '\0';
172
173           if (bufact != 0)
174             {
175               fd = __open (buf, O_RDONLY);
176               if (fd >= 0)
177                 break;
178             }
179
180           ++run_nlspath;
181         }
182     }
183
184   /* Avoid dealing with directories and block devices */
185   if (fd < 0)
186     {
187       catalog->status = nonexisting;
188       goto unlock_return;
189     }
190
191   if (__fxstat (_STAT_VER, fd, &st) < 0)
192     {
193       catalog->status = nonexisting;
194       goto close_unlock_return;
195     }
196   if (!S_ISREG (st.st_mode) || st.st_size < sizeof (struct catalog_obj))
197     {
198       /* `errno' is not set correctly but the file is not usable.
199          Use an reasonable error value.  */
200       __set_errno (EINVAL);
201       catalog->status = nonexisting;
202       goto close_unlock_return;
203     }
204
205   catalog->file_size = st.st_size;
206 #ifdef _POSIX_MAPPED_FILES
207 # ifndef MAP_COPY
208     /* Linux seems to lack read-only copy-on-write.  */
209 #  define MAP_COPY MAP_PRIVATE
210 # endif
211 # ifndef MAP_FILE
212     /* Some systems do not have this flag; it is superfluous.  */
213 #  define MAP_FILE 0
214 # endif
215 # ifndef MAP_INHERIT
216     /* Some systems might lack this; they lose.  */
217 #  define MAP_INHERIT 0
218 # endif
219   catalog->file_ptr =
220     (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
221                                    MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0);
222   if (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED)
223     /* Tell the world we managed to mmap the file.  */
224     catalog->status = mmapped;
225   else
226 #endif /* _POSIX_MAPPED_FILES */
227     {
228       /* mmap failed perhaps because the system call is not
229          implemented.  Try to load the file.  */
230       size_t todo;
231       catalog->file_ptr = malloc (st.st_size);
232       if (catalog->file_ptr == NULL)
233         {
234           catalog->status = nonexisting;
235           goto close_unlock_return;
236         }
237       todo = st.st_size;
238       /* Save read, handle partial reads.  */
239       do
240         {
241           size_t now = __read (fd, (((char *) &catalog->file_ptr)
242                                     + (st.st_size - todo)), todo);
243           if (now == 0)
244             {
245               free ((void *) catalog->file_ptr);
246               catalog->status = nonexisting;
247               goto close_unlock_return;
248             }
249           todo -= now;
250         }
251       while (todo > 0);
252       catalog->status = malloced;
253     }
254
255   /* Determine whether the file is a catalog file and if yes whether
256      it is written using the correct byte order.  Else we have to swap
257      the values.  */
258   if (catalog->file_ptr->magic == CATGETS_MAGIC)
259     swapping = 0;
260   else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
261     swapping = 1;
262   else
263     {
264     invalid_file:
265       /* Invalid file.  Free the resources and mark catalog as not
266          usable.  */
267 #ifdef _POSIX_MAPPED_FILES
268       if (catalog->status == mmapped)
269         __munmap ((void *) catalog->file_ptr, catalog->file_size);
270       else
271 #endif  /* _POSIX_MAPPED_FILES */
272         free (catalog->file_ptr);
273       catalog->status = nonexisting;
274       goto close_unlock_return;
275     }
276
277 #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
278
279   /* Get dimensions of the used hashing table.  */
280   catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
281   catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
282
283   /* The file contains two versions of the pointer tables.  Pick the
284      right one for the local byte order.  */
285 #if __BYTE_ORDER == __LITTLE_ENDIAN
286   catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
287 #elif __BYTE_ORDER == __BIG_ENDIAN
288   catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
289                                                   * catalog->plane_depth
290                                                   * 3];
291 #else
292 # error Cannot handle __BYTE_ORDER byte order
293 #endif
294
295   /* The rest of the file contains all the strings.  They are
296      addressed relative to the position of the first string.  */
297   catalog->strings =
298     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
299                                                * catalog->plane_depth * 3 * 2];
300
301   /* Determine the largest string offset mentioned in the table.  */
302   max_offset = 0;
303   tab_size = 3 * catalog->plane_size * catalog->plane_depth;
304   for (cnt = 2; cnt < tab_size; cnt += 3)
305     if (catalog->name_ptr[cnt] > max_offset)
306       max_offset = catalog->name_ptr[cnt];
307
308   /* Now we can check whether the file is large enough to contain the
309      tables it says it contains.  */
310   if (st.st_size <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
311     /* The last string is not contained in the file.  */
312     goto invalid_file;
313
314   lastp = catalog->strings + max_offset;
315   max_offset = (st.st_size
316                 - sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
317   while (*lastp != '\0')
318     {
319       if (--max_offset == 0)
320         goto invalid_file;
321       ++lastp;
322     }
323
324   /* Release the lock again.  */
325  close_unlock_return:
326   __close (fd);
327  unlock_return:
328   __libc_lock_unlock (catalog->lock);
329 }