chiark / gitweb /
util: split-out path-util.[ch]
[elogind.git] / src / shared / unit-name.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <assert.h>
25
26 #include "path-util.h"
27 #include "util.h"
28 #include "unit-name.h"
29
30 #define VALID_CHARS                             \
31         "0123456789"                            \
32         "abcdefghijklmnopqrstuvwxyz"            \
33         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"            \
34         ":-_.\\"
35
36 bool unit_name_is_valid_no_type(const char *n, bool template_ok) {
37         const char *e, *i, *at;
38
39         /* Valid formats:
40          *
41          *         string@instance.suffix
42          *         string.suffix
43          */
44
45         assert(n);
46
47         if (strlen(n) >= UNIT_NAME_MAX)
48                 return false;
49
50         e = strrchr(n, '.');
51         if (!e || e == n)
52                 return false;
53
54         for (i = n, at = NULL; i < e; i++) {
55
56                 if (*i == '@' && !at)
57                         at = i;
58
59                 if (!strchr("@" VALID_CHARS, *i))
60                         return false;
61         }
62
63         if (at) {
64                 if (at == n)
65                         return false;
66
67                 if (!template_ok && at+1 == e)
68                         return false;
69         }
70
71         return true;
72 }
73
74 bool unit_instance_is_valid(const char *i) {
75         assert(i);
76
77         /* The max length depends on the length of the string, so we
78          * don't really check this here. */
79
80         if (i[0] == 0)
81                 return false;
82
83         /* We allow additional @ in the instance string, we do not
84          * allow them in the prefix! */
85
86         for (; *i; i++)
87                 if (!strchr("@" VALID_CHARS, *i))
88                         return false;
89
90         return true;
91 }
92
93 bool unit_prefix_is_valid(const char *p) {
94
95         /* We don't allow additional @ in the instance string */
96
97         if (p[0] == 0)
98                 return false;
99
100         for (; *p; p++)
101                 if (!strchr(VALID_CHARS, *p))
102                         return false;
103
104         return true;
105 }
106
107 int unit_name_to_instance(const char *n, char **instance) {
108         const char *p, *d;
109         char *i;
110
111         assert(n);
112         assert(instance);
113
114         /* Everything past the first @ and before the last . is the instance */
115         if (!(p = strchr(n, '@'))) {
116                 *instance = NULL;
117                 return 0;
118         }
119
120         assert_se(d = strrchr(n, '.'));
121         assert(p < d);
122
123         if (!(i = strndup(p+1, d-p-1)))
124                 return -ENOMEM;
125
126         *instance = i;
127         return 0;
128 }
129
130 char *unit_name_to_prefix_and_instance(const char *n) {
131         const char *d;
132
133         assert(n);
134
135         assert_se(d = strrchr(n, '.'));
136
137         return strndup(n, d - n);
138 }
139
140 char *unit_name_to_prefix(const char *n) {
141         const char *p;
142
143         if ((p = strchr(n, '@')))
144                 return strndup(n, p - n);
145
146         return unit_name_to_prefix_and_instance(n);
147 }
148
149 char *unit_name_change_suffix(const char *n, const char *suffix) {
150         char *e, *r;
151         size_t a, b;
152
153         assert(n);
154         assert(unit_name_is_valid_no_type(n, true));
155         assert(suffix);
156
157         assert_se(e = strrchr(n, '.'));
158         a = e - n;
159         b = strlen(suffix);
160
161         if (!(r = new(char, a + b + 1)))
162                 return NULL;
163
164         memcpy(r, n, a);
165         memcpy(r+a, suffix, b+1);
166
167         return r;
168 }
169
170 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
171         assert(prefix);
172         assert(unit_prefix_is_valid(prefix));
173         assert(!instance || unit_instance_is_valid(instance));
174         assert(suffix);
175
176         if (!instance)
177                 return strappend(prefix, suffix);
178
179         return join(prefix, "@", instance, suffix, NULL);
180 }
181
182 static char* do_escape(const char *f, char *t) {
183         assert(f);
184         assert(t);
185
186         for (; *f; f++) {
187                 if (*f == '/')
188                         *(t++) = '-';
189                 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) {
190                         *(t++) = '\\';
191                         *(t++) = 'x';
192                         *(t++) = hexchar(*f >> 4);
193                         *(t++) = hexchar(*f);
194                 } else
195                         *(t++) = *f;
196         }
197
198         return t;
199 }
200
201 char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix) {
202         char *r, *t;
203         size_t a, b, c;
204
205         assert(prefix);
206         assert(suffix);
207
208         /* Takes a arbitrary string for prefix and instance plus a
209          * suffix and makes a nice string suitable as unit name of it,
210          * escaping all weird chars on the way.
211          *
212          * / becomes ., and all chars not allowed in a unit name get
213          * escaped as \xFF, including \ and ., of course. This
214          * escaping is hence reversible.
215          *
216          * This is primarily useful to make nice unit names from
217          * strings, but is actually useful for any kind of string.
218          */
219
220         a = strlen(prefix);
221         c = strlen(suffix);
222
223         if (instance) {
224                 b = strlen(instance);
225
226                 if (!(r = new(char, a*4 + 1 + b*4 + c + 1)))
227                         return NULL;
228
229                 t = do_escape(prefix, r);
230                 *(t++) = '@';
231                 t = do_escape(instance, t);
232         } else {
233
234                 if (!(r = new(char, a*4 + c + 1)))
235                         return NULL;
236
237                 t = do_escape(prefix, r);
238         }
239
240         strcpy(t, suffix);
241         return r;
242 }
243
244 char *unit_name_escape(const char *f) {
245         char *r, *t;
246
247         if (!(r = new(char, strlen(f)*4+1)))
248                 return NULL;
249
250         t = do_escape(f, r);
251         *t = 0;
252
253         return r;
254
255 }
256
257 char *unit_name_unescape(const char *f) {
258         char *r, *t;
259
260         assert(f);
261
262         if (!(r = strdup(f)))
263                 return NULL;
264
265         for (t = r; *f; f++) {
266                 if (*f == '-')
267                         *(t++) = '/';
268                 else if (*f == '\\') {
269                         int a, b;
270
271                         if (f[1] != 'x' ||
272                             (a = unhexchar(f[2])) < 0 ||
273                             (b = unhexchar(f[3])) < 0) {
274                                 /* Invalid escape code, let's take it literal then */
275                                 *(t++) = '\\';
276                         } else {
277                                 *(t++) = (char) ((a << 4) | b);
278                                 f += 3;
279                         }
280                 } else
281                         *(t++) = *f;
282         }
283
284         *t = 0;
285
286         return r;
287 }
288
289 bool unit_name_is_template(const char *n) {
290         const char *p;
291
292         assert(n);
293
294         if (!(p = strchr(n, '@')))
295                 return false;
296
297         return p[1] == '.';
298 }
299
300 char *unit_name_replace_instance(const char *f, const char *i) {
301         const char *p, *e;
302         char *r, *k;
303         size_t a;
304
305         assert(f);
306
307         p = strchr(f, '@');
308         assert_se(e = strrchr(f, '.'));
309
310         a = p - f;
311
312         if (p) {
313                 size_t b;
314
315                 b = strlen(i);
316
317                 if (!(r = new(char, a + 1 + b + strlen(e) + 1)))
318                         return NULL;
319
320                 k = mempcpy(r, f, a + 1);
321                 k = mempcpy(k, i, b);
322         } else {
323
324                 if (!(r = new(char, a + strlen(e) + 1)))
325                         return NULL;
326
327                 k = mempcpy(r, f, a);
328         }
329
330         strcpy(k, e);
331         return r;
332 }
333
334 char *unit_name_template(const char *f) {
335         const char *p, *e;
336         char *r;
337         size_t a;
338
339         if (!(p = strchr(f, '@')))
340                 return strdup(f);
341
342         assert_se(e = strrchr(f, '.'));
343         a = p - f + 1;
344
345         if (!(r = new(char, a + strlen(e) + 1)))
346                 return NULL;
347
348         strcpy(mempcpy(r, f, a), e);
349         return r;
350
351 }
352
353 char *unit_name_from_path(const char *path, const char *suffix) {
354         char *p, *r;
355
356         assert(path);
357         assert(suffix);
358
359         if (!(p = strdup(path)))
360                 return NULL;
361
362         path_kill_slashes(p);
363
364         path = p[0] == '/' ? p + 1 : p;
365
366         if (path[0] == 0) {
367                 free(p);
368                 return strappend("-", suffix);
369         }
370
371         r = unit_name_build_escape(path, NULL, suffix);
372         free(p);
373
374         return r;
375 }
376
377 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
378         char *p, *r;
379
380         assert(path);
381         assert(suffix);
382
383         if (!(p = strdup(path)))
384                 return NULL;
385
386         path_kill_slashes(p);
387
388         path = p[0] == '/' ? p + 1 : p;
389
390         if (path[0] == 0) {
391                 free(p);
392                 return unit_name_build_escape(prefix, "-", suffix);
393         }
394
395         r = unit_name_build_escape(prefix, path, suffix);
396         free(p);
397
398         return r;
399 }
400
401 char *unit_name_to_path(const char *name) {
402         char *w, *e;
403
404         assert(name);
405
406         if (!(w = unit_name_to_prefix(name)))
407                 return NULL;
408
409         e = unit_name_unescape(w);
410         free(w);
411
412         if (!e)
413                 return NULL;
414
415         if (e[0] != '/') {
416                 w = strappend("/", e);
417                 free(e);
418
419                 if (!w)
420                         return NULL;
421
422                 e = w;
423         }
424
425         return e;
426 }
427
428 char *unit_name_path_unescape(const char *f) {
429         char *e;
430
431         assert(f);
432
433         if (!(e = unit_name_unescape(f)))
434                 return NULL;
435
436         if (e[0] != '/') {
437                 char *w;
438
439                 w = strappend("/", e);
440                 free(e);
441
442                 if (!w)
443                         return NULL;
444
445                 e = w;
446         }
447
448         return e;
449 }