Lely core libraries  2.2.5
diag.c
Go to the documentation of this file.
1 
24 #include "util.h"
25 #include <lely/libc/stdio.h>
26 #include <lely/util/diag.h>
27 
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 
33 #ifdef _WIN32
34 #include <wtsapi32.h>
35 #ifdef _MSC_VER
36 #pragma comment(lib, "wtsapi32.lib")
37 #endif
38 #elif _POSIX_C_SOURCE >= 200809L && !defined(__NEWLIB__)
39 #include <syslog.h>
40 #endif
41 
42 #ifdef _WIN32
43 #ifndef LELY_DIALOG_DIAG_TIMEOUT
44 #define LELY_DIALOG_DIAG_TIMEOUT 10
45 #endif
46 #endif
47 
48 static diag_handler_t *diag_handler = &default_diag_handler;
49 static void *diag_handle;
50 static diag_at_handler_t *diag_at_handler = &default_diag_at_handler;
51 static void *diag_at_handle;
52 
53 size_t
54 floc_lex(struct floc *at, const char *begin, const char *end)
55 {
56  assert(begin);
57  assert(!end || end >= begin);
58 
59  const char *cp = begin;
60  while ((!end || cp < end) && *cp) {
61  switch (*cp++) {
62  case '\r':
63  if ((!end || cp < end) && *cp == '\n')
64  cp++;
65  // ... falls through ...
66  case '\n':
67  if (at) {
68  at->line++;
69  at->column = 1;
70  }
71  break;
72  case '\t':
73  if (at)
74  at->column = ((at->column + 7) & ~7) + 1;
75  break;
76  default:
77  if (at)
78  at->column++;
79  break;
80  }
81  }
82  return cp - begin;
83 }
84 
85 int
86 snprintf_floc(char *s, size_t n, const struct floc *at)
87 {
88  assert(at);
89 
90  if (!s)
91  n = 0;
92 
93  int t = 0;
94 
95  if (at->filename) {
96  int r = snprintf(s, n, "%s:", at->filename);
97  if (r < 0)
98  return r;
99  t += r;
100  r = MIN((size_t)r, n);
101  s += r;
102  n -= r;
103  if (at->line) {
104  r = snprintf(s, n, "%d:", at->line);
105  if (r < 0)
106  return r;
107  t += r;
108  r = MIN((size_t)r, n);
109  s += r;
110  n -= r;
111  if (at->column) {
112  r = snprintf(s, n, "%d:", at->column);
113  if (r < 0)
114  return r;
115  t += r;
116  }
117  }
118  }
119 
120  return t;
121 }
122 
123 void
124 diag_get_handler(diag_handler_t **phandler, void **phandle)
125 {
126  if (phandler)
127  *phandler = diag_handler;
128  if (phandle)
129  *phandle = diag_handle;
130 }
131 
132 void
133 diag_set_handler(diag_handler_t *handler, void *handle)
134 {
135  diag_handler = handler;
136  diag_handle = handle;
137 }
138 
139 void
140 diag_at_get_handler(diag_at_handler_t **phandler, void **phandle)
141 {
142  if (phandler)
143  *phandler = diag_at_handler;
144  if (phandle)
145  *phandle = diag_at_handle;
146 }
147 
148 void
149 diag_at_set_handler(diag_at_handler_t *handler, void *handle)
150 {
151  diag_at_handler = handler;
152  diag_at_handle = handle;
153 }
154 
155 void
156 diag(enum diag_severity severity, int errc, const char *format, ...)
157 {
158  va_list ap;
159  va_start(ap, format);
160  vdiag(severity, errc, format, ap);
161  va_end(ap);
162 }
163 
164 void
165 vdiag(enum diag_severity severity, int errc, const char *format, va_list ap)
166 {
167  if (diag_handler)
168  diag_handler(diag_handle, severity, errc, format, ap);
169 }
170 
171 void
172 diag_at(enum diag_severity severity, int errc, const struct floc *at,
173  const char *format, ...)
174 {
175  va_list ap;
176  va_start(ap, format);
177  vdiag_at(severity, errc, at, format, ap);
178  va_end(ap);
179 }
180 
181 void
182 vdiag_at(enum diag_severity severity, int errc, const struct floc *at,
183  const char *format, va_list ap)
184 {
185  if (diag_at_handler)
186  diag_at_handler(diag_at_handle, severity, errc, at, format, ap);
187 }
188 
189 void
190 diag_if(enum diag_severity severity, int errc, const struct floc *at,
191  const char *format, ...)
192 {
193  va_list ap;
194  va_start(ap, format);
195  vdiag_if(severity, errc, at, format, ap);
196  va_end(ap);
197 }
198 
199 void
200 vdiag_if(enum diag_severity severity, int errc, const struct floc *at,
201  const char *format, va_list ap)
202 {
203  if (at)
204  vdiag_at(severity, errc, at, format, ap);
205 }
206 
207 void
208 default_diag_handler(void *handle, enum diag_severity severity, int errc,
209  const char *format, va_list ap)
210 {
211  default_diag_at_handler(handle, severity, errc, NULL, format, ap);
212 }
213 
214 void
215 default_diag_at_handler(void *handle, enum diag_severity severity, int errc,
216  const struct floc *at, const char *format, va_list ap)
217 {
218  (void)handle;
219 
220  int errsv = errno;
221  char *s = NULL;
222  if (vasprintf_diag_at(&s, severity, errc, at, format, ap) >= 0) {
223  fprintf(stderr, "%s\n", s);
224  fflush(stderr);
225  }
226  free(s);
227  errno = errsv;
228 
229  if (severity == DIAG_FATAL)
230  abort();
231 }
232 
233 void
234 cmd_diag_handler(void *handle, enum diag_severity severity, int errc,
235  const char *format, va_list ap)
236 {
237  const char *cmd = handle;
238  if (cmd && *cmd) {
239  int errsv = errno;
240  fprintf(stderr, "%s: ", cmd);
241  fflush(stderr);
242  errno = errsv;
243  }
244 
245  default_diag_handler(handle, severity, errc, format, ap);
246 }
247 
248 void
249 daemon_diag_handler(void *handle, enum diag_severity severity, int errc,
250  const char *format, va_list ap)
251 {
252  daemon_diag_at_handler(handle, severity, errc, NULL, format, ap);
253 }
254 
255 void
256 daemon_diag_at_handler(void *handle, enum diag_severity severity, int errc,
257  const struct floc *at, const char *format, va_list ap)
258 {
259 #ifdef _WIN32
260  dialog_diag_at_handler(handle, severity, errc, at, format, ap);
261 #else
262  syslog_diag_at_handler(handle, severity, errc, at, format, ap);
263 #endif
264 }
265 
266 #ifdef _WIN32
267 
268 void
269 dialog_diag_handler(void *handle, enum diag_severity severity, int errc,
270  const char *format, va_list ap)
271 {
272  dialog_diag_at_handler(handle, severity, errc, NULL, format, ap);
273 }
274 
275 void
276 dialog_diag_at_handler(void *handle, enum diag_severity severity, int errc,
277  const struct floc *at, const char *format, va_list ap)
278 {
279  LPSTR pTitle = (LPSTR)(handle ? handle : "");
280 
281  DWORD Style = MB_OK | MB_SETFOREGROUND | MB_TOPMOST;
282  switch (severity) {
283  case DIAG_DEBUG:
284  case DIAG_INFO: Style |= MB_ICONINFORMATION; break;
285  case DIAG_WARNING: Style |= MB_ICONWARNING; break;
286  case DIAG_ERROR:
287  case DIAG_FATAL: Style |= MB_ICONERROR; break;
288  default: break;
289  }
290 
291  int errsv = errno;
292  char *pMessage = NULL;
293  if (vasprintf_diag_at(&pMessage, severity, errc, at, format, ap) >= 0) {
294  DWORD dwResponse;
295  WTSSendMessageA(WTS_CURRENT_SERVER_HANDLE,
296  WTSGetActiveConsoleSessionId(), pTitle,
297  strlen(pTitle), pMessage, strlen(pMessage),
298  Style, LELY_DIALOG_DIAG_TIMEOUT, &dwResponse,
299  FALSE);
300  }
301  free(pMessage);
302  errno = errsv;
303 
304  if (severity == DIAG_FATAL)
305  abort();
306 }
307 
308 #endif // _WIN32
309 
310 void
311 log_diag_handler(void *handle, enum diag_severity severity, int errc,
312  const char *format, va_list ap)
313 {
314  log_diag_at_handler(handle, severity, errc, NULL, format, ap);
315 }
316 
317 void
318 log_diag_at_handler(void *handle, enum diag_severity severity, int errc,
319  const struct floc *at, const char *format, va_list ap)
320 {
321  int errsv = errno;
322  time_t timer;
323  if (time(&timer) != -1) {
324 #ifdef _WIN32
325  struct tm tm;
326  struct tm *timeptr = NULL;
327  if (localtime_s(&tm, &timer))
328  timeptr = &tm;
329 #elif defined(_POSIX_C_SOURCE)
330  struct tm tm;
331  struct tm *timeptr = localtime_r(&timer, &tm);
332 #else
333  struct tm *timeptr = localtime(&timer);
334 #endif
335  if (timeptr) {
336  char buf[80];
337  // clang-format off
338  if (strftime(buf, sizeof(buf),
339  "%a, %d %b %Y %H:%M:%S %z", timeptr)
340  > 0) {
341  // clang-format on
342  fprintf(stderr, "%s: ", buf);
343  fflush(stderr);
344  }
345  }
346  }
347  errno = errsv;
348 
349  default_diag_at_handler(handle, severity, errc, at, format, ap);
350 }
351 
352 void
353 syslog_diag_handler(void *handle, enum diag_severity severity, int errc,
354  const char *format, va_list ap)
355 {
356  syslog_diag_at_handler(handle, severity, errc, NULL, format, ap);
357 }
358 
359 void
360 syslog_diag_at_handler(void *handle, enum diag_severity severity, int errc,
361  const struct floc *at, const char *format, va_list ap)
362 {
363 #if _POSIX_C_SOURCE >= 200809L && !defined(__NEWLIB__)
364  (void)handle;
365 
366  int priority = LOG_USER;
367  switch (severity) {
368  case DIAG_DEBUG: priority |= LOG_DEBUG; break;
369  case DIAG_INFO: priority |= LOG_INFO; break;
370  case DIAG_WARNING: priority |= LOG_WARNING; break;
371  case DIAG_ERROR: priority |= LOG_ERR; break;
372  case DIAG_FATAL: priority |= LOG_EMERG; break;
373  }
374 
375  int errsv = errno;
376  char *s = NULL;
377  if (vasprintf_diag_at(&s, DIAG_INFO, errc, at, format, ap) >= 0)
378  syslog(priority, "%s", s);
379  free(s);
380  errno = errsv;
381 
382  if (severity == DIAG_FATAL)
383  abort();
384 #else
385  log_diag_at_handler(handle, severity, errc, at, format, ap);
386 #endif
387 }
388 
389 int
390 vsnprintf_diag(char *s, size_t n, enum diag_severity severity, int errc,
391  const char *format, va_list ap)
392 {
393  return vsnprintf_diag_at(s, n, severity, errc, NULL, format, ap);
394 }
395 
396 int
397 vasprintf_diag(char **ps, enum diag_severity severity, int errc,
398  const char *format, va_list ap)
399 {
400  return vasprintf_diag_at(ps, severity, errc, NULL, format, ap);
401 }
402 
403 int
404 vsnprintf_diag_at(char *s, size_t n, enum diag_severity severity, int errc,
405  const struct floc *at, const char *format, va_list ap)
406 {
407  assert(format);
408 
409  if (!s)
410  n = 0;
411 
412  int r, t = 0;
413 
414  if (at && (r = snprintf_floc(s, n, at)) != 0) {
415  if (r < 0)
416  return r;
417  t += r;
418  r = MIN((size_t)r, n);
419  s += r;
420  n -= r;
421  r = snprintf(s, n, " ");
422  if (r < 0)
423  return r;
424  t += r;
425  r = MIN((size_t)r, n);
426  s += r;
427  n -= r;
428  }
429 
430  switch (severity) {
431  case DIAG_DEBUG: r = snprintf(s, n, "debug: "); break;
432  case DIAG_INFO: r = 0; break;
433  case DIAG_WARNING: r = snprintf(s, n, "warning: "); break;
434  case DIAG_ERROR: r = snprintf(s, n, "error: "); break;
435  case DIAG_FATAL: r = snprintf(s, n, "fatal: "); break;
436  default: r = 0; break;
437  }
438  if (r < 0)
439  return r;
440  t += r;
441  r = MIN((size_t)r, n);
442  s += r;
443  n -= r;
444 
445  if (format && *format) {
446  r = vsnprintf(s, n, format, ap);
447  if (r < 0)
448  return r;
449  t += r;
450  r = MIN((size_t)r, n);
451  s += r;
452  n -= r;
453  if (errc) {
454  r = snprintf(s, n, ": ");
455  if (r < 0)
456  return r;
457  t += r;
458  r = MIN((size_t)r, n);
459  s += r;
460  n -= r;
461  }
462  }
463 
464  if (errc) {
465  const char *errstr = errc2str(errc);
466  if (errstr) {
467  r = snprintf(s, n, "%s", errstr);
468  if (r < 0)
469  return r;
470  t += r;
471  }
472  }
473 
474  return t;
475 }
476 
477 int
478 vasprintf_diag_at(char **ps, enum diag_severity severity, int errc,
479  const struct floc *at, const char *format, va_list ap)
480 {
481  assert(ps);
482 
483  va_list aq;
484  va_copy(aq, ap);
485  int n = vsnprintf_diag_at(NULL, 0, severity, errc, at, format, aq);
486  va_end(aq);
487  if (n < 0)
488  return n;
489 
490  char *s = malloc(n + 1);
491  if (!s)
492  return -1;
493 
494  n = vsnprintf_diag_at(s, n + 1, severity, errc, at, format, ap);
495  if (n < 0) {
496  int errsv = errno;
497  free(s);
498  errno = errsv;
499  return n;
500  }
501 
502  *ps = s;
503  return n;
504 }
505 
506 const char *
507 cmdname(const char *path)
508 {
509  assert(path);
510 
511  const char *cmd = path;
512  while (*cmd)
513  cmd++;
514 #ifdef _WIN32
515  while (cmd >= path && *cmd != '\\')
516 #else
517  while (cmd >= path && *cmd != '/')
518 #endif
519  cmd--;
520  return ++cmd;
521 }
void syslog_diag_handler(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The diag() handler used for the system logging facilities.
Definition: diag.c:353
void daemon_diag_at_handler(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The diag_at() handler for daemons.
Definition: diag.c:256
void diag_set_handler(diag_handler_t *handler, void *handle)
Sets the handler function for diag().
Definition: diag.c:133
int vsnprintf_diag(char *s, size_t n, enum diag_severity severity, int errc, const char *format, va_list ap)
Prints a diagnostic message to a string buffer.
Definition: diag.c:390
size_t floc_lex(struct floc *at, const char *begin, const char *end)
Increments a file location by reading characters from a memory buffer.
Definition: diag.c:54
int vasprintf_diag_at(char **ps, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
Equivalent to vsnprintf_diag_at(), except that it allocates a string large enough to hold the output,...
Definition: diag.c:478
void cmd_diag_handler(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The diag() handler used for command-line programs.
Definition: diag.c:234
void vdiag(enum diag_severity severity, int errc, const char *format, va_list ap)
Emits a diagnostic message.
Definition: diag.c:165
void vdiag_at(enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
Emits a diagnostic message occurring at a location in a text file.
Definition: diag.c:182
const char * cmdname(const char *path)
Extracts the command name from a path.
Definition: diag.c:507
void default_diag_at_handler(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The default diag_at() handler.
Definition: diag.c:215
void diag_at_set_handler(diag_at_handler_t *handler, void *handle)
Sets the handler function for diag_at().
Definition: diag.c:149
int vasprintf_diag(char **ps, enum diag_severity severity, int errc, const char *format, va_list ap)
Equivalent to vsnprintf_diag(), except that it allocates a string large enough to hold the output,...
Definition: diag.c:397
void diag_if(enum diag_severity severity, int errc, const struct floc *at, const char *format,...)
Emits a diagnostic message occurring at a location in a text file.
Definition: diag.c:190
void diag_at_get_handler(diag_at_handler_t **phandler, void **phandle)
Retrieves the handler function for diag_at().
Definition: diag.c:140
void vdiag_if(enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
Emits a diagnostic message occurring at a location in a text file.
Definition: diag.c:200
int snprintf_floc(char *s, size_t n, const struct floc *at)
Prints a file location to a string buffer.
Definition: diag.c:86
void daemon_diag_handler(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The diag() handler for daemons.
Definition: diag.c:249
void diag_at(enum diag_severity severity, int errc, const struct floc *at, const char *format,...)
Emits a diagnostic message occurring at a location in a text file.
Definition: diag.c:172
void log_diag_at_handler(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The diag_at() handler used for log-files.
Definition: diag.c:318
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:156
void log_diag_handler(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The diag() handler for log files.
Definition: diag.c:311
int vsnprintf_diag_at(char *s, size_t n, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
Prints a diagnostic message occurring at a location in a text file to a string buffer.
Definition: diag.c:404
void diag_get_handler(diag_handler_t **phandler, void **phandle)
Retrieves the handler function for diag().
Definition: diag.c:124
void default_diag_handler(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The default diag() handler.
Definition: diag.c:208
void syslog_diag_at_handler(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The diag_at() handler used for the system logging facilities.
Definition: diag.c:360
This header file is part of the utilities library; it contains the diagnostic declarations.
void dialog_diag_at_handler(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The diag_at() handler for dialog boxes.
void diag_at_handler_t(void *handle, enum diag_severity severity, int errc, const struct floc *at, const char *format, va_list ap)
The function type of a handler for diag_at().
Definition: diag.h:82
diag_severity
The severity of a diagnostic message.
Definition: diag.h:41
@ DIAG_DEBUG
A debug message.
Definition: diag.h:43
@ DIAG_WARNING
A warning.
Definition: diag.h:47
@ DIAG_INFO
An informational message.
Definition: diag.h:45
@ DIAG_ERROR
An error.
Definition: diag.h:49
@ DIAG_FATAL
A fatal error, which SHOULD result in program termination.
Definition: diag.h:51
void diag_handler_t(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The function type of a handler for diag().
Definition: diag.h:68
void dialog_diag_handler(void *handle, enum diag_severity severity, int errc, const char *format, va_list ap)
The diag() handler for dialog boxes.
const char * errc2str(int errc)
Returns a string describing a native error code.
Definition: errnum.c:973
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
A location in a text file.
Definition: diag.h:31
int line
The line number (starting from 1).
Definition: diag.h:35
int column
The column number (starting from 1).
Definition: diag.h:37
const char * filename
The name of the file.
Definition: diag.h:33