update from main archive 961105
[jlayton/glibc.git] / catgets / open_catalog.c
1 /* Copyright (C) 1996 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
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 <endian.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27
28 #include "catgetsinfo.h"
29
30
31 #define SWAPU32(w) \
32   (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
33
34
35 void
36 __open_catalog (__nl_catd catalog, int with_path)
37 {
38   int fd;
39   struct stat st;
40   int swapping;
41
42   if (strchr (catalog->cat_name, '/') != NULL || !with_path)
43     fd = open (catalog->cat_name, O_RDONLY);
44   else
45     {
46       const char *run_nlspath = catalog->nlspath;
47 #define ENOUGH(n)                                                             \
48   if (bufact + (n) >=bufmax)                                                  \
49     {                                                                         \
50       char *old_buf = buf;                                                    \
51       bufmax += 256 + (n);                                                    \
52       buf = (char *) alloca (bufmax);                                         \
53       memcpy (buf, old_buf, bufact);                                          \
54     }
55
56       /* The RUN_NLSPATH variable contains a colon separated list of
57          descriptions where we expect to find catalogs.  We have to
58          recognize certain % substitutions and stop when we found the
59          first existing file.  */
60       char *buf;
61       size_t bufact;
62       size_t bufmax;
63
64       buf = NULL;
65       bufmax = 0;
66
67       fd = -1;
68       while (*run_nlspath != '\0')
69         {
70           bufact = 0;
71           while (*run_nlspath != ':' && *run_nlspath != '\0')
72             if (*run_nlspath == '%')
73               {
74                 const char *tmp;
75
76                 ++run_nlspath;  /* We have seen the `%'.  */
77                 switch (*run_nlspath++)
78                   {
79                   case 'N':
80                     /* Use the catalog name.  */
81                     ENOUGH (strlen (catalog->cat_name));
82                     bufact = __stpcpy (&buf[bufact], catalog->cat_name) - buf;
83                     break;
84                   case 'L':
85                     /* Use the current locale category value.  */
86                     ENOUGH (strlen (catalog->env_var));
87                     bufact = __stpcpy (&buf[bufact], catalog->env_var) - buf;
88                     break;
89                   case 'l':
90                     /* Use language element of locale category value.  */
91                     tmp = catalog->env_var;
92                     do
93                       {
94                         ENOUGH (1);
95                         buf[bufact++] = *tmp++;
96                       }
97                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
98                     break;
99                   case 't':
100                     /* Use territory element of locale category value.  */
101                     tmp = catalog->env_var;
102                     do
103                       ++tmp;
104                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
105                     if (*tmp == '_')
106                       {
107                         ++tmp;
108                         do
109                           {
110                             ENOUGH (1);
111                             buf[bufact++] = *tmp;
112                           }
113                         while (*tmp != '\0' && *tmp != '.');
114                       }
115                     break;
116                   case 'c':
117                     /* Use code set element of locale category value.  */
118                     tmp = catalog->env_var;
119                     do
120                       ++tmp;
121                     while (*tmp != '\0' && *tmp != '.');
122                     if (*tmp == '.')
123                       {
124                         ++tmp;
125                         do
126                           {
127                             ENOUGH (1);
128                             buf[bufact++] = *tmp;
129                           }
130                         while (*tmp != '\0');
131                       }
132                     break;
133                   case '%':
134                     ENOUGH (1);
135                     buf[bufact++] = '%';
136                     break;
137                   default:
138                     /* Unknown variable: ignore this path element.  */
139                     bufact = 0;
140                     while (*run_nlspath != '\0' && *run_nlspath != ':')
141                       ++run_nlspath;
142                     break;
143                   }
144               }
145             else
146               {
147                 ENOUGH (1);
148                 buf[bufact++] = *run_nlspath++;
149               }
150           ENOUGH (1);
151           buf[bufact] = '\0';
152
153           if (bufact != 0)
154             {
155               fd = __open (buf, O_RDONLY);
156               if (fd >= 0)
157                 break;
158             }
159
160           ++run_nlspath;
161         }
162     }
163
164   if (fd < 0 || __fstat (fd, &st) < 0)
165     {
166       catalog->status = nonexisting;
167       return;
168     }
169
170 #ifndef MAP_COPY
171     /* Linux seems to lack read-only copy-on-write.  */
172 #define MAP_COPY MAP_PRIVATE
173 #endif
174 #ifndef MAP_FILE
175     /* Some systems do not have this flag; it is superfluous.  */
176 #define MAP_FILE 0
177 #endif
178 #ifndef MAP_INHERIT
179     /* Some systems might lack this; they lose.  */
180 #define MAP_INHERIT 0
181 #endif
182   catalog->file_size = st.st_size;
183   catalog->file_ptr =
184     (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
185                                    MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0);
186   if (catalog->file_ptr != (struct catalog_obj *) -1)
187     /* Tell the world we managed to mmap the file.  */
188     catalog->status = mmaped;
189   else
190     {
191       /* mmap failed perhaps because the system call is not
192          implemented.  Try to load the file.  */
193       size_t todo;
194       catalog->file_ptr = malloc (st.st_size);
195       if (catalog->file_ptr == NULL)
196         {
197           catalog->status = nonexisting;
198           return;
199         }
200       todo = st.st_size;
201       /* Save read, handle partial reads.  */
202       do
203         {
204           size_t now = __read (fd, (((char *) &catalog->file_ptr)
205                                     + (st.st_size - todo)), todo);
206           if (now == 0)
207             {
208               free ((void *) catalog->file_ptr);
209               catalog->status = nonexisting;
210               return;
211             }
212           todo -= now;
213         }
214       while (todo > 0);
215       catalog->status = malloced;
216     }
217
218   /* We don't need the file anymore.  */
219   __close (fd);
220
221   /* Determine whether the file is a catalog file and if yes whether
222      it is written using the correct byte order.  Else we have to swap
223      the values.  */
224   if (catalog->file_ptr->magic == CATGETS_MAGIC)
225     swapping = 0;
226   else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
227     swapping = 1;
228   else
229     {
230       /* Illegal file.  Free he resources and mark catalog as not
231          usable.  */
232       if (catalog->status == mmaped)
233         __munmap ((void *) catalog->file_ptr, catalog->file_size);
234       else
235         free (catalog->file_ptr);
236       catalog->status = nonexisting;
237       return;
238     }
239
240 #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
241
242   /* Get dimensions of the used hashing table.  */
243   catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
244   catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
245
246   /* The file contains two versions of the pointer tables.  Pick the
247      right one for the local byte order.  */
248 #if __BYTE_ORDER == __LITTLE_ENDIAN
249   catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
250 #elif __BYTE_ORDER == __BIG_ENDIAN
251   catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
252                                                   * catalog->plane_depth
253                                                   * 3];
254 #else
255 # error Cannot handle __BYTE_ORDER byte order
256 #endif
257
258   /* The rest of the file contains all the strings.  They are
259      addressed relative to the position of the first string.  */
260   catalog->strings =
261     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
262                                                * catalog->plane_depth * 3 * 2];
263 }