CUBRID Engine  latest
message_catalog.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * message_catalog.c - Message catalog functions with NLS support
21  */
22 
23 #ident "$Id$"
24 
25 #include "config.h"
26 #include "message_catalog.h"
27 
28 /*
29  * Note: stems from FreeBSD nl_type.h and msgcat.c.
30  */
31 #if defined(WINDOWS)
32 #include <windows.h>
33 #endif
34 
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #if !defined(WINDOWS)
38 #include <sys/mman.h>
39 #include <netinet/in.h>
40 #endif
41 
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <stdlib.h>
45 #include <locale.h>
46 #include <string.h>
47 #include <limits.h>
48 #include <stdio.h>
49 
50 #include "porting.h"
51 
52 #include "environment_variable.h"
53 #include "error_code.h"
54 #include "error_manager.h"
55 #include "language_support.h"
56 
57 #if defined(WINDOWS)
58 #include "intl_support.h"
59 #endif
60 
61 /*
62  * MESSAGE CATALOG FILE FORMAT.
63  *
64  * The NetBSD/FreeBSD message catalog format is similar to the format used by
65  * Svr4 systems. The differences are:
66  * * fixed byte order (big endian)
67  * * fixed data field sizes
68  *
69  * A message catalog contains four data types: a catalog header, one
70  * or more set headers, one or more message headers, and one or more
71  * text strings.
72  *
73  * NOTE: some changes were done to the initial code. we can no longer use the ntypes.h. and if nltypes.h is included
74  * there will be conflicts... so just rename everything.
75  */
76 
77 #define NLS_MAGIC 0xff88ff89
78 
80 {
81  INT32 _magic;
82  INT32 _nsets;
83  INT32 _mem;
86 };
87 
89 {
90  INT32 _setno; /* set number: 0 < x <= NL_SETMAX */
91  INT32 _nmsgs; /* number of messages in the set */
92  INT32 _index; /* index of first msg_hdr in msg_hdr table */
93 };
94 
96 {
97  INT32 _msgno; /* msg number: 0 < x <= NL_MSGMAX */
98  INT32 _msglen;
99  INT32 _offset;
100 };
101 
102 #ifndef ENCODING_LEN
103 #define ENCODING_LEN 40
104 #endif
105 
106 #define CUB_NL_SETD 0
107 #define CUB_NL_CAT_LOCALE 1
108 
109 typedef struct _nl_cat_d
110 {
111  void *_data;
112  int _size;
113 #if defined(WINDOWS)
114  HANDLE map_handle;
115 #endif
116 } *cub_nl_catd;
117 
118 static cub_nl_catd cub_catopen (const char *, int);
119 static char *cub_catgets (cub_nl_catd, int, int, const char *);
120 static int cub_catclose (cub_nl_catd);
121 
122 /*
123  * Note: stems from FreeBSD nl_type.h and msgcat.c.
124  */
125 #define DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L"
126 
127 static cub_nl_catd load_msgcat (const char *);
128 
130 cub_catopen (const char *name, int type)
131 {
132  int spcleft, saverr;
133  char path[PATH_MAX];
134  char *nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
135  char *cptr1, *plang, *pter, *pcode;
136  struct stat sbuf;
137 
138  if (name == NULL || *name == '\0')
139  {
140  errno = EINVAL;
141  return NULL;
142  }
143 
144  /* is it absolute path ? if yes, load immediately */
145  if (strchr (name, '/') != NULL)
146  {
147  return load_msgcat (name);
148  }
149 
150  if (type == CUB_NL_CAT_LOCALE)
151  {
152  lang = setlocale (LC_MESSAGES, NULL);
153  }
154  else
155  {
156  lang = getenv ("CHARSET");
157  }
158 
159  if (lang == NULL || *lang == '\0' || strlen (lang) > ENCODING_LEN
160  || (lang[0] == '.' && (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || strchr (lang, '/') != NULL)
161  {
162  lang = (char *) "C";
163  }
164 
165  plang = cptr1 = strdup (lang);
166  if (cptr1 == NULL)
167  {
168  return NULL;
169  }
170 
171  cptr = strchr (cptr1, '@');
172  if (cptr != NULL)
173  {
174  *cptr = '\0';
175  }
176 
177  pter = pcode = (char *) "";
178  cptr = strchr (cptr1, '_');
179  if (cptr != NULL)
180  {
181  *cptr++ = '\0';
182  pter = cptr1 = cptr;
183  }
184 
185  cptr = strchr (cptr1, '.');
186  if (cptr != NULL)
187  {
188  *cptr++ = '\0';
189  pcode = cptr;
190  }
191 
192  nlspath = getenv ("NLSPATH");
193  if (nlspath == NULL)
194  {
195  nlspath = (char *) DEFAULT_NLS_PATH;
196  }
197 
198  base = cptr = strdup (nlspath);
199  if (cptr == NULL)
200  {
201  saverr = errno;
202  free (plang);
203  errno = saverr;
204  return NULL;
205  }
206 
207  while ((nlspath = strsep (&cptr, ":")) != NULL)
208  {
209  pathP = path;
210  if (*nlspath)
211  {
212  for (; *nlspath; ++nlspath)
213  {
214  if (*nlspath == '%')
215  {
216  switch (*(nlspath + 1))
217  {
218  case 'l':
219  tmpptr = plang;
220  break;
221  case 't':
222  tmpptr = pter;
223  break;
224  case 'c':
225  tmpptr = pcode;
226  break;
227  case 'L':
228  tmpptr = lang;
229  break;
230  case 'N':
231  tmpptr = (char *) name;
232  break;
233  case '%':
234  ++nlspath;
235  /* fallthrough */
236  default:
237  if (pathP - path >= PATH_MAX - 1)
238  {
239  goto too_long;
240  }
241  *(pathP++) = *nlspath;
242  continue;
243  }
244  ++nlspath;
245 
246  put_tmpptr:
247  spcleft = PATH_MAX - (CAST_STRLEN (pathP - path)) - 1;
248  if (strlcpy (pathP, tmpptr, spcleft) >= (size_t) spcleft)
249  {
250  too_long:
251  free (plang);
252  free (base);
253  errno = ENAMETOOLONG;
254  return NULL;
255  }
256  pathP += strlen (tmpptr);
257  }
258  else
259  {
260  if (pathP - path >= PATH_MAX - 1)
261  {
262  goto too_long;
263  }
264  *(pathP++) = *nlspath;
265  }
266  }
267  *pathP = '\0';
268  if (stat (path, &sbuf) == 0)
269  {
270  free (plang);
271  free (base);
272  return load_msgcat (path);
273  }
274  }
275  else
276  {
277  tmpptr = (char *) name;
278  --nlspath;
279  goto put_tmpptr;
280  }
281  }
282 
283  free (plang);
284  free (base);
285  errno = ENOENT;
286 
287  return NULL;
288 }
289 
290 char *
291 cub_catgets (cub_nl_catd catd, int set_id, int msg_id, const char *s)
292 {
293  struct nls_cat_hdr *cat_hdr;
294  struct nls_set_hdr *set_hdr;
295  struct nls_msg_hdr *msg_hdr;
296  int l, u, i, r;
297 
298  if (catd == NULL)
299  {
300  errno = EBADF;
301  /* LINTED interface problem */
302  return (char *) s;
303  }
304 
305  cat_hdr = (struct nls_cat_hdr *) catd->_data;
306  set_hdr = (struct nls_set_hdr *) (void *) ((char *) catd->_data + sizeof (struct nls_cat_hdr));
307 
308  /* binary search, see knuth algorithm b */
309  l = 0;
310  u = ntohl ((UINT32) cat_hdr->_nsets) - 1;
311  while (l <= u)
312  {
313  i = (l + u) / 2;
314  r = set_id - ntohl ((UINT32) set_hdr[i]._setno);
315 
316  if (r == 0)
317  {
318  msg_hdr =
319  (struct nls_msg_hdr *) (void *) ((char *) catd->_data + sizeof (struct nls_cat_hdr) +
320  ntohl ((UINT32) cat_hdr->_msg_hdr_offset));
321 
322  l = ntohl ((UINT32) set_hdr[i]._index);
323  u = l + ntohl ((UINT32) set_hdr[i]._nmsgs) - 1;
324  while (l <= u)
325  {
326  i = (l + u) / 2;
327  r = msg_id - ntohl ((UINT32) msg_hdr[i]._msgno);
328  if (r == 0)
329  {
330  return ((char *) catd->_data + sizeof (struct nls_cat_hdr) +
331  ntohl ((UINT32) cat_hdr->_msg_txt_offset) + ntohl ((UINT32) msg_hdr[i]._offset));
332  }
333  else if (r < 0)
334  {
335  u = i - 1;
336  }
337  else
338  {
339  l = i + 1;
340  }
341  }
342 
343  /* not found */
344  goto notfound;
345 
346  }
347  else if (r < 0)
348  {
349  u = i - 1;
350  }
351  else
352  {
353  l = i + 1;
354  }
355  }
356 
357 notfound:
358  /* not found */
359  errno = ENOMSG;
360  /* LINTED interface problem */
361  return (char *) s;
362 }
363 
364 int
366 {
367  if (catd == NULL)
368  {
369  errno = EBADF;
370  return (-1);
371  }
372 
373 #if defined(WINDOWS)
374  UnmapViewOfFile (catd->_data);
375  CloseHandle (catd->map_handle);
376 #else
377  munmap (catd->_data, (size_t) catd->_size);
378 #endif
379  free (catd);
380  return (0);
381 }
382 
383 /*
384  * Internal support functions
385  */
386 
387 static cub_nl_catd
388 load_msgcat (const char *path)
389 {
390  struct stat st;
391  cub_nl_catd catd;
392  void *data = NULL;
393 #if defined(WINDOWS)
394  HANDLE map_handle;
395  HANDLE file_handle;
396 #else
397  int fd;
398 #endif
399 
400  /* XXX: path != NULL? */
401 
402 #if defined(WINDOWS)
403  file_handle = CreateFile (path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
404  if (file_handle == NULL)
405  {
406  return NULL;
407  }
408  if (stat (path, &st) != 0)
409  {
410  CloseHandle (file_handle);
411  return NULL;
412  }
413 
414  map_handle = CreateFileMapping ((HANDLE) file_handle, NULL, PAGE_READONLY, 0, (DWORD) st.st_size, NULL);
415  if (map_handle != NULL)
416  {
417  data = MapViewOfFile (map_handle, FILE_MAP_READ, 0, 0, 0);
418  }
419 
420  if (data == NULL)
421  {
422  return NULL;
423  }
424 #else
425  fd = open (path, O_RDONLY);
426  if (fd == -1)
427  {
428  return NULL;
429  }
430 
431  if (fstat (fd, &st) != 0)
432  {
433  close (fd);
434  return NULL;
435  }
436 
437  data = mmap (0, (size_t) st.st_size, PROT_READ, MAP_SHARED, fd, (off_t) 0);
438  close (fd);
439 
440  if (data == MAP_FAILED)
441  {
442  return NULL;
443  }
444 #endif
445 
446  if (ntohl ((UINT32) ((struct nls_cat_hdr *) data)->_magic) != NLS_MAGIC)
447  {
448 #if defined(WINDOWS)
449  UnmapViewOfFile (data);
450  CloseHandle (map_handle);
451 #else
452  munmap (data, (size_t) st.st_size);
453 #endif
454  errno = EINVAL;
455  return NULL;
456  }
457 
458  catd = (cub_nl_catd) malloc (sizeof (*catd));
459  if (catd == NULL)
460  {
461 #if defined(WINDOWS)
462  UnmapViewOfFile (data);
463  CloseHandle (map_handle);
464 #else
465  munmap (data, (size_t) st.st_size);
466 #endif
467  return NULL;
468  }
469 
470  catd->_data = data;
471  catd->_size = (int) st.st_size;
472 #if defined(WINDOWS)
473  catd->map_handle = map_handle;
474 #endif
475  return catd;
476 }
477 
478 /* system message catalog definition */
480 {
481  int cat_id;
482  const char *name;
484 };
486  {MSGCAT_CATALOG_CUBRID /* 0 */ , "cubrid.cat", NULL},
487  {MSGCAT_CATALOG_CSQL /* 1 */ , "csql.cat", NULL},
488  {MSGCAT_CATALOG_UTILS /* 2 */ , "utils.cat", NULL}
489 };
490 
491 #define MSGCAT_SYSTEM_DIM \
492  (sizeof(msgcat_System) / sizeof(struct msgcat_def))
493 
494 /*
495  * msgcat_init - initialize message catalog module
496  * return: NO_ERROR or ER_FAILED
497  */
498 int
500 {
501  size_t i;
502 
503  for (i = 0; i < MSGCAT_SYSTEM_DIM; i++)
504  {
505  if (msgcat_System[i].msg_catd == NULL)
506  {
507  msgcat_System[i].msg_catd = msgcat_open (msgcat_System[i].name);
508  }
509  }
510 
511  for (i = 0; i < MSGCAT_SYSTEM_DIM; i++)
512  {
513  if (msgcat_System[i].msg_catd == NULL)
514  {
515  return ER_FAILED;
516  }
517  }
518 
519  return NO_ERROR;
520 }
521 
522 /*
523  * msgcat_final - finalize message catalog module
524  * return: NO_ERROR or ER_FAILED
525  */
526 int
528 {
529  size_t i;
530  int rc;
531 
532  rc = NO_ERROR;
533  for (i = 0; i < MSGCAT_SYSTEM_DIM; i++)
534  {
535  if (msgcat_System[i].msg_catd != NULL)
536  {
537  if (msgcat_close (msgcat_System[i].msg_catd) != NO_ERROR)
538  {
539  rc = ER_FAILED;
540  }
541  msgcat_System[i].msg_catd = NULL;
542  }
543  }
544 
545  return rc;
546 }
547 
548 /*
549  * msgcat_message -
550  * return: a message string or NULL
551  * cat_id(in):
552  * set_id(in):
553  * msg_id(in):
554  *
555  * Note:
556  */
557 char *
558 msgcat_message (int cat_id, int set_id, int msg_id)
559 {
560  char *msg;
561  static char *empty = (char *) "";
562 
563  if (cat_id < 0 || ((size_t) cat_id) >= MSGCAT_SYSTEM_DIM)
564  {
565  return NULL;
566  }
567 
568  if (msgcat_System[cat_id].msg_catd == NULL)
569  {
570  msgcat_System[cat_id].msg_catd = msgcat_open (msgcat_System[cat_id].name);
571  if (msgcat_System[cat_id].msg_catd == NULL)
572  {
573  return NULL;
574  }
575  }
576 
577  msg = msgcat_gets (msgcat_System[cat_id].msg_catd, set_id, msg_id, NULL);
578  if (msg == NULL)
579  {
580  fprintf (stderr, "Cannot find message id %d in set id %d from the file %s(%s).", msg_id, set_id,
581  msgcat_System[cat_id].name, msgcat_System[cat_id].msg_catd->file);
582  /* to protect the error of copying NULL pointer, return empty string ("") rather than NULL */
583  return empty;
584  }
585 
586  return msg;
587 }
588 
589 /*
590  * msgcat_open - open a message catalog
591  * return: message catalog descriptor MSG_CATD or NULL
592  * name(in): message catalog file name
593  *
594  * Note: File name will be converted to a full path name with the the root
595  * directory prefix.
596  * The returned MSG_CATD is allocated with malloc(). It will be freed in
597  * msgcat_close().
598  */
599 MSG_CATD
600 msgcat_open (const char *name)
601 {
602  cub_nl_catd catd;
604  char path[PATH_MAX];
605 
606  /* $CUBRID/msg/$CUBRID_MSG_LANG/'name' */
607  envvar_localedir_file (path, PATH_MAX, lang_get_msg_Loc_name (), name);
608  catd = cub_catopen (path, 0);
609  if (catd == NULL)
610  {
611  /* try once more as default language */
612  envvar_localedir_file (path, PATH_MAX, LANG_NAME_DEFAULT, name);
613  catd = cub_catopen (path, 0);
614  if (catd == NULL)
615  {
616  return NULL;
617  }
618  }
619 
620  msg_catd = (MSG_CATD) malloc (sizeof (*msg_catd));
621  if (msg_catd == NULL)
622  {
624  cub_catclose (catd);
625  return NULL;
626  }
627 
628  msg_catd->file = strdup (path);
629  msg_catd->catd = (void *) catd;
630 
631  return msg_catd;
632 }
633 
634 /*
635  * msgcat_get_descriptor - get a message catalog descriptor
636  * return: message catalog descriptor MSG_CATD
637  * cat_id(in): message id (number)
638  *
639  * Note:
640  */
641 MSG_CATD
643 {
644  return (msgcat_System[cat_id].msg_catd);
645 }
646 
647 /*
648  * msgcat_gets - read a message string from the message catalog
649  * return:
650  * msg_catd(in): message catalog descriptor
651  * set_id(in): set id (number)
652  * msg_id(in): message id (number)
653  * s(in): default message string which shall be returned if the identified
654  * message is not available
655  *
656  * Note:
657  */
658 char *
659 msgcat_gets (MSG_CATD msg_catd, int set_id, int msg_id, const char *s)
660 {
661  cub_nl_catd catd;
662 
663  catd = (cub_nl_catd) msg_catd->catd;
664  return cub_catgets (catd, set_id, msg_id, s);
665 }
666 
667 /*
668  * msgcat_close - close a message catalog
669  * return: NO_ERROR or ER_FAILED
670  * msg_catd(in): message catalog descriptor MSG_CATD
671  *
672  * Note:
673  */
674 int
676 {
677  cub_nl_catd catd;
678 
679  catd = (cub_nl_catd) msg_catd->catd;
680  free ((void *) msg_catd->file);
681  free (msg_catd);
682  if (cub_catclose (catd) < 0)
683  {
684  return ER_FAILED;
685  }
686 
687  return NO_ERROR;
688 }
689 
690 /*
691  * msgcat_open_file - open a text file in the directory of message catalogs
692  * return: FILE pointer or NULL
693  * name(in):
694  */
695 FILE *
696 msgcat_open_file (const char *name)
697 {
698  FILE *fp;
699  char path[PATH_MAX];
700 
701  /* $CUBRID/msg/$CUBRID_MSG_LANG/'name' */
702  envvar_localedir_file (path, PATH_MAX, lang_get_msg_Loc_name (), name);
703  fp = fopen (path, "r");
704  if (fp == NULL)
705  {
706  /* try once more as default language */
707  envvar_localedir_file (path, PATH_MAX, LANG_NAME_DEFAULT, name);
708  fp = fopen (path, "r");
709  }
710 
711  return fp;
712 }
MSG_CATD msgcat_get_descriptor(int cat_id)
#define NO_ERROR
Definition: error_code.h:46
const char * lang_get_msg_Loc_name(void)
INT32 _msg_txt_offset
#define MSGCAT_CATALOG_CSQL
void * catd
int msgcat_close(MSG_CATD msg_catd)
#define MSGCAT_SYSTEM_DIM
#define ER_FAILED
Definition: error_code.h:47
struct msg_catd * MSG_CATD
#define ENCODING_LEN
#define CAST_STRLEN
Definition: porting.h:470
INT32 _msg_hdr_offset
MSG_CATD msgcat_open(const char *name)
#define LANG_NAME_DEFAULT
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
static char * cub_catgets(cub_nl_catd, int, int, const char *)
#define ER_OUT_OF_VIRTUAL_MEMORY
Definition: error_code.h:50
static cub_nl_catd cub_catopen(const char *, int)
char * msgcat_gets(MSG_CATD msg_catd, int set_id, int msg_id, const char *s)
#define NULL
Definition: freelistheap.h:34
struct _nl_cat_d * cub_nl_catd
struct msgcat_def msgcat_System[]
int msgcat_init(void)
#define MSGCAT_CATALOG_CUBRID
static int rc
Definition: serial.c:50
#define NLS_MAGIC
#define DEFAULT_NLS_PATH
const char * name
#define ARG_FILE_LINE
Definition: error_manager.h:44
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: porting.c:2584
int msgcat_final(void)
#define strlen(s1)
Definition: intl_support.c:43
static cub_nl_catd load_msgcat(const char *)
FILE * msgcat_open_file(const char *name)
int i
Definition: dynamic_load.c:954
char * msgcat_message(int cat_id, int set_id, int msg_id)
const char * file
static int cub_catclose(cub_nl_catd)
char * strdup(const char *str)
Definition: porting.c:901
char * envvar_localedir_file(char *path, size_t size, const char *langpath, const char *filename)
unsigned int ntohl(unsigned int from)
MSG_CATD msg_catd
#define CUB_NL_CAT_LOCALE
#define MSGCAT_CATALOG_UTILS