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