util.c 6.72 KB
Newer Older
1
/*
2
3
 *  ezstream - source client for Icecast with external en-/decoder support
 *  Copyright (C) 2003, 2004, 2005, 2006  Ed Zaleski <oddsock@oddsock.org>
Moritz Grimm's avatar
Moritz Grimm committed
4
 *  Copyright (C) 2007, 2009, 2017, 2020  Moritz Grimm <mgrimm@mrsserver.net>
5
 *
6
7
8
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
9
 *
10
11
12
13
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
14
15
16
17
18
19
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

20
#include "compat.h"
21

22
23
24
#include <sys/types.h>
#include <sys/file.h>

25
26
#include <ctype.h>
#include <errno.h>
Moritz Grimm's avatar
Moritz Grimm committed
27
#include <langinfo.h>
Moritz Grimm's avatar
Moritz Grimm committed
28
#include <limits.h>
Moritz Grimm's avatar
Moritz Grimm committed
29
#include <locale.h>
30
31
#include <stdio.h>
#include <string.h>
32
#include <unistd.h>
33

34
35
36
#ifdef HAVE_ICONV
# include <iconv.h>
#endif
37

38
39
#include "log.h"
#include "util.h"
40
#include "xalloc.h"
41

42
43
44
45
#ifndef BUFSIZ
# define BUFSIZ 1024
#endif

46
47
48
49
50
static char		*pidfile_path;
static FILE		*pidfile_file;
static pid_t		 pidfile_pid;
static unsigned int	 pidfile_numlocks;

Moritz Grimm's avatar
Moritz Grimm committed
51
52
static char *	_util_iconvert(const char *, const char *, const char *);
static void	_util_cleanup_pidfile(void);
moritz's avatar
moritz committed
53

54
static char *
Moritz Grimm's avatar
Moritz Grimm committed
55
_util_iconvert(const char *in_str, const char *from, const char *to)
56
57
{
#ifdef HAVE_ICONV
moritz's avatar
moritz committed
58
59
60
61
62
63
64
65
	iconv_t 		 cd;
	ICONV_CONST char	*input, *ip;
	size_t			 input_len;
	char			*output;
	size_t			 output_size;
	char			 buf[BUFSIZ], *bp;
	size_t			 bufavail;
	size_t			 out_pos;
66
67
	char			*tocode;

68
69
70
	if (NULL == in_str)
		return (xstrdup(""));

Moritz Grimm's avatar
Moritz Grimm committed
71
	tocode = xstrdup(to);
72
	if ((cd = iconv_open(tocode, from)) == (iconv_t)-1 &&
73
74
	    (cd = iconv_open("", from)) == (iconv_t)-1 &&
	    (cd = iconv_open(tocode, "")) == (iconv_t)-1) {
75
		xfree(tocode);
76
		log_syserr(ERROR, errno, "iconv_open");
77
		return (xstrdup(in_str));
78
79
80
81
82
83
84
85
86
87
88
89
90
91
	}

	ip = input = (ICONV_CONST char *)in_str;
	input_len = strlen(input);
	output_size = 1;
	output = xcalloc(output_size, sizeof(char));
	out_pos = 0;
	output[out_pos] = '\0';
	while (input_len > 0) {
		char	*op;
		size_t	 count;

		buf[0] = '\0';
		bp = buf;
92
		bufavail = sizeof(buf) - 1;
93
94
95
96
97
98
99
100
101
102

		if (iconv(cd, &ip, &input_len, &bp, &bufavail) == (size_t)-1 &&
		    errno != E2BIG) {
			*bp++ = '?';
			ip++;
			input_len--;
			bufavail--;
		}
		*bp = '\0';

103
		count = sizeof(buf) - bufavail - 1;
104
105

		output_size += count;
106
		op = output = xreallocarray(output, output_size, sizeof(char));
107
108
109
110
111
112
113
114
		op += out_pos;
		memcpy(op, buf, count);
		out_pos += count;
		op += count;
		*op = '\0';
	}

	if (iconv_close(cd) == -1) {
115
		log_syserr(ERROR, errno, "iconv_close");
116
		xfree(output);
117
		xfree(tocode);
118
		return (xstrdup(in_str));
119
120
	}

121
	xfree(tocode);
122
	return (output);
123
#else
124
125
126
	(void)from;
	(void)to;

127
128
129
	if (NULL == in_str)
		return (xstrdup(""));

130
	return (xstrdup(in_str));
moritz's avatar
moritz committed
131
#endif /* HAVE_ICONV */
132
}
133

134
static void
Moritz Grimm's avatar
Moritz Grimm committed
135
_util_cleanup_pidfile(void)
136
137
138
139
140
141
142
{
	if (NULL != pidfile_path && getpid() == pidfile_pid) {
		(void)unlink(pidfile_path);
		(void)fclose(pidfile_file);
	}
}

Moritz Grimm's avatar
Moritz Grimm committed
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
const char *
util_get_progname(const char *argv0)
{
#ifdef HAVE___PROGNAME
	extern char	*__progname;

	(void)argv0;

	return (__progname);
#else
	if (argv0 == NULL) {
		return (UTIL_DEFAULT_PROGNAME);
	} else {
		const char	*p = strrchr(argv0, '/');

		if (p == NULL)
			p = argv0;
		else
			p++;

		return (p);
	}
#endif /* HAVE___PROGNAME */
}

168
int
169
util_write_pid_file(const char *path)
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
{
	int	 save_errno = 0;
	pid_t	 pid;

	if (NULL == path)
		return (0);

	xfree(pidfile_path);
	pidfile_path = xstrdup(path);

	if (NULL != pidfile_file)
		fclose(pidfile_file);
	if (NULL == (pidfile_file = fopen(pidfile_path, "w"))) {
		xfree(pidfile_path);
		pidfile_path = NULL;
		return (-1);
	}

	pid = getpid();
	if (0 >= fprintf(pidfile_file, "%ld\n", (long)pid) ||
	    0 > fflush(pidfile_file) ||
191
	    0 > flock(fileno(pidfile_file), LOCK_EX | LOCK_NB))
192
193
194
195
		goto error;

	if (0 == pidfile_numlocks) {
		pidfile_pid = pid;
Moritz Grimm's avatar
Moritz Grimm committed
196
		if (0 != atexit(_util_cleanup_pidfile))
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
			goto error;
		pidfile_numlocks++;
	}

	return (0);

error:
	save_errno = errno;
	(void)unlink(pidfile_path);
	xfree(pidfile_path);
	pidfile_path = NULL;
	(void)fclose(pidfile_file);
	pidfile_file = NULL;
	pidfile_pid = 0;
	errno = save_errno;

	return (-1);
}

Moritz Grimm's avatar
Moritz Grimm committed
216
int
217
util_strrcmp(const char *s, const char *sub)
Moritz Grimm's avatar
Moritz Grimm committed
218
219
220
221
222
{
	size_t	slen = strlen(s);
	size_t	sublen = strlen(sub);

	if (sublen > slen)
223
		return (1);
Moritz Grimm's avatar
Moritz Grimm committed
224

225
	return (!!memcmp(s + slen - sublen, sub, sublen));
Moritz Grimm's avatar
Moritz Grimm committed
226
227
228
}

int
229
util_strrcasecmp(const char *s, const char *sub)
Moritz Grimm's avatar
Moritz Grimm committed
230
231
232
233
234
235
236
{
	char	*s_cpy = xstrdup(s);
	char	*sub_cpy = xstrdup(sub);
	char	*p;
	int	 ret;

	for (p = s_cpy; *p != '\0'; p++)
237
		*p = (char)tolower((int)*p);
Moritz Grimm's avatar
Moritz Grimm committed
238
239

	for (p = sub_cpy; *p != '\0'; p++)
240
		*p = (char)tolower((int)*p);
Moritz Grimm's avatar
Moritz Grimm committed
241

242
	ret = util_strrcmp(s_cpy, sub_cpy);
Moritz Grimm's avatar
Moritz Grimm committed
243
244
245
246
247
248
249
250

	xfree(s_cpy);
	xfree(sub_cpy);

	return (ret);
}

char *
Moritz Grimm's avatar
Moritz Grimm committed
251
util_char2utf8(const char *in_str)
Moritz Grimm's avatar
Moritz Grimm committed
252
253
254
255
256
257
258
{
	char	*codeset;

	setlocale(LC_CTYPE, "");
	codeset = nl_langinfo((nl_item)CODESET);
	setlocale(LC_CTYPE, "C");

Moritz Grimm's avatar
Moritz Grimm committed
259
	return (_util_iconvert(in_str, codeset, "UTF-8"));
Moritz Grimm's avatar
Moritz Grimm committed
260
261
262
}

char *
Moritz Grimm's avatar
Moritz Grimm committed
263
util_utf82char(const char *in_str)
Moritz Grimm's avatar
Moritz Grimm committed
264
265
266
267
268
269
270
{
	char	*codeset;

	setlocale(LC_CTYPE, "");
	codeset = nl_langinfo((nl_item)CODESET);
	setlocale(LC_CTYPE, "C");

Moritz Grimm's avatar
Moritz Grimm committed
271
	return (_util_iconvert(in_str, "UTF-8", codeset));
Moritz Grimm's avatar
Moritz Grimm committed
272
273
}

274
char *
Moritz Grimm's avatar
Moritz Grimm committed
275
util_expand_words(const char *in, struct util_dict dicts[])
276
{
Moritz Grimm's avatar
Moritz Grimm committed
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
	size_t	 i;
	char	*out;
	size_t	 out_size = strlen(in) + 1;

	/* empty input string? */
	if (1 == out_size)
		return (NULL);

	out = xstrdup(in);
	i = out_size - 1;
	while (i--) {
		struct util_dict *d = dicts;

		while (d && d->from) {
			if (0 == strncmp(&out[i], d->from, strlen(d->from))) {
				char	*buf, *tmp;
				size_t	 buf_len;

				buf_len = strlen(&out[i]) + strlen(d->to)
				    - strlen(d->from);
				buf = xcalloc(buf_len + 1, sizeof(*buf));
				snprintf(buf, buf_len + 1, "%s%s",
				    d->to, &out[i + strlen(d->from)]);

				out_size += buf_len;
				tmp = xcalloc(out_size, sizeof(*tmp));
				snprintf(tmp, i + 1, "%s", out);
				snprintf(tmp + i, out_size - i, "%s", buf);
				free(buf);
				free(out);
				out = tmp;

				break;
			}
			d++;
		}
313
314
	}

Moritz Grimm's avatar
Moritz Grimm committed
315
	return (out);
316
317
}

318
#define SHELLQUOTE_OUTLEN_MAX	8191UL
319
320

char *
321
util_shellquote(const char *in, size_t outlen_max)
322
323
{
	char		*out, *out_p;
324
	size_t		 out_len;
325
326
	const char	*in_p;

327
328
329
330
	if (!outlen_max || outlen_max > SHELLQUOTE_OUTLEN_MAX)
		outlen_max = SHELLQUOTE_OUTLEN_MAX;
	out_len = outlen_max;

331
332
333
334
335
336
337
	out = xcalloc(out_len + 1, sizeof(char));

	out_p = out;
	in_p = in;

	*out_p++ = '\'';
	out_len--;
338
339
340
	while (*in_p && 1 < out_len) {
		int	stop = 0;

341
342
		switch (*in_p) {
		case '\'':
343
			if (4 < out_len) {
344
345
346
347
348
349
				*out_p++ = '\'';
				*out_p++ = '\\';
				*out_p++ = '\'';
				out_len -= 3;
			} else
				stop = 1;
350
351
352
353
			break;
		default:
			break;
		}
354
355
		if (stop)
			break;
356
357
358
359
360
361
362
		*out_p++ = *in_p++;
		out_len--;
	}
	*out_p++ = '\'';

	return (out);
}