summaryrefslogtreecommitdiff
path: root/libgphoto2_port/libgphoto2_port/gphoto2-port-log.c
blob: 52a5dd98919776f51cd7990522589e9058fc77a3 (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
135
136
137
138
139
140
141
142
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
/** \file
 *
 * \author Copyright 2001 Lutz Mueller <lutz@users.sf.net>
 *
 * \par License
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * \par
 * This library 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
 * Lesser General Public License for more details.
 *
 * \par
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

#define _DEFAULT_SOURCE

#include "config.h"
#include <gphoto2/gphoto2-port-log.h>

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

#include <gphoto2/gphoto2-port-result.h>

#ifdef ENABLE_NLS
#  include <libintl.h>
#  undef _
#  define _(String) dgettext (GETTEXT_PACKAGE, String)
#  ifdef gettext_noop
#      define N_(String) gettext_noop (String)
#  else
#      define N_(String) (String)
#  endif
#else
#  define _(String) (String)
#  define N_(String) (String)
#endif

#ifndef DISABLE_DEBUGGING
/**
 * \brief Internal logging function entry.
 *
 * Internal structure to remember the logging functions.
 * Use gp_log_add_func() and gp_log_remove_func() to access it.
 */
typedef struct {
	int id;			/**< Internal id */
	GPLogLevel   level;	/**< Internal loglevel */
	GPLogFunc    func;	/**< Internal function pointer to call */
	void        *data;	/**< Private data supplied by caller */
} LogFunc;

static LogFunc *log_funcs = NULL;
static unsigned int log_funcs_count = 0;

/**
 * \brief Add a function to get logging information
 *
 * \param level the maximum level of logging it will get, up to and including the passed value
 * \param func a #GPLogFunc
 * \param data data
 *
 * Adds a log function that will be called for each log message that is flagged
 * with a log level that appears in given log level. This function returns
 * an id that you can use for removing the log function again (using
 * #gp_log_remove_func).
 *
 * \return an id or a gphoto2 error code
 **/
int
gp_log_add_func (GPLogLevel level, GPLogFunc func, void *data)
{
	static int logfuncid = 0;

	C_PARAMS (func);
	C_MEM (log_funcs = realloc (log_funcs, sizeof (LogFunc) *
				(log_funcs_count + 1)));
	log_funcs_count++;

	log_funcs[log_funcs_count - 1].id = ++logfuncid;
	log_funcs[log_funcs_count - 1].level = level;
	log_funcs[log_funcs_count - 1].func = func;
	log_funcs[log_funcs_count - 1].data = data;

	return logfuncid;
}


char*
gpi_vsnprintf (const char* format, va_list args)
{
	va_list xargs;
	int strsize;
	char *str;

#ifdef HAVE_VA_COPY
	va_copy (xargs, args);
#else
	/* according to 'the web', the only interesting compiler without va_copy is MSVC
	 * and there a simple assignment is the way to go */
	xargs = args;
#endif

	/* query the size necessary for the string, add the terminating '\0' */
	strsize = vsnprintf (NULL, 0, format, xargs) + 1;
	va_end (xargs);

	str = malloc(strsize);
	if (!str)
		return NULL;

	/* actually print the string into the buffer */
	vsnprintf (str, strsize, format, args);

	return str;
}

/**
 * \brief Remove a logging receiving function
 * \param id an id (return value of #gp_log_add_func)
 *
 * Removes the log function with given id.
 *
 * \return a gphoto2 error code
 **/
int
gp_log_remove_func (int id)
{
	unsigned int i;

	for (i=0;i<log_funcs_count;i++) {
		if (log_funcs[i].id == id) {
			memmove (log_funcs + i, log_funcs + i + 1, sizeof(LogFunc) * (log_funcs_count - i - 1));
			log_funcs_count--;
			return GP_OK;
		}
	}
	return GP_ERROR_BAD_PARAMETERS;
}

/**
 * Width of offset field in characters. Note that HEXDUMP_COMPLETE_LINE
 * needs to be changed when this value is changed.
 */
#define HEXDUMP_OFFSET_WIDTH 4

/**
 * Distance between offset, hexdump and ascii blocks. Note that
 * HEXDUMP_COMPLETE_LINE needs to be changed when this value is changed.
 */
#define HEXDUMP_BLOCK_DISTANCE 2

/** Initial value for x "pointer" (for hexdump) */
#define HEXDUMP_INIT_X (HEXDUMP_OFFSET_WIDTH + HEXDUMP_BLOCK_DISTANCE)

/** Initial value for y "pointer" (for ascii values) */
#define HEXDUMP_INIT_Y (HEXDUMP_INIT_X + 3 * 16 - 1 + HEXDUMP_BLOCK_DISTANCE)

/** Used to switch to next line */
#define HEXDUMP_LINE_WIDTH (HEXDUMP_INIT_Y + 16)

/** Used to put the '-' character in the middle of the hexdumps */
#define HEXDUMP_MIDDLE (HEXDUMP_INIT_X + 3 * 8 - 1)

/**
 * We are lazy and do our typing only once. Please note that you have
 * to add/remove some lines when increasing/decreasing the values of
 * HEXDUMP_BLOCK_DISTANCE and/or HEXDUMP_OFFSET_WIDTH.
 */
#define HEXDUMP_COMPLETE_LINE {\
        curline[HEXDUMP_OFFSET_WIDTH - 4] = hexchars[(index >> 12) & 0xf]; \
        curline[HEXDUMP_OFFSET_WIDTH - 3] = hexchars[(index >>  8) & 0xf]; \
        curline[HEXDUMP_OFFSET_WIDTH - 2] = hexchars[(index >>  4) & 0xf]; \
        curline[HEXDUMP_OFFSET_WIDTH - 1] = '0'; \
        curline[HEXDUMP_OFFSET_WIDTH + 0] = ' '; \
        curline[HEXDUMP_OFFSET_WIDTH + 1] = ' '; \
        curline[HEXDUMP_MIDDLE] = '-'; \
        curline[HEXDUMP_INIT_Y-2] = ' '; \
        curline[HEXDUMP_INIT_Y-1] = ' '; \
        curline[HEXDUMP_LINE_WIDTH] = '\n'; \
        curline = curline + (HEXDUMP_LINE_WIDTH + 1);}

/**
 * \brief Log data
 * \brief domain the domain
 * \brief data the data to be logged
 * \brief size the size of the data
 *
 * Takes the data and creates a formatted hexdump string. If you would like
 * to log text messages, use #gp_log instead.
 **/
/* coverity[-tainted_sink] */
void
gp_log_data (const char *domain, const char *data, unsigned int size, const char *format, ...)
{
	va_list args;
	static const char hexchars[16] = "0123456789abcdef";
	char *curline, *result = 0, *msg = 0;
	int x = HEXDUMP_INIT_X;
	int y = HEXDUMP_INIT_Y;
	unsigned int index, original_size = size;
	unsigned char value;

	va_start (args, format);
	msg = gpi_vsnprintf(format, args);
	va_end (args);
	if (!msg) {
		GP_LOG_E ("Malloc for expanding format string '%s' failed.", format);
		goto exit;
	}

	if (!data) {
		gp_log (GP_LOG_DATA, domain, "%s (no hexdump, NULL buffer)", msg);
		goto exit;
	}

	if (!size) {
		gp_log (GP_LOG_DATA, domain, "%s (empty hexdump of empty buffer)", msg);
		goto exit;
	}

	if (size > 1024*1024) {
		/* Does not make sense for 200 MB movies */
		size = 1024*1024;
	}

	curline = result = malloc ((HEXDUMP_LINE_WIDTH+1)*(((size-1)/16)+1)+1);
	if (!result) {
		GP_LOG_E ("Malloc for %i bytes failed", (HEXDUMP_LINE_WIDTH+1)*(((size-1)/16)+1)+1);
		goto exit;
	}

	for (index = 0; index < size; ++index) {
                value = (unsigned char)data[index];
                curline[x] = hexchars[value >> 4];
                curline[x+1] = hexchars[value & 0xf];
                curline[x+2] = ' ';
                curline[y++] = ((value>=32)&&(value<127))?value:'.';
                x += 3;
                if ((index & 0xf) == 0xf) { /* end of line */
                        x = HEXDUMP_INIT_X;
                        y = HEXDUMP_INIT_Y;
                        HEXDUMP_COMPLETE_LINE;
                }
        }
        if ((index & 0xf) != 0) { /* not at end of line yet? */
                /* if so, complete this line */
                while (y < HEXDUMP_INIT_Y + 16) {
                        curline[x+0] = ' ';
                        curline[x+1] = ' ';
                        curline[x+2] = ' ';
                        curline[y++] = ' ';
                        x += 3;
                }
                HEXDUMP_COMPLETE_LINE;
        }
        curline[0] = '\0';

        if (size == original_size)
                gp_log (GP_LOG_DATA, domain, "%s (hexdump of %d bytes)\n%s", msg, size, result);
        else
                gp_log (GP_LOG_DATA, domain, "%s (hexdump of the first %d of %d bytes)\n%s", msg, size, original_size, result);

exit:
	free (msg);
	free (result);
}

#undef HEXDUMP_COMPLETE_LINE
#undef HEXDUMP_MIDDLE
#undef HEXDUMP_LINE_WIDTH
#undef HEXDUMP_INIT_Y
#undef HEXDUMP_INIT_X
#undef HEXDUMP_BLOCK_DISTANCE
#undef HEXDUMP_OFFSET_WIDTH

/**
 * \brief Log a debug or error message with va_list
 * \param level gphoto2 log level
 * \param domain the domain
 * \param format the format
 * \param args the va_list corresponding to format
 *
 * Logs a message at the given log level. You would normally use this
 * function to log as yet unformatted strings.
 **/
void
gp_logv (GPLogLevel level, const char *domain, const char *format,
	 va_list args)
{
	unsigned int i;
	char *str = 0;

	if (!log_funcs_count)
		return;

	str = gpi_vsnprintf(format, args);
	if (!str) {
		GP_LOG_E ("Malloc for expanding format string '%s' failed.", format);
		return;
	}

	for (i = 0; i < log_funcs_count; i++)
		if (log_funcs[i].level >= level)
			log_funcs[i].func (level, domain, str, log_funcs[i].data);
	free (str);
}

/**
 * \brief Log a debug or error message
 * \param level gphoto2 log level
 * \param domain the log domain
 * \param format a printf style format string
 * \param ... the variable argumentlist for above format string
 *
 * Logs a message at the given log level. You would normally use this
 * function to log general debug output in a printf way.
 **/
void
gp_log (GPLogLevel level, const char *domain, const char *format, ...)
{
	va_list args;

	va_start (args, format);
	gp_logv (level, domain, format, args);
	va_end (args);
}

void
gp_log_with_source_location(GPLogLevel level, const char *file, int line, const char *func, const char *format, ...)
{
	va_list args;
        char domain[100];

        /* Only display filename without any path/directory part */
        file = strrchr(file, '/') ? strrchr(file, '/') + 1 : file;
        snprintf(domain, sizeof(domain), "%s [%s:%d]", func, file, line);

	va_start (args, format);
	gp_logv (level, domain, format, args);
	va_end (args);
}

#else /* DISABLE_DEBUGGING */

/*
 * Even if debugging is disabled, we must keep stubs to these functions
 * around so that programs dynamically linked to this library will still run.
 */

/* Remove these macros so we can compile functions with these names */
#ifdef gp_log_add_func
#undef gp_log_add_func
#endif
#ifdef gp_log_remove_func
#undef gp_log_remove_func
#endif
#ifdef gp_log_data
#undef gp_log_data
#endif
#ifdef gp_logv
#undef gp_logv
#endif
#ifdef gp_log
#undef gp_log
#endif
#ifdef gp_log_with_source_location
#undef gp_log_with_source_location
#endif

int
gp_log_add_func (GPLogLevel level, GPLogFunc func, void *data)
{
	return 0;
}

int
gp_log_remove_func (int id)
{
	return 0;
}

void
gp_log_data (const char *domain, const char *data, unsigned int size, const char *format, ...)
{
}

void
gp_logv (GPLogLevel level, const char *domain, const char *format,
	 va_list args)
{
}

void
gp_log (GPLogLevel level, const char *domain, const char *format, ...)
{
}

void
gp_log_with_source_location(GPLogLevel level, const char *file, int line, const char *func, const char *format, ...)
{
}
#endif /* DISABLE_DEBUGGING */


#ifdef _GPHOTO2_INTERNAL_CODE

/**
 * \brief (Internal) translate a enumeration code to a string
 */
const char *
gpi_enum_to_string(unsigned int _enum,
		   const StringFlagItem *map)
{
	int i;
	for (i=0; map[i].str != NULL; i++) {
		if (_enum == map[i].flag) {
			return(map[i].str);
			break;
		}
	}
	return NULL;
}

/**
 * \brief (Internal) translate a string to an enumeration code
 */
int
gpi_string_to_enum(const char *str,
		   unsigned int *result,
		   const StringFlagItem *map)
{
	int i;
	for (i=0; map[i].str != NULL; i++) {
		if (0==strcmp(map[i].str, str)) {
			(*result) = map[i].flag;
			return 0;
		}
	}
	return 1;
}

void
gpi_flags_to_string_list(unsigned int flags,
			 const StringFlagItem *map,
			 string_item_func func, void *data)
{
	int i;
	for (i=0; map[i].str != NULL; i++) {
		if ((flags == 0) && (map[i].flag == 0)) {
			func(map[i].str, data);
			break;
		} else if (0 != (flags & map[i].flag)) {
			func(map[i].str, data);
		}
	}
}

unsigned int
gpi_string_to_flag(const char *str,
	       const StringFlagItem *map)
{
	int i;
	for (i=0; map[i].str != NULL; i++) {
		if (0==strcmp(map[i].str, str)) {
			return map[i].flag;
		}
	}
  return 0;
}

int
gpi_string_or_to_flags(const char *str,
		       unsigned int *flags,
		       const StringFlagItem *map)
{
	int i;
	int found = 0;
	for (i=0; map[i].str != NULL; i++) {
		if (0==strcmp(map[i].str, str)) {
			(*flags) |= map[i].flag;
			found = 1;
		}
	}
	if (found) {
		return 0;
	} else {
		return 1;
	}
}

unsigned int
gpi_string_list_to_flags(const char *str[],
		     const StringFlagItem *map)
{
	int i;
	unsigned int flags = 0;
	for (i=0; str[i] != NULL; i++) {
		flags |= gpi_string_to_flag(str[i], map);
	}
	return flags;
}

#endif /* _GPHOTO2_INTERNAL_CODE */


/*
 * Local Variables:
 * c-file-style:"linux"
 * indent-tabs-mode:t
 * End:
 */