refactoring widget library
[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 rootfd;
34         int nrlocales;
35         char **locales;
36 };
37
38 static int validsubpath(const char *subpath)
39 {
40         int l = 0, i = 0;
41         if (subpath[i] == '/')
42                 return 0;
43         while(subpath[i]) {
44                 switch(subpath[i++]) {
45                 case '.':
46                         if (!subpath[i])
47                                 break;
48                         if (subpath[i] == '/') {
49                                 i++;
50                                 break;
51                         }
52                         if (subpath[i++] == '.') {
53                                 if (!subpath[i]) {
54                                         l--;
55                                         break;
56                                 }
57                                 if (subpath[i++] == '/') {
58                                         l--;
59                                         break;
60                                 }
61                         }
62                 default:
63                         while(subpath[i] && subpath[i] != '/')
64                                 i++;
65                         l++;
66                 case '/':
67                         break;
68                 }
69         }
70         return l >= 0;
71 }
72
73 struct wgt *wgt_create()
74 {
75         struct wgt *wgt = malloc(sizeof * wgt);
76         if (wgt) {
77                 wgt->rootfd = -1;
78                 wgt->nrlocales = 0;
79                 wgt->locales = NULL;
80         }
81         return wgt;
82 }
83
84 void wgt_disconnect(struct wgt *wgt)
85 {
86         assert(wgt);
87         if (wgt->rootfd >= 0)
88                 close(wgt->rootfd);
89         wgt->rootfd = -1;
90 }
91
92 void wgt_locales_reset(struct wgt *wgt)
93 {
94         assert(wgt);
95         while(wgt->nrlocales)
96                 free(wgt->locales[--wgt->nrlocales]);
97 }
98
99 void wgt_destroy(struct wgt *wgt)
100 {
101         if (wgt) {
102                 wgt_disconnect(wgt);
103                 wgt_locales_reset(wgt);
104                 free(wgt->locales);
105                 free(wgt);
106         }
107 }
108
109 int wgt_connect(struct wgt *wgt, const char *pathname)
110 {
111         int rfd;
112
113         assert(wgt);
114
115         rfd = AT_FDCWD;
116         if (pathname) {
117                 rfd = openat(rfd, pathname, O_PATH|O_DIRECTORY);
118                 if (rfd < 0)
119                         return rfd;
120         }
121         if (wgt->rootfd >= 0)
122                 close(wgt->rootfd);
123         wgt->rootfd = rfd;
124         return 0;
125 }
126
127 int wgt_is_connected(struct wgt *wgt)
128 {
129         assert(wgt);
130         return wgt->rootfd != -1;
131 }
132
133 int wgt_has(struct wgt *wgt, const char *filename)
134 {
135         assert(wgt);
136         assert(wgt_is_connected(wgt));
137         if (!validsubpath(filename)) {
138                 errno = EINVAL;
139                 return -1;
140         }
141         return 0 == faccessat(wgt->rootfd, filename, F_OK, 0);
142 }
143
144 int wgt_open_read(struct wgt *wgt, const char *filename)
145 {
146         assert(wgt);
147         assert(wgt_is_connected(wgt));
148         if (!validsubpath(filename)) {
149                 errno = EINVAL;
150                 return -1;
151         }
152         return openat(wgt->rootfd, filename, O_RDONLY);
153 }
154
155 static int locadd(struct wgt *wgt, const char *locstr, int length)
156 {
157         int i;
158         char *item, **ptr;
159
160         item = strndup(locstr, length);
161         if (item != NULL) {
162                 for (i = 0 ; item[i] ; i++)
163                         item[i] = tolower(item[i]);
164                 for (i = 0 ; i < wgt->nrlocales ; i++)
165                         if (!strcmp(item, wgt->locales[i])) {
166                                 free(item);
167                                 return 0;
168                         }
169
170                 ptr = realloc(wgt->locales, (1 + wgt->nrlocales) * sizeof(wgt->locales[0]));
171                 if (ptr) {
172                         wgt->locales = ptr;
173                         wgt->locales[wgt->nrlocales++] = item;
174                         return 0;
175                 }
176                 free(item);
177         }
178         errno = ENOMEM;
179         return -1;
180 }
181
182 int wgt_locales_add(struct wgt *wgt, const char *locstr)
183 {
184         const char *stop, *next;
185         assert(wgt);
186         while (*locstr) {
187                 stop = strchrnul(locstr, ',');
188                 next = stop + !!*stop;
189                 while (locstr != stop) {
190                         if (locadd(wgt, locstr, stop - locstr))
191                                 return -1;
192                         do { stop--; } while(stop > locstr && *stop != '-');
193                 }
194                 locstr = next;
195         }
196         return 0;
197 }
198
199 int wgt_locales_score(struct wgt *wgt, const char *lang)
200 {
201         int i;
202
203         assert(wgt);
204         if (lang)
205                 for (i = 0 ; i < wgt->nrlocales ; i++)
206                         if (!strcasecmp(lang, wgt->locales[i]))
207                                 return i;
208
209         return INT_MAX;
210 }
211
212 static const char *localize(struct wgt *wgt, const char *filename, char path[PATH_MAX])
213 {
214         int i;
215
216         if (!validsubpath(filename)) {
217                 errno = EINVAL;
218                 return NULL;
219         }
220         for (i = 0 ; i < wgt->nrlocales ; i++) {
221                 if (snprintf(path, PATH_MAX, "locales/%s/%s", wgt->locales[i], filename) >= PATH_MAX) {
222                         errno = EINVAL;
223                         return NULL;
224                 }
225                 if (0 == faccessat(wgt->rootfd, path, F_OK, 0))
226                         return path;
227         }
228         if (0 == faccessat(wgt->rootfd, filename, F_OK, 0))
229                 return filename;
230         errno = ENOENT;
231         return NULL;
232 }
233
234 char *wgt_locales_locate(struct wgt *wgt, const char *filename)
235 {
236         char path[PATH_MAX];
237         char * result;
238         const char * loc;
239
240         assert(wgt);
241         assert(wgt_is_connected(wgt));
242         loc = localize(wgt, filename, path);
243         if (!loc)
244                 result = NULL;
245         else {
246                 result = strdup(loc);
247                 if (!result)
248                         errno = ENOMEM;
249         }
250         return result;
251 }
252
253
254 int wgt_locales_open_read(struct wgt *wgt, const char *filename)
255 {
256         char path[PATH_MAX];
257         const char *loc;
258
259         assert(wgt);
260         assert(wgt_is_connected(wgt));
261         loc = localize(wgt, filename, path);
262         if (!loc)
263                 return -1;
264
265         return openat(wgt->rootfd, loc, O_RDONLY);
266 }
267
268