summaryrefslogtreecommitdiffstats
path: root/systemd/systemd.c
blob: c7bdb4d28bb9037a6fe0ad9f0955dc25bab743ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * Helper functions for systemd generators in nfs-utils.
 *
 * Currently just systemd_escape().
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "systemd.h"

static const char hex[16] =
{
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
};

/*
 * determine length of the string that systemd_escape() needs to allocate
 */
static int systemd_len(char *path)
{
	char *p;
	int len = 0;

	p = path;
	while (*p == '/')
		/* multiple leading "/" are ignored */
		p++;

	if (!*p)
		/* root directory "/" becomes is encoded as a single "-" */
		return 1;

	if (*p == '.')
		/*
		 * replace "." with "\x2d" escape sequence if
		 * it's the first character in escaped path
		 * */
		len += 4;

	while (*p) {
		unsigned char c = *p++;

		if (c == '/') {
			/* multiple non-trailing slashes become '-' */
			while (*p == '/')
				p++;
			if (*p)
				len++;
		} else if (isalnum(c) || c == ':' || c == '.' || c == '_')
			/* these characters are not replaced */
			len++;
		else
			/* replace with "\x2d" escape sequence */
			len += 4;
	}

	return len;
}

/*
 * convert c to "\x2d" escape sequence and append to string
 * at position p, advancing p
 */
static char *hexify(unsigned char c, char *p)
{
	*p++ = '\\';
	*p++ = 'x';
	*p++ = hex[c >> 4];
	*p++ = hex[c & 0xf];
	return p;
}

/*
 * convert a path to a unit name according to the logic in systemd.unit(5):
 *
 *     Basically, given a path, "/" is replaced by "-", and all other
 *     characters which are not ASCII alphanumerics are replaced by C-style
 *     "\x2d" escapes (except that "_" is never replaced and "." is only
 *     replaced when it would be the first character in the escaped path).
 *     The root directory "/" is encoded as single dash, while otherwise the
 *     initial and ending "/" are removed from all paths during
 *     transformation.
 *
 * NB: Although the systemd.unit(5) doesn't mention it, the ':' character
 * is not escaped.
 */
char *systemd_escape(char *path, char *suffix)
{
	char *result;
	char *p;
	int len;

	len = systemd_len(path);
	result = malloc(len + strlen(suffix) + 1);
	p = result;
	while (*path == '/')
		/* multiple leading "/" are ignored */
		path++;
	if (!*path) {
		/* root directory "/" becomes is encoded as a single "-" */
		*p++ = '-';
		goto out;
	}
	if (*path == '.')
		/*
		 * replace "." with "\x2d" escape sequence if
		 * it's the first character in escaped path
		 * */
		p = hexify(*path++, p);

	while (*path) {
		unsigned char c = *path++;

		if (c == '/') {
			/* multiple non-trailing slashes become '-' */
			while (*path == '/')
				path++;
			if (*path)
				*p++ = '-';
		} else if (isalnum(c) || c == ':' || c == '.' || c == '_')
			/* these characters are not replaced */
			*p++ = c;
		else
			/* replace with "\x2d" escape sequence */
			p = hexify(c, p);
	}

out:
	sprintf(p, "%s", suffix);
	return result;
}