1 /*
2  *  libzvbi -- Miscellaneous cows and chickens
3  *
4  *  Copyright (C) 2000-2003 I�aki Garc�a Etxebarria
5  *  Copyright (C) 2001-2007 Michael H. Schimek
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public
18  *  License along with this library; if not, write to the
19  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA  02110-1301  USA.
21  */
22 
23 /* $Id: misc.c,v 1.13 2008/02/19 00:35:20 mschimek Exp $ */
24 
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28 
29 #include <ctype.h>
30 #include <errno.h>
31 
32 #include "misc.h"
33 
34 #ifdef ZAPPING8
35 const char vbi_intl_domainname[] = PACKAGE;
36 #else
37 #  include "version.h"
38 #  if 2 == VBI_VERSION_MINOR
39 const char _zvbi_intl_domainname[] = PACKAGE;
40 #  else
41 const char vbi_intl_domainname[] = PACKAGE;
42 #  endif
43 #endif
44 
45 _vbi_log_hook		_vbi_global_log;
46 
47 /**
48  * @internal
49  * Number of set bits.
50  */
51 unsigned int
_vbi_popcnt(uint32_t x)52 _vbi_popcnt			(uint32_t		x)
53 {
54 	x -= ((x >> 1) & 0x55555555);
55 	x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
56 	x = (x + (x >> 4)) & 0x0F0F0F0F;
57 	return ((uint32_t)(x * 0x01010101)) >> 24;
58 }
59 
60 /**
61  * @internal
62  * @param dst The string will be stored in this buffer.
63  * @param src NUL-terminated string to be copied.
64  * @param size Maximum number of bytes to be copied, including the
65  *   terminating NUL (i.e. this is the size of the @a dst buffer).
66  *
67  * Copies @a src to @a dst, but no more than @a size - 1 characters.
68  * Always NUL-terminates @a dst, unless @a size is zero.
69  *
70  * strlcpy() is a BSD extension. Don't call this function
71  * directly, we #define strlcpy if necessary.
72  *
73  * @returns
74  * strlen (src).
75  */
76 size_t
_vbi_strlcpy(char * dst,const char * src,size_t size)77 _vbi_strlcpy			(char *			dst,
78 				 const char *		src,
79 				 size_t			size)
80 {
81 	const char *src1;
82 
83 	assert (NULL != dst);
84 	assert (NULL != src);
85 
86 	src1 = src;
87 
88 	if (likely (size > 1)) {
89 		char *end = dst + size - 1;
90 
91 		do {
92 			if (unlikely (0 == (*dst++ = *src++)))
93 				goto finish;
94 		} while (dst < end);
95 
96 		*dst = 0;
97 	} else if (size > 0) {
98 		*dst = 0;
99 	}
100 
101 	while (*src++)
102 		;
103 
104  finish:
105 	return src - src1 - 1;
106 }
107 
108 /**
109  * @internal
110  * strndup() is a BSD/GNU extension. Don't call this function
111  * directly, we #define strndup if necessary.
112  */
113 char *
_vbi_strndup(const char * s,size_t len)114 _vbi_strndup			(const char *		s,
115 				 size_t			len)
116 {
117 	size_t n;
118 	char *r;
119 
120 	if (NULL == s)
121 		return NULL;
122 
123 	n = strlen (s);
124 	len = MIN (len, n);
125 
126 	r = vbi_malloc (len + 1);
127 
128 	if (r) {
129 		memcpy (r, s, len);
130 		r[len] = 0;
131 	}
132 
133 	return r;
134 }
135 
136 /**
137  * @internal
138  * vasprintf() is a BSD/GNU extension. Don't call this function
139  * directly, we #define vasprintf if necessary.
140  */
141 int
_vbi_vasprintf(char ** dstp,const char * templ,va_list ap)142 _vbi_vasprintf			(char **		dstp,
143 				 const char *		templ,
144 				 va_list		ap)
145 {
146 	char *buf;
147 	unsigned long size;
148 	va_list ap2;
149 	int temp;
150 
151 	assert (NULL != dstp);
152 	assert (NULL != templ);
153 
154 	temp = errno;
155 
156 	buf = NULL;
157 	size = 64;
158 
159 	__va_copy (ap2, ap);
160 
161 	for (;;) {
162 
163 		char *buf2;
164 		long len;
165 
166 		if (!(buf2 = vbi_realloc (buf, size)))
167 			break;
168 
169 		buf = buf2;
170 
171 		len = vsnprintf (buf, size, templ, ap);
172 
173 		if (len < 0) {
174 			/* Not enough. */
175 			size *= 2;
176 		} else if ((unsigned long) len < size) {
177 			*dstp = buf;
178 			errno = temp;
179 			return len;
180 		} else {
181 			/* Size needed. */
182 			size = len + 1;
183 		}
184 
185 		/* vsnprintf() may advance ap. */
186 		__va_copy (ap, ap2);
187 	}
188 
189 	vbi_free (buf);
190 	buf = NULL;
191 
192 	/* According to "man 3 asprintf" GNU's version leaves *dstp
193 	   undefined on error, so don't count on it. FreeBSD's
194 	   asprintf NULLs *dstp, which is safer. */
195 	*dstp = NULL;
196 	errno = temp;
197 
198 	return -1;
199 }
200 
201 /**
202  * @internal
203  * asprintf() is a GNU extension. Don't call this function
204  * directly, we #define asprintf if necessary.
205  */
206 int
_vbi_asprintf(char ** dstp,const char * templ,...)207 _vbi_asprintf			(char **		dstp,
208 				 const char *		templ,
209 				 ...)
210 {
211 	va_list ap;
212 	int len;
213 
214 	va_start (ap, templ);
215 
216 	/* May fail, returning -1. */
217 	len = vasprintf (dstp, templ, ap);
218 
219 	va_end (ap);
220 
221 	return len;
222 }
223 
224 /** @internal */
225 vbi_bool
_vbi_keyword_lookup(int * value,const char ** inout_s,const _vbi_key_value_pair * table,unsigned int n_pairs)226 _vbi_keyword_lookup		(int *			value,
227 				 const char **		inout_s,
228 				 const _vbi_key_value_pair *table,
229 				 unsigned int		n_pairs)
230 {
231 	const char *s;
232 	unsigned int i;
233 
234 	assert (NULL != value);
235 	assert (NULL != inout_s);
236 	assert (NULL != *inout_s);
237 	assert (NULL != table);
238 
239 	s = *inout_s;
240 
241 	while (isspace (*s))
242 		++s;
243 
244 	if (isdigit (*s)) {
245 		long val;
246 		char *end;
247 
248 		val = strtol (s, &end, 10);
249 
250 		for (i = 0; NULL != table[i].key; ++i) {
251 			if (val == table[i].value) {
252 				*value = val;
253 				*inout_s = end;
254 				return TRUE;
255 			}
256 		}
257 	} else {
258 		for (i = 0; i < n_pairs; ++i) {
259 			size_t len = strlen (table[i].key);
260 
261 			if (0 == strncasecmp (s, table[i].key, len)
262 			    && !isalnum (s[len])) {
263 				*value = table[i].value;
264 				*inout_s = s + len;
265 				return TRUE;
266 			}
267 		}
268 	}
269 
270 	return FALSE;
271 }
272 
273 void
_vbi_shrink_vector_capacity(void ** vector,size_t * capacity,size_t min_capacity,size_t element_size)274 _vbi_shrink_vector_capacity	(void **		vector,
275 				 size_t *		capacity,
276 				 size_t			min_capacity,
277 				 size_t			element_size)
278 {
279 	void *new_vec;
280 	size_t new_capacity;
281 
282 	if (min_capacity >= *capacity)
283 		return;
284 
285 	new_capacity = min_capacity;
286 
287 	new_vec = vbi_realloc (*vector, new_capacity * element_size);
288 	if (unlikely (NULL == new_vec))
289 		return;
290 
291 	*vector = new_vec;
292 	*capacity = new_capacity;
293 }
294 
295 vbi_bool
_vbi_grow_vector_capacity(void ** vector,size_t * capacity,size_t min_capacity,size_t element_size)296 _vbi_grow_vector_capacity	(void **		vector,
297 				 size_t *		capacity,
298 				 size_t			min_capacity,
299 				 size_t			element_size)
300 {
301 	void *new_vec;
302 	size_t old_capacity;
303 	size_t new_capacity;
304 	size_t max_capacity;
305 
306 	assert (min_capacity > 0);
307 	assert (element_size > 0);
308 
309 	max_capacity = SIZE_MAX / element_size;
310 
311 	if (unlikely (min_capacity > max_capacity)) {
312 		goto failed;
313 	}
314 
315 	old_capacity = *capacity;
316 
317 	if (unlikely (old_capacity > max_capacity - (1 << 16))) {
318 		new_capacity = max_capacity;
319 	} else if (old_capacity >= (1 << 16)) {
320 		new_capacity = MAX (min_capacity, old_capacity + (1 << 16));
321 	} else {
322 		new_capacity = MAX (min_capacity, old_capacity * 2);
323 	}
324 
325 	new_vec = vbi_realloc (*vector, new_capacity * element_size);
326 	if (unlikely (NULL == new_vec)) {
327 		if (new_capacity <= min_capacity)
328 			goto failed;
329 
330 		new_capacity = min_capacity;
331 
332 		new_vec = vbi_realloc (*vector, new_capacity * element_size);
333 		if (unlikely (NULL == new_vec))
334 			goto failed;
335 	}
336 
337 	*vector = new_vec;
338 	*capacity = new_capacity;
339 
340 	return TRUE;
341 
342  failed:
343 	errno = ENOMEM;
344 
345 	return FALSE;
346 }
347 
348 /**
349  * @ingroup Basic
350  *
351  * Log function printing messages on standard output.
352  *
353  * @since 0.2.22
354  */
355 void
vbi_log_on_stderr(vbi_log_mask level,const char * context,const char * message,void * user_data)356 vbi_log_on_stderr		(vbi_log_mask		level,
357 				 const char *		context,
358 				 const char *		message,
359 				 void *			user_data)
360 {
361 	vbi_log_mask max_level;
362 
363 	/* This function exists in libzvbi 0.2 with vbi_ prefix and
364 	   in libzvbi 0.3 and Zapping with vbi_ prefix (so I can
365 	   use both versions in Zapping until 0.3 is finished). */
366 	if (0 == strncmp (context, "vbi_", 4)) {
367 		context += 4;
368 	/* Not "vbi_" to prevent an accidental s/vbi_/vbi_. */
369 	} else if (0 == strncmp (context, "vbi" "3_", 5)) {
370 		context += 5;
371 	}
372 
373 	if (NULL != user_data) {
374 		max_level = * (vbi_log_mask *) user_data;
375 		if (level > max_level)
376 			return;
377 	}
378 
379 	fprintf (stderr, "libzvbi:%s: %s\n", context, message);
380 }
381 
382 /** @internal */
383 void
_vbi_log_vprintf(vbi_log_fn log_fn,void * user_data,vbi_log_mask mask,const char * source_file,const char * context,const char * templ,va_list ap)384 _vbi_log_vprintf		(vbi_log_fn		log_fn,
385 				 void *			user_data,
386 				 vbi_log_mask		mask,
387 				 const char *		source_file,
388 				 const char *		context,
389 				 const char *		templ,
390 				 va_list		ap)
391 {
392 	char ctx_buffer[160];
393 	char *msg_buffer;
394 	int saved_errno;
395 	unsigned int i;
396 	int r;
397 
398 	assert (NULL != source_file);
399 	assert (NULL != context);
400 	assert (NULL != templ);
401 
402 	if (NULL == log_fn)
403 		return;
404 
405 	saved_errno = errno;
406 
407 	for (i = 0; i < N_ELEMENTS (ctx_buffer) - 2; ++i) {
408 		int c = source_file[i];
409 
410 		if ('.' == c)
411 			break;
412 
413 		ctx_buffer[i] = c;
414 	}
415 
416 	ctx_buffer[i++] = ':';
417 
418 	strlcpy (ctx_buffer + i, context,
419 		 N_ELEMENTS (ctx_buffer) - i);
420 
421 	r = vasprintf (&msg_buffer, templ, ap);
422 	if (r > 1 && NULL != msg_buffer) {
423 		log_fn (mask, ctx_buffer, msg_buffer, user_data);
424 
425 		vbi_free (msg_buffer);
426 		msg_buffer = NULL;
427 	}
428 
429 	errno = saved_errno;
430 }
431 
432 /** @internal */
433 void
_vbi_log_printf(vbi_log_fn log_fn,void * user_data,vbi_log_mask mask,const char * source_file,const char * context,const char * templ,...)434 _vbi_log_printf			(vbi_log_fn		log_fn,
435 				 void *			user_data,
436 				 vbi_log_mask		mask,
437 				 const char *		source_file,
438 				 const char *		context,
439 				 const char *		templ,
440 				 ...)
441 {
442 	va_list ap;
443 
444 	va_start (ap, templ);
445 
446 	_vbi_log_vprintf (log_fn, user_data, mask,
447 			  source_file, context, templ, ap);
448 
449 	va_end (ap);
450 }
451 
452 /*
453 Local variables:
454 c-set-style: K&R
455 c-basic-offset: 8
456 End:
457 */
458