/* * * Copyright IBM Corporation 1993 * * All Rights Reserved * * License to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of IBM not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS, AND * NONINFRINGEMENT OF THIRD PARTY RIGHTS, IN NO EVENT SHALL * IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdlib.h> #include <stdio.h> #include <ctype.h> #include "Xlibint.h" #include "XlcPubI.h" #include <X11/Xos.h> #include <unistd.h> /************************************************************************/ #ifdef __UNIXOS2__ # define seteuid setuid #endif #define iscomment(ch) ((ch) == '#' || (ch) == '\0') #if defined(WIN32) #define isreadable(f) (_XAccessFile(f)) #else #define isreadable(f) ((access((f), R_OK) != -1) ? 1 : 0) #endif #ifndef __UNIXOS2__ #define LC_PATHDELIM ':' #else #define LC_PATHDELIM ';' #endif #define XLC_BUFSIZE 256 #ifndef X_NOT_POSIX #ifdef _POSIX_SOURCE #include <limits.h> #else #define _POSIX_SOURCE #include <limits.h> #undef _POSIX_SOURCE #endif #endif #ifndef PATH_MAX #ifdef WIN32 #define PATH_MAX 512 #else #include <sys/param.h> #endif #ifndef PATH_MAX #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN #else #define PATH_MAX 1024 #endif #endif #endif #define NUM_LOCALEDIR 64 /* Splits a NUL terminated line into constituents, at colons and newline characters. Leading whitespace is removed from constituents. The constituents are stored at argv[0..argsize-1]. The number of stored constituents (<= argsize) is returned. The line is destructively modified. */ static int parse_line( char *line, char **argv, int argsize) { int argc = 0; char *p = line; while (argc < argsize) { while (isspace(*p)) { ++p; } if (*p == '\0') { break; } argv[argc++] = p; while (*p != ':' && *p != '\n' && *p != '\0') { ++p; } if (*p == '\0') { break; } *p++ = '\0'; } return argc; } #ifdef __UNIXOS2__ /* fg021216: entries in locale files are separated by colons while under OS/2, path entries are separated by semicolon, so we need two functions */ static int parse_line1( char *line, char **argv, int argsize) { int argc = 0; char *p = line; while (argc < argsize) { while (isspace(*p)) { ++p; } if (*p == '\0') { break; } argv[argc++] = p; while (*p != ';' && *p != '\n' && *p != '\0') { ++p; } if (*p == '\0') { break; } *p++ = '\0'; } return argc; } #elif defined(WIN32) /* this is parse_line but skips drive letters at the beginning of the entry */ static int parse_line1( char *line, char **argv, int argsize) { int argc = 0; char *p = line; while (argc < argsize) { while (isspace(*p)) { ++p; } if (*p == '\0') { break; } argv[argc++] = p; if (isalpha(*p) && p[1] == ':') { p+= 2; /* skip drive letters */ } while (*p != ':' && *p != '\n' && *p != '\0') { ++p; } if (*p == '\0') { break; } *p++ = '\0'; } return argc; } #endif /* __UNIXOS2__ */ /* Splits a colon separated list of directories, and returns the constituent paths (without trailing slash). At most argsize constituents are stored at argv[0..argsize-1]. The number of stored constituents is returned. */ static int _XlcParsePath( char *path, char **argv, int argsize) { char *p = path; int n, i; #if !defined(__UNIXOS2__) && !defined(WIN32) n = parse_line(path, argv, argsize); #else n = parse_line1(path, argv, argsize); #endif for (i = 0; i < n; ++i) { int len; p = argv[i]; len = strlen(p); if (len > 0 && p[len - 1] == '/') { /* eliminate trailing slash */ p[len - 1] = '\0'; } } return n; } #ifndef XLOCALEDIR #define XLOCALEDIR "/usr/lib/X11/locale" #endif void xlocaledir( char *buf, int buf_len) { char *p = buf; int len = 0; #ifndef NO_XLOCALEDIR char *dir; int priv = 1; dir = getenv("XLOCALEDIR"); if (dir) { #ifndef WIN32 /* * Only use the user-supplied path if the process isn't priviledged. */ if (getuid() == geteuid() && getgid() == getegid()) { #if defined(HASSETUGID) priv = issetugid(); #elif defined(HASGETRESUID) { uid_t ruid, euid, suid; gid_t rgid, egid, sgid; if ((getresuid(&ruid, &euid, &suid) == 0) && (getresgid(&rgid, &egid, &sgid) == 0)) priv = (euid != suid) || (egid != sgid); } #else /* * If there are saved ID's the process might still be priviledged * even though the above test succeeded. If issetugid() and * getresgid() aren't available, test this by trying to set * euid to 0. * * Note: this only protects setuid-root clients. It doesn't * protect other setuid or any setgid clients. If this tradeoff * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def. */ unsigned int oldeuid; oldeuid = geteuid(); if (seteuid(0) != 0) { priv = 0; } else { if (seteuid(oldeuid) == -1) { /* XXX ouch, coudn't get back to original uid what can we do ??? */ _exit(127); } priv = 1; } #endif } #else priv = 0; #endif if (!priv) { len = strlen(dir); strncpy(p, dir, buf_len); if (len < buf_len) { p[len++] = LC_PATHDELIM; p += len; } } } #endif /* NO_XLOCALEDIR */ if (len < buf_len) #ifndef __UNIXOS2__ strncpy(p, XLOCALEDIR, buf_len - len); #else strncpy(p,__XOS2RedirRoot(XLOCALEDIR), buf_len - len); #endif buf[buf_len-1] = '\0'; } static void xlocalelibdir( char *buf, int buf_len) { char *p = buf; int len = 0; #ifndef NO_XLOCALEDIR char *dir; int priv = 1; dir = getenv("XLOCALELIBDIR"); if (dir) { #ifndef WIN32 /* * Only use the user-supplied path if the process isn't priviledged. */ if (getuid() == geteuid() && getgid() == getegid()) { #if defined(HASSETUGID) priv = issetugid(); #elif defined(HASGETRESUID) { uid_t ruid, euid, suid; gid_t rgid, egid, sgid; if ((getresuid(&ruid, &euid, &suid) == 0) && (getresgid(&rgid, &egid, &sgid) == 0)) priv = (euid != suid) || (egid != sgid); } #else /* * If there are saved ID's the process might still be priviledged * even though the above test succeeded. If issetugid() and * getresgid() aren't available, test this by trying to set * euid to 0. * * Note: this only protects setuid-root clients. It doesn't * protect other setuid or any setgid clients. If this tradeoff * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def. */ unsigned int oldeuid; oldeuid = geteuid(); if (seteuid(0) != 0) { priv = 0; } else { if (seteuid(oldeuid) == -1) { /* XXX ouch, coudn't get back to original uid what can we do ??? */ _exit(127); } priv = 1; } #endif } #else priv = 0; #endif if (!priv) { len = strlen(dir); strncpy(p, dir, buf_len); if (len < buf_len) { p[len++] = LC_PATHDELIM; p += len; } } } #endif /* NO_XLOCALEDIR */ if (len < buf_len) #ifndef __UNIXOS2__ strncpy(p, XLOCALELIBDIR, buf_len - len); #else strncpy(p,__XOS2RedirRoot(XLOCALELIBDIR), buf_len - len); #endif buf[buf_len-1] = '\0'; } /* Mapping direction */ typedef enum { LtoR, /* Map first field to second field */ RtoL /* Map second field to first field */ } MapDirection; static char * resolve_name( const char *lc_name, char *file_name, MapDirection direction) { FILE *fp; char buf[XLC_BUFSIZE], *name = NULL; fp = _XFopenFile (file_name, "r"); if (fp == NULL) return NULL; while (fgets(buf, XLC_BUFSIZE, fp) != NULL) { char *p = buf; int n; char *args[2], *from, *to; #ifdef __UNIXOS2__ /* Take out CR under OS/2 */ int len; len = strlen(p); if (len > 1) { if (*(p+len-2) == '\r' && *(p+len-1) == '\n') { *(p+len-2) = '\n'; *(p+len-1) = '\0'; } } #endif while (isspace(*p)) { ++p; } if (iscomment(*p)) { continue; } n = parse_line(p, args, 2); /* get first 2 fields */ if (n != 2) { continue; } if (direction == LtoR) { from = args[0], to = args[1]; /* left to right */ } else { from = args[1], to = args[0]; /* right to left */ } if (! strcmp(from, lc_name)) { name = Xmalloc(strlen(to) + 1); if (name != NULL) { strcpy(name, to); } break; } } fclose(fp); return name; } #define c_tolower(ch) ((ch) >= 'A' && (ch) <= 'Z' ? (ch) - 'A' + 'a' : (ch)) static char * lowercase( char *dst, const char *src) { const char *s; char *t; for (s = src, t = dst; *s; ++s, ++t) *t = c_tolower(*s); *t = '\0'; return dst; } /* * normalize_lcname(): remove any '_' and '-' and convert any character * to lower case after the <language>_<territory> part. If result is identical * to argument, free result and * return NULL. */ static char * normalize_lcname (const char *name) { char *p, *ret; const char *tmp = name; p = ret = Xmalloc(strlen(name) + 1); if (!p) return NULL; if (tmp) { while (*tmp && *tmp != '.' && *tmp != '@') *p++ = *tmp++; while (*tmp) { if (*tmp != '-') *p++ = c_tolower(*tmp); tmp++; } } *p = '\0'; if (strcmp(ret, name) == 0) { Xfree(ret); return NULL; } return ret; } /************************************************************************/ char * _XlcFileName( XLCd lcd, const char *category) { char *siname; char cat[XLC_BUFSIZE], dir[XLC_BUFSIZE]; int i, n; char *args[NUM_LOCALEDIR]; char *file_name = NULL; if (lcd == (XLCd)NULL) return NULL; siname = XLC_PUBLIC(lcd, siname); if (category) lowercase(cat, category); else cat[0] = '\0'; xlocaledir(dir,XLC_BUFSIZE); n = _XlcParsePath(dir, args, NUM_LOCALEDIR); for (i = 0; i < n; ++i) { char buf[PATH_MAX], *name; name = NULL; if ((5 + (args[i] ? strlen (args[i]) : 0) + strlen(cat)) < PATH_MAX) { sprintf(buf, "%s/%s.dir", args[i], cat); name = resolve_name(siname, buf, RtoL); } if (name == NULL) { continue; } if (*name == '/') { /* supposed to be absolute path name */ file_name = name; } else { file_name = Xmalloc(2 + (args[i] ? strlen (args[i]) : 0) + (name ? strlen (name) : 0)); if (file_name != NULL) sprintf(file_name, "%s/%s", args[i], name); Xfree(name); } if (isreadable(file_name)) { break; } Xfree(file_name); file_name = NULL; /* Then, try with next dir */ } return file_name; } /************************************************************************/ #ifndef LOCALE_ALIAS #define LOCALE_ALIAS "locale.alias" #endif int _XlcResolveLocaleName( const char* lc_name, XLCdPublicPart* pub) { char dir[PATH_MAX], buf[PATH_MAX], *name = NULL; char *dst; int i, n, sinamelen; char *args[NUM_LOCALEDIR]; static const char locale_alias[] = LOCALE_ALIAS; char *tmp_siname; char *nlc_name = NULL; xlocaledir (dir, PATH_MAX); n = _XlcParsePath(dir, args, NUM_LOCALEDIR); for (i = 0; i < n; ++i) { if ((2 + (args[i] ? strlen (args[i]) : 0) + strlen (locale_alias)) < PATH_MAX) { sprintf (buf, "%s/%s", args[i], locale_alias); name = resolve_name (lc_name, buf, LtoR); if (!name) { if (!nlc_name) nlc_name = normalize_lcname(lc_name); if (nlc_name) name = resolve_name (nlc_name, buf, LtoR); } } if (name != NULL) { break; } } if (nlc_name) Xfree(nlc_name); if (name == NULL) { /* vendor locale name == Xlocale name, no expansion of alias */ pub->siname = Xmalloc (strlen (lc_name) + 1); strcpy (pub->siname, lc_name); } else { pub->siname = name; } sinamelen = strlen (pub->siname); if (sinamelen == 1 && pub->siname[0] == 'C') { pub->language = pub->siname; pub->territory = pub->codeset = NULL; return 1; } /* * pub->siname is in the format <lang>_<terr>.<codeset>, typical would * be "en_US.ISO8859-1", "en_US.utf8", "ru_RU.KOI-8", or ja_JP.SJIS, * although it could be ja.SJIS too. */ tmp_siname = Xrealloc (pub->siname, 2 * (sinamelen + 1)); if (tmp_siname == NULL) { return 0; } pub->siname = tmp_siname; /* language */ dst = &pub->siname[sinamelen + 1]; strcpy (dst, pub->siname); pub->language = dst; /* territory */ dst = strchr (dst, '_'); if (dst) { *dst = '\0'; pub->territory = ++dst; } else dst = &pub->siname[sinamelen + 1]; /* codeset */ dst = strchr (dst, '.'); if (dst) { *dst = '\0'; pub->codeset = ++dst; } return (pub->siname[0] != '\0') ? 1 : 0; } /************************************************************************/ int _XlcResolveI18NPath(char *buf, int buf_len) { if (buf != NULL) { xlocaledir(buf, buf_len); } return 1; } char * _XlcLocaleDirName(char *dir_name, size_t dir_len, char *lc_name) { char dir[PATH_MAX], buf[PATH_MAX], *name = NULL; int i, n; char *args[NUM_LOCALEDIR]; static char locale_alias[] = LOCALE_ALIAS; char *target_name = (char*)0; char *target_dir = (char*)0; char *nlc_name = NULL; static char* last_dir_name = 0; static size_t last_dir_len = 0; static char* last_lc_name = 0; if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0 && dir_len >= last_dir_len) { strcpy (dir_name, last_dir_name); return dir_name; } xlocaledir (dir, PATH_MAX); n = _XlcParsePath(dir, args, 256); for (i = 0; i < n; ++i) { if ((2 + (args[i] ? strlen(args[i]) : 0) + strlen(locale_alias)) < PATH_MAX) { sprintf (buf, "%s/%s", args[i], locale_alias); name = resolve_name(lc_name, buf, LtoR); if (!name) { if (!nlc_name) nlc_name = normalize_lcname(lc_name); if (nlc_name) name = resolve_name (nlc_name, buf, LtoR); } } /* If name is not an alias, use lc_name for locale.dir search */ if (name == NULL) name = lc_name; /* look at locale.dir */ target_dir = args[i]; if (!target_dir) { /* something wrong */ if (name != lc_name) Xfree(name); continue; } if ((1 + (target_dir ? strlen (target_dir) : 0) + strlen("locale.dir")) < PATH_MAX) { sprintf(buf, "%s/locale.dir", target_dir); target_name = resolve_name(name, buf, RtoL); } if (name != lc_name) Xfree(name); if (target_name != NULL) { char *p = 0; if ((p = strstr(target_name, "/XLC_LOCALE"))) { *p = '\0'; break; } Xfree(target_name); target_name = NULL; } name = NULL; } if (nlc_name) Xfree(nlc_name); if (target_name == NULL) { /* vendor locale name == Xlocale name, no expansion of alias */ target_dir = args[0]; target_name = lc_name; } /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */ strncpy(dir_name, target_dir, dir_len - 1); if (strlen(target_dir) >= dir_len - 1) { dir_name[dir_len - 1] = '\0'; } else { strcat(dir_name, "/"); strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1); if (strlen(target_name) >= dir_len - strlen(dir_name) - 1) dir_name[dir_len - 1] = '\0'; } if (target_name != lc_name) Xfree(target_name); if (last_dir_name != 0) Xfree (last_dir_name); if (last_lc_name != 0) Xfree (last_lc_name); last_dir_len = strlen (dir_name) + 1; last_dir_name = Xmalloc (last_dir_len); strcpy (last_dir_name, dir_name); last_lc_name = Xmalloc (strlen (lc_name) + 1); strcpy (last_lc_name, lc_name); return dir_name; } char * _XlcLocaleLibDirName(char *dir_name, size_t dir_len, char *lc_name) { char dir[PATH_MAX], buf[PATH_MAX], *name = NULL; int i, n; char *args[NUM_LOCALEDIR]; static char locale_alias[] = LOCALE_ALIAS; char *target_name = (char*)0; char *target_dir = (char*)0; char *nlc_name = NULL; static char* last_dir_name = 0; static size_t last_dir_len = 0; static char* last_lc_name = 0; if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0 && dir_len >= last_dir_len) { strcpy (dir_name, last_dir_name); return dir_name; } xlocalelibdir (dir, PATH_MAX); n = _XlcParsePath(dir, args, 256); for (i = 0; i < n; ++i) { if ((2 + (args[i] ? strlen(args[i]) : 0) + strlen(locale_alias)) < PATH_MAX) { sprintf (buf, "%s/%s", args[i], locale_alias); name = resolve_name(lc_name, buf, LtoR); if (!name) { if (!nlc_name) nlc_name = normalize_lcname(lc_name); if (nlc_name) name = resolve_name (nlc_name, buf, LtoR); } } /* If name is not an alias, use lc_name for locale.dir search */ if (name == NULL) name = lc_name; /* look at locale.dir */ target_dir = args[i]; if (!target_dir) { /* something wrong */ if (name != lc_name) Xfree(name); continue; } if ((1 + (target_dir ? strlen (target_dir) : 0) + strlen("locale.dir")) < PATH_MAX) { sprintf(buf, "%s/locale.dir", target_dir); target_name = resolve_name(name, buf, RtoL); } if (name != lc_name) Xfree(name); if (target_name != NULL) { char *p = 0; if ((p = strstr(target_name, "/XLC_LOCALE"))) { *p = '\0'; break; } Xfree(target_name); target_name = NULL; } name = NULL; } if (nlc_name) Xfree(nlc_name); if (target_name == NULL) { /* vendor locale name == Xlocale name, no expansion of alias */ target_dir = args[0]; target_name = lc_name; } /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */ strncpy(dir_name, target_dir, dir_len - 1); if (strlen(target_dir) >= dir_len - 1) { dir_name[dir_len - 1] = '\0'; } else { strcat(dir_name, "/"); strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1); if (strlen(target_name) >= dir_len - strlen(dir_name) - 1) dir_name[dir_len - 1] = '\0'; } if (target_name != lc_name) Xfree(target_name); if (last_dir_name != 0) Xfree (last_dir_name); if (last_lc_name != 0) Xfree (last_lc_name); last_dir_len = strlen (dir_name) + 1; last_dir_name = Xmalloc (last_dir_len); strcpy (last_dir_name, dir_name); last_lc_name = Xmalloc (strlen (lc_name) + 1); strcpy (last_lc_name, lc_name); return dir_name; }