xref: /freebsd/contrib/nvi/common/util.c (revision 755cc40c21ca63388c6a67ba848a908b429e9391)
1b8ba871bSPeter Wemm /*-
2b8ba871bSPeter Wemm  * Copyright (c) 1991, 1993, 1994
3b8ba871bSPeter Wemm  *	The Regents of the University of California.  All rights reserved.
4b8ba871bSPeter Wemm  * Copyright (c) 1991, 1993, 1994, 1995, 1996
5b8ba871bSPeter Wemm  *	Keith Bostic.  All rights reserved.
6b8ba871bSPeter Wemm  *
7b8ba871bSPeter Wemm  * See the LICENSE file for redistribution information.
8b8ba871bSPeter Wemm  */
9b8ba871bSPeter Wemm 
10b8ba871bSPeter Wemm #include "config.h"
11b8ba871bSPeter Wemm 
12b8ba871bSPeter Wemm #include <sys/types.h>
13b8ba871bSPeter Wemm #include <sys/queue.h>
14b8ba871bSPeter Wemm 
15f0957ccaSPeter Wemm #ifdef __APPLE__
16f0957ccaSPeter Wemm #include <mach/clock.h>
17f0957ccaSPeter Wemm #include <mach/mach.h>
18f0957ccaSPeter Wemm #include <mach/mach_time.h>
19f0957ccaSPeter Wemm #endif
20f0957ccaSPeter Wemm 
21b8ba871bSPeter Wemm #include <bitstring.h>
22f0957ccaSPeter Wemm #include <ctype.h>
23b8ba871bSPeter Wemm #include <errno.h>
24b8ba871bSPeter Wemm #include <limits.h>
25f0957ccaSPeter Wemm #include <pwd.h>
26b8ba871bSPeter Wemm #include <stdio.h>
27b8ba871bSPeter Wemm #include <stdlib.h>
28b8ba871bSPeter Wemm #include <string.h>
29f0957ccaSPeter Wemm #include <time.h>
30b8ba871bSPeter Wemm #include <unistd.h>
31b8ba871bSPeter Wemm 
32b8ba871bSPeter Wemm #include "common.h"
33b8ba871bSPeter Wemm 
34b8ba871bSPeter Wemm /*
35b8ba871bSPeter Wemm  * binc --
36b8ba871bSPeter Wemm  *	Increase the size of a buffer.
37b8ba871bSPeter Wemm  *
38c271fa92SBaptiste Daroussin  * PUBLIC: void *binc(SCR *, void *, size_t *, size_t);
39b8ba871bSPeter Wemm  */
40b8ba871bSPeter Wemm void *
binc(SCR * sp,void * bp,size_t * bsizep,size_t min)41110d525eSBaptiste Daroussin binc(SCR *sp,			/* sp MAY BE NULL!!! */
42110d525eSBaptiste Daroussin     void *bp, size_t *bsizep, size_t min)
43b8ba871bSPeter Wemm {
44b8ba871bSPeter Wemm 	size_t csize;
45b8ba871bSPeter Wemm 
46b8ba871bSPeter Wemm 	/* If already larger than the minimum, just return. */
47b8ba871bSPeter Wemm 	if (min && *bsizep >= min)
48b8ba871bSPeter Wemm 		return (bp);
49b8ba871bSPeter Wemm 
50f0957ccaSPeter Wemm 	csize = p2roundup(MAX(min, 256));
51b8ba871bSPeter Wemm 	REALLOC(sp, bp, void *, csize);
52b8ba871bSPeter Wemm 
53b8ba871bSPeter Wemm 	if (bp == NULL) {
54b8ba871bSPeter Wemm 		*bsizep = 0;
55b8ba871bSPeter Wemm 		return (NULL);
56b8ba871bSPeter Wemm 	}
57b8ba871bSPeter Wemm 	/*
58b8ba871bSPeter Wemm 	 * Memory is guaranteed to be zero-filled, various parts of
59b8ba871bSPeter Wemm 	 * nvi depend on this.
60b8ba871bSPeter Wemm 	 */
61b8ba871bSPeter Wemm 	memset((char *)bp + *bsizep, 0, csize - *bsizep);
62b8ba871bSPeter Wemm 	*bsizep = csize;
63b8ba871bSPeter Wemm 	return (bp);
64b8ba871bSPeter Wemm }
65b8ba871bSPeter Wemm 
66b8ba871bSPeter Wemm /*
67b8ba871bSPeter Wemm  * nonblank --
68b8ba871bSPeter Wemm  *	Set the column number of the first non-blank character
69b8ba871bSPeter Wemm  *	including or after the starting column.  On error, set
70b8ba871bSPeter Wemm  *	the column to 0, it's safest.
71b8ba871bSPeter Wemm  *
72c271fa92SBaptiste Daroussin  * PUBLIC: int nonblank(SCR *, recno_t, size_t *);
73b8ba871bSPeter Wemm  */
74b8ba871bSPeter Wemm int
nonblank(SCR * sp,recno_t lno,size_t * cnop)75110d525eSBaptiste Daroussin nonblank(SCR *sp, recno_t lno, size_t *cnop)
76b8ba871bSPeter Wemm {
77f0957ccaSPeter Wemm 	CHAR_T *p;
78b8ba871bSPeter Wemm 	size_t cnt, len, off;
79b8ba871bSPeter Wemm 	int isempty;
80b8ba871bSPeter Wemm 
81b8ba871bSPeter Wemm 	/* Default. */
82b8ba871bSPeter Wemm 	off = *cnop;
83b8ba871bSPeter Wemm 	*cnop = 0;
84b8ba871bSPeter Wemm 
85b8ba871bSPeter Wemm 	/* Get the line, succeeding in an empty file. */
86b8ba871bSPeter Wemm 	if (db_eget(sp, lno, &p, &len, &isempty))
87b8ba871bSPeter Wemm 		return (!isempty);
88b8ba871bSPeter Wemm 
89b8ba871bSPeter Wemm 	/* Set the offset. */
90b8ba871bSPeter Wemm 	if (len == 0 || off >= len)
91b8ba871bSPeter Wemm 		return (0);
92b8ba871bSPeter Wemm 
93b8ba871bSPeter Wemm 	for (cnt = off, p = &p[off],
94f0957ccaSPeter Wemm 	    len -= off; len && ISBLANK(*p); ++cnt, ++p, --len);
95b8ba871bSPeter Wemm 
96b8ba871bSPeter Wemm 	/* Set the return. */
97b8ba871bSPeter Wemm 	*cnop = len ? cnt : cnt - 1;
98b8ba871bSPeter Wemm 	return (0);
99b8ba871bSPeter Wemm }
100b8ba871bSPeter Wemm 
101b8ba871bSPeter Wemm /*
102f0957ccaSPeter Wemm  * join --
103f0957ccaSPeter Wemm  *	Join two paths; need free.
104f0957ccaSPeter Wemm  *
105c271fa92SBaptiste Daroussin  * PUBLIC: char *join(char *, char *);
106f0957ccaSPeter Wemm  */
107f0957ccaSPeter Wemm char *
join(char * path1,char * path2)108110d525eSBaptiste Daroussin join(char *path1, char *path2)
109f0957ccaSPeter Wemm {
110f0957ccaSPeter Wemm 	char *p;
111f0957ccaSPeter Wemm 
112f0957ccaSPeter Wemm 	if (path1[0] == '\0' || path2[0] == '/')
113f0957ccaSPeter Wemm 		return strdup(path2);
114*755cc40cSBaptiste Daroussin 	if (asprintf(&p, path1[strlen(path1)-1] == '/' ?
115*755cc40cSBaptiste Daroussin 	    "%s%s" : "%s/%s", path1, path2) == -1)
116*755cc40cSBaptiste Daroussin 		return NULL;
117f0957ccaSPeter Wemm 	return p;
118f0957ccaSPeter Wemm }
119f0957ccaSPeter Wemm 
120f0957ccaSPeter Wemm /*
121f0957ccaSPeter Wemm  * expanduser --
122f0957ccaSPeter Wemm  *	Return a "~" or "~user" expanded path; need free.
123f0957ccaSPeter Wemm  *
124c271fa92SBaptiste Daroussin  * PUBLIC: char *expanduser(char *);
125f0957ccaSPeter Wemm  */
126f0957ccaSPeter Wemm char *
expanduser(char * str)127f0957ccaSPeter Wemm expanduser(char *str)
128f0957ccaSPeter Wemm {
129f0957ccaSPeter Wemm 	struct passwd *pwd;
130f0957ccaSPeter Wemm 	char *p, *t, *u, *h;
131f0957ccaSPeter Wemm 
132f0957ccaSPeter Wemm 	/*
133f0957ccaSPeter Wemm 	 * This function always expands the content between the
134f0957ccaSPeter Wemm 	 * leading '~' and the first '/' or '\0' from the input.
135f0957ccaSPeter Wemm 	 * Return NULL whenever we fail to do so.
136f0957ccaSPeter Wemm 	 */
137f0957ccaSPeter Wemm 	if (*str != '~')
138f0957ccaSPeter Wemm 		return (NULL);
139f0957ccaSPeter Wemm 	p = str + 1;
140f0957ccaSPeter Wemm 	for (t = p; *t != '/' && *t != '\0'; ++t)
141f0957ccaSPeter Wemm 		continue;
142f0957ccaSPeter Wemm 	if (t == p) {
143f0957ccaSPeter Wemm 		/* ~ */
144*755cc40cSBaptiste Daroussin #ifdef __GLIBC__
145*755cc40cSBaptiste Daroussin 		extern char *secure_getenv(const char *);
146*755cc40cSBaptiste Daroussin 		if ((h = secure_getenv("HOME")) == NULL) {
147*755cc40cSBaptiste Daroussin #else
148f0957ccaSPeter Wemm 		if (issetugid() != 0 ||
149f0957ccaSPeter Wemm 		    (h = getenv("HOME")) == NULL) {
150*755cc40cSBaptiste Daroussin #endif
151f0957ccaSPeter Wemm 			if (((h = getlogin()) != NULL &&
152f0957ccaSPeter Wemm 			     (pwd = getpwnam(h)) != NULL) ||
153f0957ccaSPeter Wemm 			    (pwd = getpwuid(getuid())) != NULL)
154f0957ccaSPeter Wemm 				h = pwd->pw_dir;
155f0957ccaSPeter Wemm 			else
156f0957ccaSPeter Wemm 				return (NULL);
157f0957ccaSPeter Wemm 		}
158f0957ccaSPeter Wemm 	} else {
159f0957ccaSPeter Wemm 		/* ~user */
160f0957ccaSPeter Wemm 		if ((u = strndup(p, t - p)) == NULL)
161f0957ccaSPeter Wemm 			return (NULL);
162f0957ccaSPeter Wemm 		if ((pwd = getpwnam(u)) == NULL) {
163f0957ccaSPeter Wemm 			free(u);
164f0957ccaSPeter Wemm 			return (NULL);
165f0957ccaSPeter Wemm 		} else
166f0957ccaSPeter Wemm 			h = pwd->pw_dir;
167f0957ccaSPeter Wemm 		free(u);
168f0957ccaSPeter Wemm 	}
169f0957ccaSPeter Wemm 
170f0957ccaSPeter Wemm 	for (; *t == '/' && *t != '\0'; ++t)
171f0957ccaSPeter Wemm 		continue;
172f0957ccaSPeter Wemm 	return (join(h, t));
173f0957ccaSPeter Wemm }
174f0957ccaSPeter Wemm 
175f0957ccaSPeter Wemm /*
176f0957ccaSPeter Wemm  * quote --
177f0957ccaSPeter Wemm  *	Return a escaped string for /bin/sh; need free.
178f0957ccaSPeter Wemm  *
179c271fa92SBaptiste Daroussin  * PUBLIC: char *quote(char *);
180f0957ccaSPeter Wemm  */
181f0957ccaSPeter Wemm char *
182f0957ccaSPeter Wemm quote(char *str)
183f0957ccaSPeter Wemm {
184f0957ccaSPeter Wemm 	char *p, *t;
185f0957ccaSPeter Wemm 	size_t i = 0, n = 0;
186f0957ccaSPeter Wemm 	int unsafe = 0;
187f0957ccaSPeter Wemm 
188f0957ccaSPeter Wemm 	for (p = str; *p != '\0'; p++, i++) {
189f0957ccaSPeter Wemm 		if (*p == '\'')
190f0957ccaSPeter Wemm 			n++;
191f0957ccaSPeter Wemm 		if (unsafe)
192f0957ccaSPeter Wemm 			continue;
193110d525eSBaptiste Daroussin 		if (isascii((u_char)*p)) {
194110d525eSBaptiste Daroussin 			if (isalnum((u_char)*p))
195f0957ccaSPeter Wemm 				continue;
196f0957ccaSPeter Wemm 			switch (*p) {
197f0957ccaSPeter Wemm 			case '%': case '+': case ',': case '-': case '.':
198f0957ccaSPeter Wemm 			case '/': case ':': case '=': case '@': case '_':
199f0957ccaSPeter Wemm 				continue;
200f0957ccaSPeter Wemm 			}
201f0957ccaSPeter Wemm 		}
202f0957ccaSPeter Wemm 		unsafe = 1;
203f0957ccaSPeter Wemm 	}
204f0957ccaSPeter Wemm 	if (!unsafe)
205f0957ccaSPeter Wemm 		t = strdup(str);
206f0957ccaSPeter Wemm #define SQT "'\\''"
207f0957ccaSPeter Wemm 	else if ((p = t = malloc(i + n * (sizeof(SQT) - 2) + 3)) != NULL) {
208f0957ccaSPeter Wemm 		*p++ = '\'';
209f0957ccaSPeter Wemm 		for (; *str != '\0'; str++) {
210f0957ccaSPeter Wemm 			if (*str == '\'') {
211f0957ccaSPeter Wemm 				(void)memcpy(p, SQT, sizeof(SQT) - 1);
212f0957ccaSPeter Wemm 				p += sizeof(SQT) - 1;
213f0957ccaSPeter Wemm 			} else
214f0957ccaSPeter Wemm 				*p++ = *str;
215f0957ccaSPeter Wemm 		}
216f0957ccaSPeter Wemm 		*p++ = '\'';
217f0957ccaSPeter Wemm 		*p = '\0';
218f0957ccaSPeter Wemm 	}
219f0957ccaSPeter Wemm 	return t;
220f0957ccaSPeter Wemm }
221f0957ccaSPeter Wemm 
222f0957ccaSPeter Wemm /*
223b8ba871bSPeter Wemm  * v_strdup --
224f0957ccaSPeter Wemm  *	Strdup for 8-bit character strings with an associated length.
225f0957ccaSPeter Wemm  *
226c271fa92SBaptiste Daroussin  * PUBLIC: char *v_strdup(SCR *, const char *, size_t);
227f0957ccaSPeter Wemm  */
228f0957ccaSPeter Wemm char *
229110d525eSBaptiste Daroussin v_strdup(SCR *sp, const char *str, size_t len)
230f0957ccaSPeter Wemm {
231f0957ccaSPeter Wemm 	char *copy;
232f0957ccaSPeter Wemm 
233110d525eSBaptiste Daroussin 	MALLOC(sp, copy, len + 1);
234f0957ccaSPeter Wemm 	if (copy == NULL)
235f0957ccaSPeter Wemm 		return (NULL);
236f0957ccaSPeter Wemm 	memcpy(copy, str, len);
237f0957ccaSPeter Wemm 	copy[len] = '\0';
238f0957ccaSPeter Wemm 	return (copy);
239f0957ccaSPeter Wemm }
240f0957ccaSPeter Wemm 
241f0957ccaSPeter Wemm /*
242f0957ccaSPeter Wemm  * v_wstrdup --
243b8ba871bSPeter Wemm  *	Strdup for wide character strings with an associated length.
244b8ba871bSPeter Wemm  *
245c271fa92SBaptiste Daroussin  * PUBLIC: CHAR_T *v_wstrdup(SCR *, const CHAR_T *, size_t);
246b8ba871bSPeter Wemm  */
247b8ba871bSPeter Wemm CHAR_T *
248110d525eSBaptiste Daroussin v_wstrdup(SCR *sp, const CHAR_T *str, size_t len)
249b8ba871bSPeter Wemm {
250b8ba871bSPeter Wemm 	CHAR_T *copy;
251b8ba871bSPeter Wemm 
252110d525eSBaptiste Daroussin 	MALLOC(sp, copy, (len + 1) * sizeof(CHAR_T));
253b8ba871bSPeter Wemm 	if (copy == NULL)
254b8ba871bSPeter Wemm 		return (NULL);
255f0957ccaSPeter Wemm 	MEMCPY(copy, str, len);
256b8ba871bSPeter Wemm 	copy[len] = '\0';
257b8ba871bSPeter Wemm 	return (copy);
258b8ba871bSPeter Wemm }
259b8ba871bSPeter Wemm 
260b8ba871bSPeter Wemm /*
261b8ba871bSPeter Wemm  * nget_uslong --
262b8ba871bSPeter Wemm  *      Get an unsigned long, checking for overflow.
263b8ba871bSPeter Wemm  *
264c271fa92SBaptiste Daroussin  * PUBLIC: enum nresult nget_uslong(u_long *, const CHAR_T *, CHAR_T **, int);
265b8ba871bSPeter Wemm  */
266b8ba871bSPeter Wemm enum nresult
267110d525eSBaptiste Daroussin nget_uslong(u_long *valp, const CHAR_T *p, CHAR_T **endp, int base)
268b8ba871bSPeter Wemm {
269b8ba871bSPeter Wemm 	errno = 0;
270f0957ccaSPeter Wemm 	*valp = STRTOUL(p, endp, base);
271b8ba871bSPeter Wemm 	if (errno == 0)
272b8ba871bSPeter Wemm 		return (NUM_OK);
273b8ba871bSPeter Wemm 	if (errno == ERANGE && *valp == ULONG_MAX)
274b8ba871bSPeter Wemm 		return (NUM_OVER);
275b8ba871bSPeter Wemm 	return (NUM_ERR);
276b8ba871bSPeter Wemm }
277b8ba871bSPeter Wemm 
278b8ba871bSPeter Wemm /*
279b8ba871bSPeter Wemm  * nget_slong --
280b8ba871bSPeter Wemm  *      Convert a signed long, checking for overflow and underflow.
281b8ba871bSPeter Wemm  *
282c271fa92SBaptiste Daroussin  * PUBLIC: enum nresult nget_slong(long *, const CHAR_T *, CHAR_T **, int);
283b8ba871bSPeter Wemm  */
284b8ba871bSPeter Wemm enum nresult
285110d525eSBaptiste Daroussin nget_slong(long *valp, const CHAR_T *p, CHAR_T **endp, int base)
286b8ba871bSPeter Wemm {
287b8ba871bSPeter Wemm 	errno = 0;
288f0957ccaSPeter Wemm 	*valp = STRTOL(p, endp, base);
289b8ba871bSPeter Wemm 	if (errno == 0)
290b8ba871bSPeter Wemm 		return (NUM_OK);
291b8ba871bSPeter Wemm 	if (errno == ERANGE) {
292b8ba871bSPeter Wemm 		if (*valp == LONG_MAX)
293b8ba871bSPeter Wemm 			return (NUM_OVER);
294b8ba871bSPeter Wemm 		if (*valp == LONG_MIN)
295b8ba871bSPeter Wemm 			return (NUM_UNDER);
296b8ba871bSPeter Wemm 	}
297b8ba871bSPeter Wemm 	return (NUM_ERR);
298b8ba871bSPeter Wemm }
299b8ba871bSPeter Wemm 
300f0957ccaSPeter Wemm /*
301f0957ccaSPeter Wemm  * timepoint_steady --
302f0957ccaSPeter Wemm  *      Get a timestamp from a monotonic clock.
303f0957ccaSPeter Wemm  *
304c271fa92SBaptiste Daroussin  * PUBLIC: void timepoint_steady(struct timespec *);
305f0957ccaSPeter Wemm  */
306f0957ccaSPeter Wemm void
307110d525eSBaptiste Daroussin timepoint_steady(struct timespec *ts)
308f0957ccaSPeter Wemm {
309f0957ccaSPeter Wemm #ifdef __APPLE__
310f0957ccaSPeter Wemm 	static mach_timebase_info_data_t base = { 0 };
311f0957ccaSPeter Wemm 	uint64_t val;
312f0957ccaSPeter Wemm 	uint64_t ns;
313f0957ccaSPeter Wemm 
314f0957ccaSPeter Wemm 	if (base.denom == 0)
315f0957ccaSPeter Wemm 		(void)mach_timebase_info(&base);
316f0957ccaSPeter Wemm 
317f0957ccaSPeter Wemm 	val = mach_absolute_time();
318f0957ccaSPeter Wemm 	ns = val * base.numer / base.denom;
319f0957ccaSPeter Wemm 	ts->tv_sec = ns / 1000000000;
320f0957ccaSPeter Wemm 	ts->tv_nsec = ns % 1000000000;
321b8ba871bSPeter Wemm #else
322f0957ccaSPeter Wemm #ifdef CLOCK_MONOTONIC_FAST
323f0957ccaSPeter Wemm 	(void)clock_gettime(CLOCK_MONOTONIC_FAST, ts);
324f0957ccaSPeter Wemm #else
325f0957ccaSPeter Wemm 	(void)clock_gettime(CLOCK_MONOTONIC, ts);
326b8ba871bSPeter Wemm #endif
327f0957ccaSPeter Wemm #endif
328f0957ccaSPeter Wemm }
329f0957ccaSPeter Wemm 
330f0957ccaSPeter Wemm /*
331f0957ccaSPeter Wemm  * timepoint_system --
332f0957ccaSPeter Wemm  *      Get the current calendar time.
333f0957ccaSPeter Wemm  *
334c271fa92SBaptiste Daroussin  * PUBLIC: void timepoint_system(struct timespec *);
335f0957ccaSPeter Wemm  */
336f0957ccaSPeter Wemm void
337110d525eSBaptiste Daroussin timepoint_system(struct timespec *ts)
338f0957ccaSPeter Wemm {
339f0957ccaSPeter Wemm #ifdef __APPLE__
340f0957ccaSPeter Wemm 	clock_serv_t clk;
341f0957ccaSPeter Wemm 	mach_timespec_t mts;
342f0957ccaSPeter Wemm 	kern_return_t kr;
343f0957ccaSPeter Wemm 
344f0957ccaSPeter Wemm 	kr = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clk);
345f0957ccaSPeter Wemm 	if (kr != KERN_SUCCESS)
346f0957ccaSPeter Wemm 		return;
347f0957ccaSPeter Wemm 	(void)clock_get_time(clk, &mts);
348f0957ccaSPeter Wemm 	(void)mach_port_deallocate(mach_task_self(), clk);
349f0957ccaSPeter Wemm 	ts->tv_sec = mts.tv_sec;
350f0957ccaSPeter Wemm 	ts->tv_nsec = mts.tv_nsec;
351f0957ccaSPeter Wemm #else
352f0957ccaSPeter Wemm #ifdef CLOCK_REALTIME_FAST
353f0957ccaSPeter Wemm 	(void)clock_gettime(CLOCK_REALTIME_FAST, ts);
354f0957ccaSPeter Wemm #else
355f0957ccaSPeter Wemm 	(void)clock_gettime(CLOCK_REALTIME, ts);
356f0957ccaSPeter Wemm #endif
357f0957ccaSPeter Wemm #endif
358f0957ccaSPeter Wemm }
359f0957ccaSPeter Wemm 
360f0957ccaSPeter Wemm #ifdef DEBUG
361f0957ccaSPeter Wemm #include <stdarg.h>
362b8ba871bSPeter Wemm 
363b8ba871bSPeter Wemm /*
364b8ba871bSPeter Wemm  * TRACE --
365b8ba871bSPeter Wemm  *	debugging trace routine.
366b8ba871bSPeter Wemm  *
367c271fa92SBaptiste Daroussin  * PUBLIC: void TRACE(SCR *, const char *, ...);
368b8ba871bSPeter Wemm  */
369b8ba871bSPeter Wemm void
370110d525eSBaptiste Daroussin TRACE(SCR *sp, const char *fmt, ...)
371b8ba871bSPeter Wemm {
372b8ba871bSPeter Wemm 	FILE *tfp;
373b8ba871bSPeter Wemm 	va_list ap;
374b8ba871bSPeter Wemm 
375b8ba871bSPeter Wemm 	if ((tfp = sp->gp->tracefp) == NULL)
376b8ba871bSPeter Wemm 		return;
377b8ba871bSPeter Wemm 	va_start(ap, fmt);
378b8ba871bSPeter Wemm 	(void)vfprintf(tfp, fmt, ap);
379b8ba871bSPeter Wemm 	va_end(ap);
380b8ba871bSPeter Wemm 
381b8ba871bSPeter Wemm 	(void)fflush(tfp);
382b8ba871bSPeter Wemm }
383b8ba871bSPeter Wemm #endif
384