2 Copyright (C) 2015-2018 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
37 unsigned int nrlocales;
41 /* a valid subpath is a relative path not looking deeper than root using .. */
42 static int validsubpath(const char *subpath)
46 /* absolute path is not valid */
47 if (subpath[i] == '/')
50 /* inspect the path */
52 switch(subpath[i++]) {
56 if (subpath[i] == '/') {
60 if (subpath[i++] == '.') {
65 if (subpath[i++] == '/') {
71 while(subpath[i] && subpath[i] != '/')
83 * Normalizes and checks the 'subpath'.
84 * Removes any starting '/' and checks that 'subpath'
85 * does not contains sequence of '..' going deeper than
87 * Returns the normalized subpath or NULL in case of
90 static const char *normalsubpath(const char *subpath)
92 while(*subpath == '/')
94 return validsubpath(subpath) ? subpath : NULL;
98 * Creates a wgt handler and returns it or return NULL
99 * in case of memory depletion.
101 struct wgt *wgt_create()
103 struct wgt *wgt = malloc(sizeof * wgt);
116 * Adds a reference to 'wgt'
118 void wgt_addref(struct wgt *wgt)
125 * Drops a reference to 'wgt' and destroys it
126 * if not more referenced
128 void wgt_unref(struct wgt *wgt)
131 if (!--wgt->refcount) {
133 wgt_locales_reset(wgt);
140 * Creates a wgt handle and connect it to 'dirfd' and 'pathname'.
142 * Returns the created and connected wgt handle on success
143 * or returns NULL if allocation failed or connecting had
146 struct wgt *wgt_createat(int dirfd, const char *pathname)
148 struct wgt *wgt = wgt_create();
150 if (wgt_connectat(wgt, dirfd, pathname)) {
159 * Connect 'wgt' to the directory of 'pathname' relative
160 * to the directory handled by 'dirfd'.
162 * Use AT_FDCWD for connecting relatively to the current directory.
164 * Use 'pathname' == NULL or "" for connecting to 'dirfd'. In
165 * that case, 'dirfd' is duplicated and can safely be used later
168 * If 'wgt' is already connected, it will be diconnected before.
170 * The languages settings are not changed.
172 * Returns 0 in case of success or -1 in case or error.
174 int wgt_connectat(struct wgt *wgt, int dirfd, const char *pathname)
180 rfd = (pathname && *pathname) ? openat(dirfd, pathname, O_PATH|O_DIRECTORY) : dup(dirfd);
184 if (wgt->rootfd >= 0)
191 * Connect 'wgt' to the directory of 'pathname'.
193 * Acts like wgt_connectat(wgt, AT_FDCWD, pathname)
195 int wgt_connect(struct wgt *wgt, const char *pathname)
197 return wgt_connectat(wgt, AT_FDCWD, pathname);
201 * Disconnetcs 'wgt' if connected.
203 void wgt_disconnect(struct wgt *wgt)
206 if (wgt->rootfd >= 0)
212 * Checks if 'wgt' is connected and returns 1 if connected
213 * or 0 if not connected.
215 int wgt_is_connected(struct wgt *wgt)
218 return wgt->rootfd != -1;
222 * Tests wether the connected 'wgt' has the 'filename'.
224 * It is an error (with errno = EINVAL) to test an
227 * Returns 0 if it hasn't it, 1 if it has it or
228 * -1 if an error occured.
230 int wgt_has(struct wgt *wgt, const char *filename)
233 assert(wgt_is_connected(wgt));
235 filename = normalsubpath(filename);
240 return 0 == faccessat(wgt->rootfd, filename, F_OK, 0);
244 * Opens 'filename' for read from the connected 'wgt'.
246 * Returns the file descriptor as returned by openat
247 * system call or -1 in case of error.
249 int wgt_open_read(struct wgt *wgt, const char *filename)
252 assert(wgt_is_connected(wgt));
253 filename = normalsubpath(filename);
258 return openat(wgt->rootfd, filename, O_RDONLY);
262 * Adds if needed the locale 'locstr' of 'length'
263 * to the list of locales.
265 static int locadd(struct wgt *wgt, const char *locstr, size_t length)
270 item = strndup(locstr, length);
272 /* normalize in lower case */
273 for (i = 0 ; item[i] ; i++)
274 item[i] = (char)tolower(item[i]);
276 /* search it (no duplication) */
277 for (i = 0 ; i < wgt->nrlocales ; i++)
278 if (!strcmp(item, wgt->locales[i])) {
283 /* append it to the list */
284 ptr = realloc(wgt->locales, (1 + wgt->nrlocales) * sizeof(wgt->locales[0]));
287 wgt->locales[wgt->nrlocales++] = item;
297 * clears the list of locales of 'wgt'
299 void wgt_locales_reset(struct wgt *wgt)
302 while(wgt->nrlocales)
303 free(wgt->locales[--wgt->nrlocales]);
307 * Adds to 'wgt' the locales defined by 'locstr'.
309 * Example: passing "fr-CH,en-GB" will add "fr-CH",
310 * "fr", "en-GB" and then "en" to the list of locales.
312 * Returns 0 in case of success or -1 in case of memory
315 int wgt_locales_add(struct wgt *wgt, const char *locstr)
317 const char *stop, *next;
320 /* iterate the comma separated languages */
322 stop = strchrnul(locstr, ',');
323 next = stop + !!*stop;
324 /* iterate variant of languages in reverse order */
325 while (locstr != stop) {
326 if (locadd(wgt, locstr, (size_t)(stop - locstr)))
328 do { stop--; } while(stop > locstr && *stop != '-');
336 * Get the score of the language 'lang' for 'wgt'.
338 * The lower result means the higher priority of the language.
339 * The returned value of 0 is the top first priority.
341 unsigned int wgt_locales_score(struct wgt *wgt, const char *lang)
347 for (i = 0 ; i < wgt->nrlocales ; i++)
348 if (!strcasecmp(lang, wgt->locales[i]))
355 * Applies the localisation algorithm of 'filename'
356 * within 'wgt'. Use the scratch buffer given by 'path'.
358 * Returns the filepath of the located file or NULL
359 * if not found. If not NULL, the returned value is either
360 * 'path' or the normalized version of 'filename'.
362 static const char *localize(struct wgt *wgt, const char *filename, char path[PATH_MAX])
366 /* get the normalized name */
367 filename = normalsubpath(filename);
373 /* search in locales */
374 for (i = 0 ; i < wgt->nrlocales ; i++) {
375 if (snprintf(path, PATH_MAX, "locales/%s/%s", wgt->locales[i], filename) >= PATH_MAX) {
379 if (0 == faccessat(wgt->rootfd, path, F_OK, 0))
382 if (0 == faccessat(wgt->rootfd, filename, F_OK, 0))
389 * Gets the localized file of 'filename' within 'wgt'.
391 * Returns a fresh allocated string for the found 'filename'.
392 * Returns NULL if file is not found (ENOENT) or memory
393 * exhausted (ENOMEM).
395 char *wgt_locales_locate(struct wgt *wgt, const char *filename)
402 assert(wgt_is_connected(wgt));
404 loc = localize(wgt, filename, path);
408 result = strdup(loc);
416 * Opens for read the localized version of 'filename'
417 * from the connected 'wgt'.
419 * Returns the file descriptor as returned by openat
420 * system call or -1 in case of error.
422 int wgt_locales_open_read(struct wgt *wgt, const char *filename)
428 assert(wgt_is_connected(wgt));
430 loc = localize(wgt, filename, path);
434 return openat(wgt->rootfd, loc, O_RDONLY);
438 #if defined(TEST_wgt_validsubpath)
440 void t(const char *subpath, int validity) {
441 printf("%s -> %d = %d, %s\n", subpath, validity, validsubpath(subpath), validsubpath(subpath)==validity ? "ok" : "NOT OK");
453 t("a/b/c/../../..",1);
454 t("a/b/c/../../../.",1);
455 t("./..a/././..b/..c/./.././.././../.",1);
456 t("./..a/././..b/..c/./.././.././.././..",0);
457 t("./..a//.//./..b/..c/./.././/./././///.././.././a/a/a/a/a",1);