YAZ  5.34.0
log.c
Go to the documentation of this file.
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 
11 #if HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14 
15 #include <yaz/yconfig.h>
16 
17 #ifdef WIN32
18 #include <windows.h>
19 #include <sys/stat.h>
20 #endif
21 
22 #if HAVE_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <yaz/yaz-iconv.h>
32 #include <yaz/errno.h>
33 #include <yaz/thread_id.h>
34 #include <yaz/log.h>
35 #include <yaz/mutex.h>
36 #include <yaz/snprintf.h>
37 #include <yaz/xmalloc.h>
38 #if YAZ_POSIX_THREADS
39 #include <pthread.h>
40 #endif
41 
43 
45 
46 struct {
47  enum l_file_type type;
48  FILE *log_file;
49  char l_prefix[512];
50  char l_prefix2[512];
51  char l_fname[512];
52 } yaz_log_info = {
53  use_stderr, 0, "", "", ""
54 };
55 
56 static void (*start_hook_func)(int, const char *, void *) = NULL;
57 static void *start_hook_info;
58 
59 static void (*end_hook_func)(int, const char *, void *) = NULL;
60 static void *end_hook_info;
61 
62 static void (*hook_func)(int, const char *, void *) = NULL;
63 static void *hook_info;
64 
65 static char l_old_default_format[] = "%H:%M:%S-%d/%m";
66 static char l_new_default_format[] = "%Y%m%d-%H%M%S";
67 #define TIMEFORMAT_LEN 50
68 #define TID_LEN 30
69 static char l_custom_format[TIMEFORMAT_LEN] = "";
71 
79 static int l_max_size = 0;
80 
81 #define MAX_MASK_NAMES 35 /* 32 bits plus a few combo names */
82 static struct {
83  int mask;
84  char *name;
86 {
87  { YLOG_FATAL, "fatal"},
88  { YLOG_DEBUG, "debug"},
89  { YLOG_WARN, "warn" },
90  { YLOG_LOG, "log" },
91  { YLOG_ERRNO, ""},
92  { YLOG_MALLOC, "malloc"},
93  { YLOG_TID, "tid" },
94  { YLOG_APP, "app" },
95  { YLOG_NOTIME, "notime" },
96  { YLOG_APP2, "app2" },
97  { YLOG_APP3, "app3" },
98  { YLOG_ALL, "all" },
99  { YLOG_FLUSH, "flush" },
100  { YLOG_LOGLVL, "loglevel" },
101  { 0, "none" },
102  { 0, NULL }
103  /* the rest will be filled in if the user defines dynamic modules*/
104 };
105 
106 static unsigned int next_log_bit = YLOG_LAST_BIT<<1; /* first dynamic bit */
107 
108 static int yaz_log_reopen_flag = 0;
109 
110 static YAZ_MUTEX log_mutex = 0;
111 
112 static void yaz_log_open(void);
113 
114 void yaz_log_lock(void)
115 {
117 }
118 
119 void yaz_log_unlock(void)
120 {
122 }
123 
125 {
126  char *env;
127 
128  if (log_mutex == 0)
130 #if YAZ_POSIX_THREADS
131  pthread_atfork(yaz_log_lock, yaz_log_unlock, yaz_log_unlock);
132 #endif
133  env = getenv("YAZ_LOG");
134  if (env)
136 }
137 
138 FILE *yaz_log_file(void)
139 {
140  FILE *f = 0;
141  switch (yaz_log_info.type)
142  {
143  case use_stderr: f = stderr; break;
144  case use_none: f = 0; break;
145  case use_file: f = yaz_log_info.log_file; break;
146  }
147  return f;
148 }
149 
150 void yaz_log_close(void)
151 {
152  if (yaz_log_info.type == use_file && yaz_log_info.log_file)
153  {
154  fclose(yaz_log_info.log_file);
155  yaz_log_info.log_file = 0;
156  }
157 }
158 
160 {
161  if (log_mutex)
162  {
164  yaz_log_close();
165  }
166 }
167 
168 void yaz_log_init_file(const char *fname)
169 {
171 
172  yaz_log_close();
173  if (fname)
174  {
175  if (*fname == '\0')
176  yaz_log_info.type = use_stderr; /* empty name; use stderr */
177  else
178  yaz_log_info.type = use_file;
179  strncpy(yaz_log_info.l_fname, fname, sizeof(yaz_log_info.l_fname)-1);
180  yaz_log_info.l_fname[sizeof(yaz_log_info.l_fname)-1] = '\0';
181  }
182  else
183  {
184  yaz_log_info.type = use_none; /* NULL name; use no file at all */
185  yaz_log_info.l_fname[0] = '\0';
186  }
187  yaz_log_open();
188 }
189 
190 static void rotate_log(const char *cur_fname)
191 {
192  int i;
193 
194 #ifdef WIN32
195  /* windows can't rename a file if it is open */
196  yaz_log_close();
197 #endif
198  for (i = 0; i<9; i++)
199  {
200  char fname_str[FILENAME_MAX];
201  struct stat stat_buf;
202 
203  yaz_snprintf(fname_str, sizeof(fname_str), "%s.%d", cur_fname, i);
204  if (stat(fname_str, &stat_buf) != 0)
205  break;
206  }
207  for (; i >= 0; --i)
208  {
209  char fname_str[2][FILENAME_MAX];
210 
211  if (i > 0)
212  yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
213  "%s.%d", cur_fname, i-1);
214  else
215  yaz_snprintf(fname_str[0], sizeof(fname_str[0]),
216  "%s", cur_fname);
217  yaz_snprintf(fname_str[1], sizeof(fname_str[1]),
218  "%s.%d", cur_fname, i);
219 #ifdef WIN32
220  MoveFileEx(fname_str[0], fname_str[1], MOVEFILE_REPLACE_EXISTING);
221 #else
222  rename(fname_str[0], fname_str[1]);
223 #endif
224  }
225 }
226 
227 
228 void yaz_log_init_level(int level)
229 {
231  if ( (l_level & YLOG_FLUSH) != (level & YLOG_FLUSH) )
232  {
233  l_level = level;
234  yaz_log_open(); /* make sure we set buffering right */
235  }
236  else
237  l_level = level;
238 
239  if (l_level & YLOG_LOGLVL)
240  { /* dump the log level bits */
241  const char *bittype = "Static ";
242  int i, sz;
243 
244  yaz_log(YLOG_LOGLVL, "Setting log level to %d = 0x%08x",
245  l_level, l_level);
246  /* determine size of mask_names (locked) */
247  for (sz = 0; mask_names[sz].name; sz++)
248  ;
249  /* second pass without lock */
250  for (i = 0; i < sz; i++)
251  if (mask_names[i].mask && *mask_names[i].name)
252  if (strcmp(mask_names[i].name, "all") != 0)
253  {
254  yaz_log(YLOG_LOGLVL, "%s log bit %08x '%s' is %s",
255  bittype, mask_names[i].mask, mask_names[i].name,
256  (level & mask_names[i].mask)? "ON": "off");
257  if (mask_names[i].mask > YLOG_LAST_BIT)
258  bittype = "Dynamic";
259  }
260  }
261 }
262 
263 void yaz_log_init_prefix(const char *prefix)
264 {
265  if (prefix && *prefix)
266  yaz_snprintf(yaz_log_info.l_prefix,
267  sizeof(yaz_log_info.l_prefix), "%s ", prefix);
268  else
269  *yaz_log_info.l_prefix = 0;
270 }
271 
272 void yaz_log_init_prefix2(const char *prefix)
273 {
274  if (prefix && *prefix)
275  yaz_snprintf(yaz_log_info.l_prefix2,
276  sizeof(yaz_log_info.l_prefix2), "%s ", prefix);
277  else
278  *yaz_log_info.l_prefix2 = 0;
279 }
280 
281 void yaz_log_init(int level, const char *prefix, const char *fname)
282 {
284  yaz_log_init_level(level);
285  yaz_log_init_prefix(prefix);
286  if (fname && *fname)
287  yaz_log_init_file(fname);
288 }
289 
291 {
292  if (mx > 0)
293  l_max_size = mx;
294  else
295  l_max_size = 0;
296 }
297 
298 void yaz_log_set_handler(void (*func)(int, const char *, void *), void *info)
299 {
300  hook_func = func;
301  hook_info = info;
302 }
303 
304 void log_event_start(void (*func)(int, const char *, void *), void *info)
305 {
306  start_hook_func = func;
307  start_hook_info = info;
308 }
309 
310 void log_event_end(void (*func)(int, const char *, void *), void *info)
311 {
312  end_hook_func = func;
313  end_hook_info = info;
314 }
315 
316 static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
317 {
318  char new_filename[512];
319  static char cur_filename[512] = "";
320 
321  if (yaz_log_info.type != use_file)
322  return;
323 
325  {
326  force = 1;
328  }
329  if (*yaz_log_info.l_fname)
330  {
331  strftime(new_filename, sizeof(new_filename)-1, yaz_log_info.l_fname,
332  tm);
333  if (strcmp(new_filename, cur_filename))
334  {
335  strcpy(cur_filename, new_filename);
336  force = 1;
337  }
338  }
339 
340  if (l_max_size > 0 && yaz_log_info.log_file)
341  {
342  long flen = ftell(yaz_log_info.log_file);
343  if (flen > l_max_size)
344  {
345  rotate_log(cur_filename);
346  force = 1;
347  }
348  }
349  if (force && *cur_filename)
350  {
351  FILE *new_file;
352 #ifdef WIN32
353  yaz_log_close();
354 #endif
355  if (!strncmp(cur_filename, "fd=", 3))
356  new_file = fdopen(atoi(cur_filename + 3), filemode);
357  else
358  new_file = fopen(cur_filename, filemode);
359  if (new_file)
360  {
361  yaz_log_close();
362  yaz_log_info.log_file = new_file;
363  }
364  else
365  {
366  /* disable log rotate */
367  l_max_size = 0;
368  }
369  }
370 }
371 
372 static void yaz_log_do_reopen(const char *filemode)
373 {
374  time_t cur_time = time(0);
375 #if HAVE_LOCALTIME_R
376  struct tm tm0, *tm = &tm0;
377 #else
378  struct tm *tm;
379 #endif
380 
381  yaz_log_lock();
382 #if HAVE_LOCALTIME_R
383  localtime_r(&cur_time, tm);
384 #else
385  tm = localtime(&cur_time);
386 #endif
387  yaz_log_open_check(tm, 1, filemode);
388  yaz_log_unlock();
389 }
390 
392 {
394 }
395 
396 static void yaz_log_open()
397 {
398  yaz_log_do_reopen("a");
399 }
400 
402 {
403  yaz_log_do_reopen("w");
404 }
405 
406 static void yaz_strftime(char *dst, size_t sz,
407  const char *fmt, const struct tm *tm)
408 {
409  strftime(dst, sz, fmt, tm);
410 }
411 
412 static void yaz_log_to_file(int level, const char *fmt, va_list ap,
413  const char *error_cp)
414 {
415  FILE *file;
416  time_t ti = time(0);
417 #if HAVE_LOCALTIME_R
418  struct tm tm0, *tm = &tm0;
419 #else
420  struct tm *tm;
421 #endif
422 
423  yaz_log_lock();
424 #if HAVE_LOCALTIME_R
425  localtime_r(&ti, tm);
426 #else
427  tm = localtime(&ti);
428 #endif
429 
430  yaz_log_open_check(tm, 0, "a");
431  file = yaz_log_file(); /* file may change in yaz_log_open_check */
432 
433  if (file)
434  {
435  char tbuf[TIMEFORMAT_LEN];
436  char tid[TID_LEN];
437  char flags[1024];
438  int i;
439 
440  *flags = '\0';
441  for (i = 0; level && mask_names[i].name; i++)
442  if ( mask_names[i].mask & level)
443  {
444  if (*mask_names[i].name && mask_names[i].mask &&
445  mask_names[i].mask != YLOG_ALL)
446  {
447  if (strlen(flags) + strlen(mask_names[i].name)
448  < sizeof(flags) - 4)
449  {
450  strcat(flags, "[");
451  strcat(flags, mask_names[i].name);
452  strcat(flags, "]");
453  }
454  level &= ~mask_names[i].mask;
455  }
456  }
457 
458  tbuf[0] = '\0';
459  if (!(l_level & YLOG_NOTIME))
460  {
462  tbuf[TIMEFORMAT_LEN-2] = '\0';
463  }
464  if (tbuf[0])
465  strcat(tbuf, " ");
466  tid[0] = '\0';
467 
468  if (l_level & YLOG_TID)
469  {
470  yaz_thread_id_cstr(tid, sizeof(tid)-1);
471  if (tid[0])
472  strcat(tid, " ");
473  }
474 
475  fprintf(file, "%s%s%s%s %s", tbuf, yaz_log_info.l_prefix,
476  tid, flags, yaz_log_info.l_prefix2);
477  vfprintf(file, fmt, ap);
478  if (error_cp)
479  fprintf(file, " [%s]", error_cp);
480  fputs("\n", file);
481  if (l_level & YLOG_FLUSH)
482  fflush(file);
483  }
484  yaz_log_unlock();
485 }
486 
487 void yaz_log(int level, const char *fmt, ...)
488 {
489  va_list ap;
490  FILE *file;
491  int o_level = level;
492  char *error_cp = 0, error_buf[128];
493 
494  if (o_level & YLOG_ERRNO)
495  {
496  yaz_strerror(error_buf, sizeof(error_buf));
497  error_cp = error_buf;
498  }
500  if (!(level & l_level))
501  return;
502  va_start(ap, fmt);
503 
504  file = yaz_log_file();
506  {
507  char buf[1024];
508  /* 30 is enough for our 'rest of output' message */
509  yaz_vsnprintf(buf, sizeof(buf)-30, fmt, ap);
510  if (strlen(buf) >= sizeof(buf)-31)
511  strcat(buf, " [rest of output omitted]");
512  if (start_hook_func)
513  (*start_hook_func)(o_level, buf, start_hook_info);
514  if (hook_func)
515  (*hook_func)(o_level, buf, hook_info);
516  if (file)
517  yaz_log_to_file(level, fmt, ap, error_cp);
518  if (end_hook_func)
519  (*end_hook_func)(o_level, buf, end_hook_info);
520  }
521  else
522  {
523  if (file)
524  yaz_log_to_file(level, fmt, ap, error_cp);
525  }
526  va_end(ap);
527 }
528 
529 void yaz_log_time_format(const char *fmt)
530 {
531  if ( !fmt || !*fmt)
532  { /* no format, default to new */
534  return;
535  }
536  if (0==strcmp(fmt, "old"))
537  { /* force the old format */
539  return;
540  }
541  /* else use custom format */
542  strncpy(l_custom_format, fmt, TIMEFORMAT_LEN-1);
545 }
546 
548 static char *clean_name(const char *name, size_t len, char *namebuf, size_t buflen)
549 {
550  char *p = namebuf;
551  char *start = namebuf;
552  if (buflen <= len)
553  len = buflen-1;
554  strncpy(namebuf, name, len);
555  namebuf[len] = '\0';
556  while ((p = strchr(start, '/')))
557  start = p+1;
558  if ((p = strrchr(start, '.')))
559  *p = '\0';
560  return start;
561 }
562 
563 static int define_module_bit(const char *name)
564 {
565  size_t i;
566 
567  for (i = 0; mask_names[i].name; i++)
568  if (0 == strcmp(mask_names[i].name, name))
569  {
570  return mask_names[i].mask;
571  }
572  if ( (i>=MAX_MASK_NAMES) || (next_log_bit & (1U<<31) ))
573  {
574  yaz_log(YLOG_WARN, "No more log bits left, not logging '%s'", name);
575  return 0;
576  }
577  mask_names[i].mask = (int) next_log_bit; /* next_log_bit can hold int */
579  mask_names[i].name = (char *) malloc(strlen(name)+1);
580  strcpy(mask_names[i].name, name);
581  mask_names[i+1].name = NULL;
582  mask_names[i+1].mask = 0;
583  return mask_names[i].mask;
584 }
585 
586 int yaz_log_module_level(const char *name)
587 {
588  int i;
589  char clean[255];
590  char *n = clean_name(name, strlen(name), clean, sizeof(clean));
592 
593  for (i = 0; mask_names[i].name; i++)
594  if (0==strcmp(n, mask_names[i].name))
595  {
596  yaz_log(YLOG_LOGLVL, "returning log bit 0x%x for '%s' %s",
597  mask_names[i].mask, n,
598  strcmp(n,name) ? name : "");
599  return mask_names[i].mask;
600  }
601  yaz_log(YLOG_LOGLVL, "returning NO log bit for '%s' %s", n,
602  strcmp(n, name) ? name : "" );
603  return 0;
604 }
605 
606 int yaz_log_mask_str(const char *str)
607 {
608  yaz_init_globals(); /* since l_level may be affected */
609  return yaz_log_mask_str_x(str, l_level);
610 }
611 
612 /* this function is called by yaz_log_init_globals & yaz_init_globals
613  and, thus, may not call any of them indirectly */
614 int yaz_log_mask_str_x(const char *str, int level)
615 {
616  const char *p;
617 
618  while (*str)
619  {
620  int negated = 0;
621  for (p = str; *p && *p != ','; p++)
622  ;
623  if (*str=='-')
624  {
625  negated = 1;
626  str++;
627  }
628  if (yaz_isdigit(*str))
629  {
630  level = atoi(str);
631  }
632  else
633  {
634  char clean[509];
635  char *n = clean_name(str, (size_t) (p - str), clean, sizeof(clean));
636  int mask = define_module_bit(n);
637  if (!mask)
638  level = 0; /* 'none' clears them all */
639  else if (negated)
640  level &= ~mask;
641  else
642  level |= mask;
643  }
644  if (*p == ',')
645  p++;
646  str = p;
647  }
648  return level;
649 }
650 /*
651  * Local variables:
652  * c-basic-offset: 4
653  * c-file-style: "Stroustrup"
654  * indent-tabs-mode: nil
655  * End:
656  * vim: shiftwidth=4 tabstop=8 expandtab
657  */
658 
void * malloc(YYSIZE_T)
void yaz_strerror(char *buf, size_t bufsz)
returns system error description string
Definition: errno.c:41
Header for errno utilities.
void yaz_init_globals(void)
Definition: init_globals.c:47
void yaz_log_set_handler(void(*func)(int, const char *, void *), void *info)
sets custom log handler
Definition: log.c:298
static void yaz_log_to_file(int level, const char *fmt, va_list ap, const char *error_cp)
Definition: log.c:412
static int l_level
Definition: log.c:42
FILE * yaz_log_file(void)
returns FILE handle for log or NULL if no file is in use
Definition: log.c:138
static void(* end_hook_func)(int, const char *, void *)
Definition: log.c:59
void yaz_log_init_prefix(const char *prefix)
sets log message prefix
Definition: log.c:263
void yaz_log_init_globals(void)
Definition: log.c:124
#define MAX_MASK_NAMES
Definition: log.c:81
static unsigned int next_log_bit
Definition: log.c:106
static struct @4 mask_names[MAX_MASK_NAMES]
char l_prefix2[512]
Definition: log.c:50
int yaz_log_mask_str(const char *str)
converts log level string to log level (integer)
Definition: log.c:606
void yaz_log_init_max_size(int mx)
sets limit in bytes for size for log file
Definition: log.c:290
void log_event_start(void(*func)(int, const char *, void *), void *info)
Definition: log.c:304
void yaz_log_init_prefix2(const char *prefix)
sets second log message prefix
Definition: log.c:272
static void(* start_hook_func)(int, const char *, void *)
Definition: log.c:56
void yaz_log_init_level(int level)
sets log level
Definition: log.c:228
char * name
Definition: log.c:84
static void yaz_strftime(char *dst, size_t sz, const char *fmt, const struct tm *tm)
Definition: log.c:406
static void * end_hook_info
Definition: log.c:60
static void * hook_info
Definition: log.c:63
static char l_custom_format[TIMEFORMAT_LEN]
Definition: log.c:69
static int l_max_size
Definition: log.c:79
void yaz_log_reopen()
reopen current log file (unless disabled or stderr)
Definition: log.c:391
void yaz_log_init(int level, const char *prefix, const char *fname)
sets level, prefix and filename for logging
Definition: log.c:281
static char * l_actual_format
Definition: log.c:70
static YAZ_MUTEX log_mutex
Definition: log.c:110
void yaz_log_deinit_globals(void)
Definition: log.c:159
static char * clean_name(const char *name, size_t len, char *namebuf, size_t buflen)
Definition: log.c:548
static int yaz_log_reopen_flag
Definition: log.c:108
enum l_file_type type
Definition: log.c:47
char l_prefix[512]
Definition: log.c:49
l_file_type
Definition: log.c:44
@ use_file
Definition: log.c:44
@ use_stderr
Definition: log.c:44
@ use_none
Definition: log.c:44
static char l_old_default_format[]
Definition: log.c:65
static int define_module_bit(const char *name)
Definition: log.c:563
static void(* hook_func)(int, const char *, void *)
Definition: log.c:62
struct @3 yaz_log_info
static void * start_hook_info
Definition: log.c:57
int mask
Definition: log.c:83
FILE * log_file
Definition: log.c:48
#define TID_LEN
Definition: log.c:68
static void yaz_log_do_reopen(const char *filemode)
Definition: log.c:372
void yaz_log_lock(void)
Lock for YAZ log writes.
Definition: log.c:114
void yaz_log_time_format(const char *fmt)
sets time format for log mesages
Definition: log.c:529
static void yaz_log_open_check(struct tm *tm, int force, const char *filemode)
Definition: log.c:316
void yaz_log(int level, const char *fmt,...)
Writes log message.
Definition: log.c:487
static void rotate_log(const char *cur_fname)
Definition: log.c:190
void yaz_log_close(void)
Definition: log.c:150
void yaz_log_init_file(const char *fname)
sets log file
Definition: log.c:168
void yaz_log_trunc()
Truncate the log file.
Definition: log.c:401
char l_fname[512]
Definition: log.c:51
void yaz_log_unlock(void)
Unlock for YAZ log writes.
Definition: log.c:119
int yaz_log_mask_str_x(const char *str, int level)
converts log level string to log level with "start" level
Definition: log.c:614
static char l_new_default_format[]
Definition: log.c:66
int yaz_log_module_level(const char *name)
returns level for module
Definition: log.c:586
#define TIMEFORMAT_LEN
Definition: log.c:67
static void yaz_log_open(void)
Definition: log.c:396
void log_event_end(void(*func)(int, const char *, void *), void *info)
Definition: log.c:310
Logging utility.
#define YLOG_APP2
log level: application 2
Definition: log.h:60
#define YLOG_WARN
log level: warning
Definition: log.h:46
#define YLOG_FATAL
log level: fatal
Definition: log.h:42
#define YLOG_MALLOC
log level: malloc debug
Definition: log.h:56
#define YLOG_ERRNO
log level: append system error message
Definition: log.h:50
#define YLOG_FLUSH
log level: flush
Definition: log.h:64
#define YLOG_DEFAULT_LEVEL
default log level
Definition: log.h:72
#define YLOG_DEBUG
log level: debugging
Definition: log.h:44
#define YLOG_APP
log level: application
Definition: log.h:54
#define YLOG_NOTIME
log level: do not output date and time
Definition: log.h:58
#define YLOG_TID
log level: append thread Id
Definition: log.h:52
#define YLOG_ALL
Definition: log.h:69
#define YLOG_LOGLVL
dynamic log level start
Definition: log.h:66
#define YLOG_LOG
log level: log (regular)
Definition: log.h:48
#define YLOG_APP3
log level: application 3
Definition: log.h:62
#define YLOG_LAST_BIT
last bit for regular log bits . Rest are dynamic
Definition: log.h:78
void yaz_mutex_leave(YAZ_MUTEX p)
leave critical section / AKA unlock
Definition: mutex.c:123
void yaz_mutex_enter(YAZ_MUTEX p)
enter critical section / AKA lock
Definition: mutex.c:72
void yaz_mutex_create(YAZ_MUTEX *p)
create MUTEX
Definition: mutex.c:43
void yaz_mutex_destroy(YAZ_MUTEX *p)
destroy MUTEX
Definition: mutex.c:141
Header for Mutex functions.
void yaz_vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
Definition: snprintf.c:17
void yaz_snprintf(char *buf, size_t size, const char *fmt,...)
Definition: snprintf.c:31
Header for config file reading utilities.
void yaz_thread_id_cstr(char *buf, size_t buf_max)
format current thread as printable C-string
Definition: thread_id.c:32
Header for Mutex functions.
Header for memory handling functions.
Header for YAZ iconv interface.
#define yaz_isdigit(x)
Definition: yaz-iconv.h:86
Header with fundamental macros.