1 /*
2 LibRCC - Module providing file names recoding
3
4 Copyright (C) 2005-2008 Suren A. Chilingaryan <csa@dside.dyndns.org>
5
6 This library is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License version 2.1 or later
8 as published by the Free Software Foundation.
9
10 This library is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include "../config.h"
26
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif /* HAVE_UNISTD_H */
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif /* HAVE_SYS_TYPES_H */
34
35 #ifdef HAVE_SYS_STAT_H
36 # include <sys/stat.h>
37 #endif /* HAVE_SYS_STAT_H */
38
39 #ifdef HAVE_MNTENT_H
40 # include <mntent.h>
41 #endif /* HAVE_MNTENT_H */
42
43 #include "internal.h"
44 #include "rcciconv.h"
45 #include "rccconfig.h"
46
47 #ifndef strndup
rccStrndup(const char * str,size_t n)48 static char *rccStrndup(const char *str, size_t n) {
49 char *res;
50
51 n = STRNLEN(str, n);
52 res = (char*)malloc((n+1)*sizeof(char));
53 if (!res) return res;
54 strncpy(res, str, n);
55 res[n] = 0;
56 return res;
57 }
58 #define strndup rccStrndup
59 #endif /* !strndup */
60
rccCreateFullName(const char * path,const char * filename)61 static char *rccCreateFullName(const char *path, const char *filename) {
62 unsigned int i;
63 char *name;
64
65 if (!path) {
66 if (filename) return strdup(filename);
67 else return strdup("/");
68 } else if (!filename) return strdup(path);
69
70
71 i = strlen(path);
72 name = (char*)malloc((i+strlen(filename)+2)*sizeof(char));
73 if (!name) return NULL;
74
75 if ((path[i-1]=='/')||(filename[0]=='/'))
76 sprintf(name, "%s%s", path, filename);
77 else
78 sprintf(name, "%s/%s", path, filename);
79
80 return name;
81 }
82
rccIsFile(const char * filename)83 static int rccIsFile(const char *filename) {
84 struct stat st;
85
86 #ifdef HAVE_SYS_STAT_H
87 # ifdef S_ISREG
88 if ((!stat(filename,&st))&&(S_ISREG(st.st_mode))) return 1;
89 # else /* S_ISREG */
90 if (!stat(filename,&st)) return 1;
91 # endif /* S_ISREG */
92 #endif /* HAVE_SYS_STAT_H */
93 return 0;
94 }
95
rccCheckFile(const char * prefix,const char * name)96 static char *rccCheckFile(const char *prefix, const char *name) {
97 char *temp;
98
99 temp = rccCreateFullName(prefix, name);
100 if ((!temp)||(rccIsFile(temp))) return temp;
101
102 free(temp);
103 return NULL;
104 }
105
106 /* Converts: 'filename' to 'prefix/name' using 'fspath' */
rccFS0(rcc_language_config config,const char * fspath,const char * filename,char ** prefix,char ** name)107 int rccFS0(rcc_language_config config, const char *fspath, const char *filename, char **prefix, char **name) {
108 #ifdef HAVE_MNTENT_H
109 FILE *mtab;
110 struct mntent *fsentry;
111 char *lastprefix;
112 #endif /* HAVE_MNTENT_H */
113
114 const char *tmp = NULL;
115 size_t len;
116
117 if (fspath) {
118 len = strlen(fspath);
119 if (!len) return 1;
120
121 if (!strncmp(filename, fspath, len)) tmp = filename + strlen(fspath);
122 #ifdef HAVE_MNTENT_H
123 } else {
124 lastprefix = config->ctx->lastprefix;
125
126 /* only required with non-english mount directories */
127 len = strlen(lastprefix);
128 if ((len)&&(!strncmp(filename, lastprefix, len))) {
129 tmp = filename + len;
130 }
131
132 if (tmp) mtab = NULL;
133 else mtab = setmntent(_PATH_MNTTAB, "r");
134 if (mtab) {
135 while (!feof(mtab)) {
136 fsentry = getmntent(mtab);
137 if ((fsentry)&&(fsentry->mnt_dir)) {
138 len = strlen(fsentry->mnt_dir);
139 if (len > 1) {
140 if (!strncmp(filename, fsentry->mnt_dir, len)) {
141 tmp = filename + len;
142 if (len<RCC_MAX_PREFIX_CHARS) strcpy(lastprefix, fsentry->mnt_dir);
143 break;
144 }
145 }
146 }
147 }
148 endmntent(mtab);
149 }
150 #endif /* HAVE_MNTENT_H */
151 }
152
153 if (!tmp) return 1;
154
155 *name = strdup(tmp);
156 *prefix = strndup(filename, (tmp-filename));
157
158 if ((!*name)||(!*prefix)) {
159 if (*name) free(*name);
160 if (*prefix) free(*prefix);
161 return -1;
162 }
163
164 return 0;
165 }
166
167 /* Normalizes 'prefix/name' using 'fspath'
168 returns:
169 -1 Error
170 0 Okey
171 bit 1 Exact Match
172 bit 2 Memory cleanup isn't required
173 */
rccFS1(rcc_language_config config,const char * fspath,char ** prefix,char ** name)174 int rccFS1(rcc_language_config config, const char *fspath, char **prefix, char **name) {
175 char *result;
176 char *path, *filename;
177
178 path = *prefix;
179 filename = *name;
180
181
182 if ((path)&&(filename)) {
183 result = rccCreateFullName(path, filename);
184 if (!result) return -1;
185 } else if (filename) result = filename;
186 else if (path) result = path;
187 else return -1;
188
189 if (rccIsASCII(result)) {
190 *name = result;
191 if ((path)&&(filename)) return 1;
192 return 3;
193 }
194
195 // Checking without recoding in case of autodetection
196 if (rccGetOption(config->ctx, RCC_OPTION_AUTODETECT_FS_NAMES)) {
197 if (rccIsFile(result)) {
198 *prefix = NULL;
199 *name = result;
200
201 if ((path)&&(filename)) return 1;
202 return 3;
203 }
204 }
205
206 if (rccFS0(config, fspath, result, prefix, name)) {
207 *prefix = NULL;
208 *name = result;
209
210 if ((path)&&(filename)) return 0;
211 return 2;
212 }
213
214 if ((path)&&(filename)) free(result);
215
216 return 0;
217 }
218
219 /* Checks if 'prefix/name' is accessible using 'icnv' recoding. In case of
220 sucess returns pointer on statically allocated memory, and NULL overwise */
rccFS2(rcc_language_config config,iconv_t icnv,const char * prefix,const char * name)221 char *rccFS2(rcc_language_config config, iconv_t icnv, const char *prefix, const char *name) {
222 size_t size;
223 char *tmpbuffer = config->ctx->tmpbuffer;
224
225 if (icnv) {
226 size = rccIConvInternal(config->ctx, icnv, name, 0);
227 if (size == (size_t)-1) return NULL;
228 } else {
229 strncpy(tmpbuffer, name, RCC_MAX_STRING_CHARS);
230 tmpbuffer[RCC_MAX_STRING_CHARS] = 0;
231 }
232
233 return rccCheckFile(prefix, tmpbuffer);
234 }
235
236 /* Tries to find 'name' encoding in 'prefix/name' file. Returns pointer on
237 statically allocated string with correct filename or NULL. */
rccFS3(rcc_language_config config,rcc_class_id class_id,const char * prefix,const char * name)238 char *rccFS3(rcc_language_config config, rcc_class_id class_id, const char *prefix, const char *name) {
239 unsigned int i;
240 char *result;
241 rcc_charset charset;
242 rcc_language *language;
243 iconv_t icnv = config->fsiconv;
244
245 if ((rccGetOption(config->ctx, RCC_OPTION_AUTODETECT_FS_NAMES))&&(icnv)) {
246 result = rccFS2(config, icnv, prefix, name);
247 if (result) return result;
248 }
249
250 result = rccFS2(config, config->iconv_to[class_id], prefix, name);
251 if (result) {
252 if (icnv) rccIConvClose(icnv);
253 config->fsiconv = NULL;
254 return result;
255 }
256
257 if (rccGetOption(config->ctx, RCC_OPTION_AUTODETECT_FS_NAMES)) {
258 language = config->language;
259 if (language->charsets[0]) {
260 for (i=1;(!result);i++) {
261 charset = language->charsets[i];
262 if (!charset) break;
263
264 if (icnv) rccIConvClose(icnv);
265 if (rccIsUTF8(charset)) icnv = NULL;
266 else {
267 icnv = rccIConvOpen(charset, "UTF-8");
268 }
269
270 result = rccFS2(config, icnv, prefix, name);
271 }
272 }
273 }
274 if (result) config->fsiconv = icnv;
275 else {
276 if (icnv) rccIConvClose(icnv);
277 config->fsiconv = NULL;
278 }
279
280 return result;
281 }
282
rccFS5(rcc_context ctx,rcc_language_config config,rcc_class_id class_id,const char * utfstring)283 char *rccFS5(rcc_context ctx, rcc_language_config config, rcc_class_id class_id, const char *utfstring) {
284 int err;
285 char *prefix, *name;
286 char *result;
287
288 if (rccIsASCII(utfstring)) return strdup(utfstring);
289
290 name = NULL;
291 prefix = NULL;
292
293 rccMutexLock(ctx->mutex);
294 rccMutexLock(config->mutex);
295 err = rccFS0(config, NULL, utfstring, &prefix, &name);
296 if (err>=0) {
297 result = rccFS3(config, class_id, prefix, name);
298 rccMutexUnLock(config->mutex);
299 rccMutexUnLock(ctx->mutex);
300 if (!err) {
301 if (prefix) free(prefix);
302 free(name);
303 }
304 return result;
305 }
306 rccMutexUnLock(config->mutex);
307 rccMutexUnLock(ctx->mutex);
308
309 return NULL;
310 }
311