9c3cd6eee6468d515e30e891bd9b309427e8d82e
[src/app-framework-main.git] / src / wgt.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <ctype.h>
28
29
30 #include "wgt.h"
31
32 struct wgt {
33         int refcount;
34         int rootfd;
35         int nrlocales;
36         char **locales;
37 };
38
39 static int validsubpath(const char *subpath)
40 {
41         int l = 0, i = 0;
42         if (subpath[i] == '/')
43                 return 0;
44         while(subpath[i]) {
45                 switch(subpath[i++]) {
46                 case '.':
47                         if (!subpath[i])
48                                 break;
49                         if (subpath[i] == '/') {
50                                 i++;
51                                 break;
52                         }
53                         if (subpath[i++] == '.') {
54                                 if (!subpath[i]) {
55                                         l--;
56                                         break;
57                                 }
58                                 if (subpath[i++] == '/') {
59                                         l--;
60                                         break;
61                                 }
62                         }
63                 default:
64                         while(subpath[i] && subpath[i] != '/')
65                                 i++;
66                         l++;
67                 case '/':
68                         break;
69                 }
70         }
71         return l >= 0;
72 }
73
74 static const char *normalsubpath(const char *subpath)
75 {
76         while(*subpath == '/')
77                 subpath++;
78         return validsubpath(subpath) ? subpath : NULL;
79 }
80
81 struct wgt *wgt_create()
82 {
83         struct wgt *wgt = malloc(sizeof * wgt);
84         if (!wgt)
85                 errno = ENOMEM;
86         else {
87                 wgt->refcount = 1;
88                 wgt->rootfd = -1;
89                 wgt->nrlocales = 0;
90                 wgt->locales = NULL;
91         }
92         return wgt;
93 }
94
95 void wgt_disconnect(struct wgt *wgt)
96 {
97         assert(wgt);
98         if (wgt->rootfd >= 0)
99                 close(wgt->rootfd);
100         wgt->rootfd = -1;
101 }
102
103 void wgt_locales_reset(struct wgt *wgt)
104 {
105         assert(wgt);
106         while(wgt->nrlocales)
107                 free(wgt->locales[--wgt->nrlocales]);
108 }
109
110 void wgt_addref(struct wgt *wgt)
111 {
112         assert(wgt);
113         wgt->refcount++;
114 }
115
116 void wgt_unref(struct wgt *wgt)
117 {
118         assert(wgt);
119         if (!--wgt->refcount) {
120                 wgt_disconnect(wgt);
121                 wgt_locales_reset(wgt);
122                 free(wgt->locales);
123                 free(wgt);
124         }
125 }
126
127 int wgt_connectat(struct wgt *wgt, int dirfd, const char *pathname)
128 {
129         int rfd;
130
131         assert(wgt);
132
133         rfd = dirfd;
134         if (pathname) {
135                 rfd = openat(rfd, pathname, O_PATH|O_DIRECTORY);
136                 if (rfd < 0)
137                         return rfd;
138         }
139         if (wgt->rootfd >= 0)
140                 close(wgt->rootfd);
141         wgt->rootfd = rfd;
142         return 0;
143 }
144
145 int wgt_connect(struct wgt *wgt, const char *pathname)
146 {
147         return wgt_connectat(wgt, AT_FDCWD, pathname);
148 }
149
150 int wgt_is_connected(struct wgt *wgt)
151 {
152         assert(wgt);
153         return wgt->rootfd != -1;
154 }
155
156 int wgt_has(struct wgt *wgt, const char *filename)
157 {
158         assert(wgt);
159         assert(wgt_is_connected(wgt));
160
161         filename = normalsubpath(filename);
162         if (!filename) {
163                 errno = EINVAL;
164                 return -1;
165         }
166         return 0 == faccessat(wgt->rootfd, filename, F_OK, 0);
167 }
168
169 int wgt_open_read(struct wgt *wgt, const char *filename)
170 {
171         assert(wgt);
172         assert(wgt_is_connected(wgt));
173         filename = normalsubpath(filename);
174         if (!filename) {
175                 errno = EINVAL;
176                 return -1;
177         }
178         return openat(wgt->rootfd, filename, O_RDONLY);
179 }
180
181 static int locadd(struct wgt *wgt, const char *locstr, int length)
182 {
183         int i;
184         char *item, **ptr;
185
186         item = strndup(locstr, length);
187         if (item != NULL) {
188                 for (i = 0 ; item[i] ; i++)
189                         item[i] = tolower(item[i]);
190                 for (i = 0 ; i < wgt->nrlocales ; i++)
191                         if (!strcmp(item, wgt->locales[i])) {
192                                 free(item);
193                                 return 0;
194                         }
195
196                 ptr = realloc(wgt->locales, (1 + wgt->nrlocales) * sizeof(wgt->locales[0]));
197                 if (ptr) {
198                         wgt->locales = ptr;
199                         wgt->locales[wgt->nrlocales++] = item;
200                         return 0;
201                 }
202                 free(item);
203         }
204         errno = ENOMEM;
205         return -1;
206 }
207
208 int wgt_locales_add(struct wgt *wgt, const char *locstr)
209 {
210         const char *stop, *next;
211         assert(wgt);
212         while (*locstr) {
213                 stop = strchrnul(locstr, ',');
214                 next = stop + !!*stop;
215                 while (locstr != stop) {
216                         if (locadd(wgt, locstr, stop - locstr))
217                                 return -1;
218                         do { stop--; } while(stop > locstr && *stop != '-');
219                 }
220                 locstr = next;
221         }
222         return 0;
223 }
224
225 int wgt_locales_score(struct wgt *wgt, const char *lang)
226 {
227         int i;
228
229         assert(wgt);
230         if (lang)
231                 for (i = 0 ; i < wgt->nrlocales ; i++)
232                         if (!strcasecmp(lang, wgt->locales[i]))
233                                 return i;
234
235         return INT_MAX;
236 }
237
238 static const char *localize(struct wgt *wgt, const char *filename, char path[PATH_MAX])
239 {
240         int i;
241
242         filename = normalsubpath(filename);
243         if (!filename) {
244                 errno = EINVAL;
245                 return NULL;
246         }
247         for (i = 0 ; i < wgt->nrlocales ; i++) {
248                 if (snprintf(path, PATH_MAX, "locales/%s/%s", wgt->locales[i], filename) >= PATH_MAX) {
249                         errno = EINVAL;
250                         return NULL;
251                 }
252                 if (0 == faccessat(wgt->rootfd, path, F_OK, 0))
253                         return path;
254         }
255         if (0 == faccessat(wgt->rootfd, filename, F_OK, 0))
256                 return filename;
257         errno = ENOENT;
258         return NULL;
259 }
260
261 char *wgt_locales_locate(struct wgt *wgt, const char *filename)
262 {
263         char path[PATH_MAX];
264         char * result;
265         const char * loc;
266
267         assert(wgt);
268         assert(wgt_is_connected(wgt));
269         loc = localize(wgt, filename, path);
270         if (!loc)
271                 result = NULL;
272         else {
273                 result = strdup(loc);
274                 if (!result)
275                         errno = ENOMEM;
276         }
277         return result;
278 }
279
280
281 int wgt_locales_open_read(struct wgt *wgt, const char *filename)
282 {
283         char path[PATH_MAX];
284         const char *loc;
285
286         assert(wgt);
287         assert(wgt_is_connected(wgt));
288         loc = localize(wgt, filename, path);
289         if (!loc)
290                 return -1;
291
292         return openat(wgt->rootfd, loc, O_RDONLY);
293 }
294
295