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