(write_locale_data): Don't add extra slash in filename.
[platform/upstream/glibc.git] / locale / programs / locfile.c
1 /* Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
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 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/param.h>
29 #include <sys/stat.h>
30
31 #include "localedef.h"
32 #include "locfile.h"
33
34 #include "locfile-kw.h"
35
36
37 int
38 locfile_read (struct localedef_t *result, struct charmap_t *charmap)
39 {
40   const char *filename = result->name;
41   const char *repertoire_name = result->repertoire_name;
42   int locale_mask = result->needed ^ result->avail;
43   struct linereader *ldfile;
44   int not_here = ALL_LOCALES;
45
46   /* If no repertoire name was specified use the global one.  */
47   if (repertoire_name == NULL)
48     repertoire_name = repertoire_global;
49
50   /* Open the locale definition file.  */
51   ldfile = lr_open (filename, locfile_hash);
52   if (ldfile == NULL)
53     {
54       if (filename[0] != '/')
55         {
56           char *i18npath = getenv ("I18NPATH");
57           if (i18npath != NULL && *i18npath != '\0')
58             {
59               char path[strlen (filename) + 1 + strlen (i18npath)
60                         + sizeof ("/locales/") - 1];
61               char *next;
62               i18npath = strdupa (i18npath);
63
64
65               while (ldfile == NULL
66                      && (next = strsep (&i18npath, ":")) != NULL)
67                 {
68                   stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
69
70                   ldfile = lr_open (path, locfile_hash);
71
72                   if (ldfile == NULL)
73                     {
74                       stpcpy (stpcpy (path, next), filename);
75
76                       ldfile = lr_open (path, locfile_hash);
77                     }
78                 }
79             }
80
81           /* Test in the default directory.  */
82           if (ldfile == NULL)
83             {
84               char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
85
86               stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
87               ldfile = lr_open (path, locfile_hash);
88             }
89         }
90
91       if (ldfile == NULL)
92         return 1;
93     }
94
95     /* Parse locale definition file and store result in RESULT.  */
96   while (1)
97     {
98       struct token *now = lr_token (ldfile, charmap, NULL);
99       enum token_t nowtok = now->tok;
100       struct token *arg;
101
102       if (nowtok == tok_eof)
103         break;
104
105       if (nowtok == tok_eol)
106         /* Ignore empty lines.  */
107         continue;
108
109       switch (nowtok)
110         {
111         case tok_escape_char:
112         case tok_comment_char:
113           /* We need an argument.  */
114           arg = lr_token (ldfile, charmap, NULL);
115
116           if (arg->tok != tok_ident)
117             {
118               SYNTAX_ERROR (_("bad argument"));
119               continue;
120             }
121
122           if (arg->val.str.lenmb != 1)
123             {
124               lr_error (ldfile, _("\
125 argument to `%s' must be a single character"),
126                         nowtok == tok_escape_char
127                         ? "escape_char" : "comment_char");
128
129               lr_ignore_rest (ldfile, 0);
130               continue;
131             }
132
133           if (nowtok == tok_escape_char)
134             ldfile->escape_char = *arg->val.str.startmb;
135           else
136             ldfile->comment_char = *arg->val.str.startmb;
137           break;
138
139         case tok_repertoiremap:
140           /* We need an argument.  */
141           arg = lr_token (ldfile, charmap, NULL);
142
143           if (arg->tok != tok_ident)
144             {
145               SYNTAX_ERROR (_("bad argument"));
146               continue;
147             }
148
149           if (repertoire_name == NULL)
150             {
151               repertoire_name = memcpy (xmalloc (arg->val.str.lenmb + 1),
152                                         arg->val.str.startmb,
153                                         arg->val.str.lenmb);
154               ((char *) repertoire_name)[arg->val.str.lenmb] = '\0';
155             }
156           break;
157
158         case tok_lc_ctype:
159           ctype_read (ldfile, result, charmap, repertoire_name,
160                       (locale_mask & CTYPE_LOCALE) == 0);
161           result->avail |= locale_mask & CTYPE_LOCALE;
162           not_here ^= CTYPE_LOCALE;
163           continue;
164
165         case tok_lc_collate:
166           collate_read (ldfile, result, charmap, repertoire_name,
167                         (locale_mask & COLLATE_LOCALE) == 0);
168           result->avail |= locale_mask & COLLATE_LOCALE;
169           not_here ^= COLLATE_LOCALE;
170           continue;
171
172         case tok_lc_monetary:
173           monetary_read (ldfile, result, charmap, repertoire_name,
174                          (locale_mask & MONETARY_LOCALE) == 0);
175           result->avail |= locale_mask & MONETARY_LOCALE;
176           not_here ^= MONETARY_LOCALE;
177           continue;
178
179         case tok_lc_numeric:
180           numeric_read (ldfile, result, charmap, repertoire_name,
181                         (locale_mask & NUMERIC_LOCALE) == 0);
182           result->avail |= locale_mask & NUMERIC_LOCALE;
183           not_here ^= NUMERIC_LOCALE;
184           continue;
185
186         case tok_lc_time:
187           time_read (ldfile, result, charmap, repertoire_name,
188                      (locale_mask & TIME_LOCALE) == 0);
189           result->avail |= locale_mask & TIME_LOCALE;
190           not_here ^= TIME_LOCALE;
191           continue;
192
193         case tok_lc_messages:
194           messages_read (ldfile, result, charmap, repertoire_name,
195                          (locale_mask & MESSAGES_LOCALE) == 0);
196           result->avail |= locale_mask & MESSAGES_LOCALE;
197           not_here ^= MESSAGES_LOCALE;
198           continue;
199
200         case tok_lc_paper:
201           paper_read (ldfile, result, charmap, repertoire_name,
202                       (locale_mask & PAPER_LOCALE) == 0);
203           result->avail |= locale_mask & PAPER_LOCALE;
204           not_here ^= PAPER_LOCALE;
205           continue;
206
207         case tok_lc_name:
208           name_read (ldfile, result, charmap, repertoire_name,
209                      (locale_mask & NAME_LOCALE) == 0);
210           result->avail |= locale_mask & NAME_LOCALE;
211           not_here ^= NAME_LOCALE;
212           continue;
213
214         case tok_lc_address:
215           address_read (ldfile, result, charmap, repertoire_name,
216                         (locale_mask & ADDRESS_LOCALE) == 0);
217           result->avail |= locale_mask & ADDRESS_LOCALE;
218           not_here ^= ADDRESS_LOCALE;
219           continue;
220
221         case tok_lc_telephone:
222           telephone_read (ldfile, result, charmap, repertoire_name,
223                           (locale_mask & TELEPHONE_LOCALE) == 0);
224           result->avail |= locale_mask & TELEPHONE_LOCALE;
225           not_here ^= TELEPHONE_LOCALE;
226           continue;
227
228         case tok_lc_measurement:
229           measurement_read (ldfile, result, charmap, repertoire_name,
230                             (locale_mask & MEASUREMENT_LOCALE) == 0);
231           result->avail |= locale_mask & MEASUREMENT_LOCALE;
232           not_here ^= MEASUREMENT_LOCALE;
233           continue;
234
235         case tok_lc_identification:
236           identification_read (ldfile, result, charmap, repertoire_name,
237                                (locale_mask & IDENTIFICATION_LOCALE) == 0);
238           result->avail |= locale_mask & IDENTIFICATION_LOCALE;
239           not_here ^= IDENTIFICATION_LOCALE;
240           continue;
241
242         default:
243           SYNTAX_ERROR (_("\
244 syntax error: not inside a locale definition section"));
245           continue;
246         }
247
248       /* The rest of the line must be empty.  */
249       lr_ignore_rest (ldfile, 1);
250     }
251
252   /* We read all of the file.  */
253   lr_close (ldfile);
254
255   /* Mark the categories which are not contained in the file.  We assume
256      them to be available and the default data will be used.  */
257   result->avail |= not_here;
258
259   return 0;
260 }
261
262
263 static void (*const check_funcs[]) (struct localedef_t *,
264                                     struct charmap_t *) =
265 {
266   [LC_CTYPE] = ctype_finish,
267   [LC_COLLATE] = collate_finish,
268   [LC_MESSAGES] = messages_finish,
269   [LC_MONETARY] = monetary_finish,
270   [LC_NUMERIC] = numeric_finish,
271   [LC_TIME] = time_finish,
272   [LC_PAPER] = paper_finish,
273   [LC_NAME] = name_finish,
274   [LC_ADDRESS] = address_finish,
275   [LC_TELEPHONE] = telephone_finish,
276   [LC_MEASUREMENT] = measurement_finish,
277   [LC_IDENTIFICATION] = identification_finish
278 };
279
280
281 void
282 check_all_categories (struct localedef_t *definitions,
283                       struct charmap_t *charmap)
284 {
285   int cnt;
286
287   for (cnt = 0; cnt < sizeof (check_funcs) / sizeof (check_funcs[0]); ++cnt)
288     if (check_funcs[cnt] != NULL)
289       check_funcs[cnt] (definitions, charmap);
290 }
291
292
293 static void (*const write_funcs[]) (struct localedef_t *, struct charmap_t *,
294                                     const char *) =
295 {
296   [LC_CTYPE] = ctype_output,
297   [LC_COLLATE] = collate_output,
298   [LC_MESSAGES] = messages_output,
299   [LC_MONETARY] = monetary_output,
300   [LC_NUMERIC] = numeric_output,
301   [LC_TIME] = time_output,
302   [LC_PAPER] = paper_output,
303   [LC_NAME] = name_output,
304   [LC_ADDRESS] = address_output,
305   [LC_TELEPHONE] = telephone_output,
306   [LC_MEASUREMENT] = measurement_output,
307   [LC_IDENTIFICATION] = identification_output
308 };
309
310
311 void
312 write_all_categories (struct localedef_t *definitions,
313                       struct charmap_t *charmap,
314                       const char *output_path)
315 {
316   int cnt;
317
318   for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
319     if (write_funcs[cnt] != NULL)
320       write_funcs[cnt] (definitions, charmap, output_path);
321 }
322
323
324 void
325 write_locale_data (const char *output_path, const char *category,
326                    size_t n_elem, struct iovec *vec)
327 {
328   size_t cnt, step, maxiov;
329   int fd;
330   char *fname;
331
332   fname = malloc (strlen (output_path) + 2 * strlen (category) + 7);
333   if (fname == NULL)
334     error (5, errno, _("memory exhausted"));
335
336   /* Normally we write to the directory pointed to by the OUTPUT_PATH.
337      But for LC_MESSAGES we have to take care for the translation
338      data.  This means we need to have a directory LC_MESSAGES in
339      which we place the file under the name SYS_LC_MESSAGES.  */
340   sprintf (fname, "%s%s", output_path, category);
341   if (strcmp (category, "LC_MESSAGES") == 0)
342     {
343       struct stat st;
344
345       if (stat (fname, &st) < 0)
346         {
347           if (mkdir (fname, 0777) < 0)
348             fd = creat (fname, 0666);
349           else
350             {
351               fd = -1;
352               errno = EISDIR;
353             }
354         }
355       else if (S_ISREG (st.st_mode))
356         fd = creat (fname, 0666);
357       else
358         {
359           fd = -1;
360           errno = EISDIR;
361         }
362     }
363   else
364     fd = creat (fname, 0666);
365
366   if (fd == -1)
367     {
368       int save_err = errno;
369
370       if (errno == EISDIR)
371         {
372           sprintf (fname, "%1$s/%2$s/SYS_%2$s", output_path, category);
373           fd = creat (fname, 0666);
374           if (fd == -1)
375             save_err = errno;
376         }
377
378       if (fd == -1)
379         {
380           if (!be_quiet)
381             error (0, save_err, _("\
382 cannot open output file `%s' for category `%s'"),
383                    fname, category);
384           return;
385         }
386     }
387   free (fname);
388
389 #ifdef UIO_MAXIOV
390   maxiov = UIO_MAXIOV;
391 #else
392   maxiov = sysconf (_SC_UIO_MAXIOV);
393 #endif
394
395   /* Write the data using writev.  But we must take care for the
396      limitation of the implementation.  */
397   for (cnt = 0; cnt < n_elem; cnt += step)
398     {
399       step = n_elem - cnt;
400       if (maxiov > 0)
401         step = MIN (maxiov, step);
402
403       if (writev (fd, &vec[cnt], step) < 0)
404         {
405           if (!be_quiet)
406             error (0, errno, _("failure while writing data for category `%s'"),
407                    category);
408           break;
409         }
410     }
411
412   close (fd);
413 }