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
55static diag_handler_t *diag_handler = &default_diag_handler;
56static void *diag_handle;
57static diag_at_handler_t *diag_at_handler = &default_diag_at_handler;
58static void *diag_at_handle;
59
60#endif // !LELY_NO_DIAG
61
62#if !LELY_NO_STDIO
63
64size_t
65floc_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
96int
97snprintf_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
138void
139diag_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
147void
148diag_set_handler(diag_handler_t *handler, void *handle)
149{
150 diag_handler = handler;
151 diag_handle = handle;
152}
153
154void
155diag_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
163void
165{
166 diag_at_handler = handler;
167 diag_at_handle = handle;
168}
169
170void
171diag(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
179void
180vdiag(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
186void
187diag_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
196void
197vdiag_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
204void
205diag_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
214void
215vdiag_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
222void
223default_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
229void
230default_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
257void
258cmd_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
272void
273daemon_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
279void
280daemon_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
292void
293dialog_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
299void
300dialog_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
334void
335log_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
341void
342log_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
376void
377syslog_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
383void
384syslog_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
413int
414vsnprintf_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
420int
421vasprintf_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
427int
428vsnprintf_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
501int
502vasprintf_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
536const char *
537cmdname(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
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
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
void diag_set_handler(diag_handler_t *handler, void *handle)
Sets the handler function for diag().
Definition diag.c:148
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
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
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
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
void vdiag(enum diag_severity severity, int errc, const char *format, va_list ap)
Emits a diagnostic message.
Definition diag.c:180
const char * cmdname(const char *path)
Extracts the command name from a path.
Definition diag.c:537
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
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
void diag_at_set_handler(diag_at_handler_t *handler, void *handle)
Sets the handler function for diag_at().
Definition diag.c:164
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
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
void diag_at_get_handler(diag_at_handler_t **phandler, void **phandle)
Retrieves the handler function for diag_at().
Definition diag.c:155
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
int snprintf_floc(char *s, size_t n, const struct floc *at)
Prints a file location to a string buffer.
Definition diag.c:97
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
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
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
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition diag.c:171
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
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
void diag_get_handler(diag_handler_t **phandler, void **phandle)
Retrieves the handler function for diag().
Definition diag.c:139
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
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
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:90
diag_severity
The severity of a diagnostic message.
Definition diag.h:49
@ DIAG_DEBUG
A debug message.
Definition diag.h:51
@ DIAG_WARNING
A warning.
Definition diag.h:55
@ DIAG_INFO
An informational message.
Definition diag.h:53
@ DIAG_ERROR
An error.
Definition diag.h:57
@ DIAG_FATAL
A fatal error, which SHOULD result in program termination.
Definition diag.h:59
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
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.h:436
#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:39
int line
The line number (starting from 1).
Definition diag.h:43
int column
The column number (starting from 1).
Definition diag.h:45
const char * filename
The name of the file.
Definition diag.h:41