]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/debug.c
Merge changes from CUPS 1.5svn-r9000.
[thirdparty/cups.git] / cups / debug.c
1 /*
2 * "$Id$"
3 *
4 * Debugging functions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2008-2009 by Apple Inc.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 *
16 * Contents:
17 *
18 * debug_vsnprintf() - Format a string into a fixed size buffer.
19 * _cups_debug_printf() - Write a formatted line to the log.
20 * _cups_debug_puts() - Write a single line to the log.
21 */
22
23 /*
24 * Include necessary headers...
25 */
26
27 #include "globals.h"
28 #include "debug.h"
29 #ifdef WIN32
30 # include <io.h>
31 #else
32 # include <sys/time.h>
33 # include <unistd.h>
34 #endif /* WIN32 */
35 #include <fcntl.h>
36 #ifndef WIN32
37 # include <regex.h>
38 #endif /* WIN32 */
39
40
41 /*
42 * Globals...
43 */
44
45 int _cups_debug_fd = -1;
46 /* Debug log file descriptor */
47 int _cups_debug_level = 1;
48 /* Log level (0 to 9) */
49
50
51 #ifdef DEBUG
52 /*
53 * Local globals...
54 */
55
56 # ifndef WIN32
57 static regex_t *debug_filter = NULL;
58 /* Filter expression for messages */
59 # endif /* !WIN32 */
60 static int debug_init = 0; /* Did we initialize debugging? */
61 # ifdef HAVE_PTHREAD_H
62 static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
63 /* Mutex to control initialization */
64 # endif /* HAVE_PTHREAD_H */
65
66
67 /*
68 * 'debug_vsnprintf()' - Format a string into a fixed size buffer.
69 */
70
71 static int /* O - Number of bytes formatted */
72 debug_vsnprintf(char *buffer, /* O - Output buffer */
73 size_t bufsize, /* O - Size of output buffer */
74 const char *format, /* I - printf-style format string */
75 va_list ap) /* I - Pointer to additional arguments */
76 {
77 char *bufptr, /* Pointer to position in buffer */
78 *bufend, /* Pointer to end of buffer */
79 size, /* Size character (h, l, L) */
80 type; /* Format type character */
81 int width, /* Width of field */
82 prec; /* Number of characters of precision */
83 char tformat[100], /* Temporary format string for sprintf() */
84 *tptr, /* Pointer into temporary format */
85 temp[1024]; /* Buffer for formatted numbers */
86 char *s; /* Pointer to string */
87 int bytes; /* Total number of bytes needed */
88
89
90 if (!buffer || bufsize < 2 || !format)
91 return (-1);
92
93 /*
94 * Loop through the format string, formatting as needed...
95 */
96
97 bufptr = buffer;
98 bufend = buffer + bufsize - 1;
99 bytes = 0;
100
101 while (*format)
102 {
103 if (*format == '%')
104 {
105 tptr = tformat;
106 *tptr++ = *format++;
107
108 if (*format == '%')
109 {
110 if (bufptr < bufend)
111 *bufptr++ = *format;
112 bytes ++;
113 format ++;
114 continue;
115 }
116 else if (strchr(" -+#\'", *format))
117 *tptr++ = *format++;
118
119 if (*format == '*')
120 {
121 /*
122 * Get width from argument...
123 */
124
125 format ++;
126 width = va_arg(ap, int);
127
128 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
129 tptr += strlen(tptr);
130 }
131 else
132 {
133 width = 0;
134
135 while (isdigit(*format & 255))
136 {
137 if (tptr < (tformat + sizeof(tformat) - 1))
138 *tptr++ = *format;
139
140 width = width * 10 + *format++ - '0';
141 }
142 }
143
144 if (*format == '.')
145 {
146 if (tptr < (tformat + sizeof(tformat) - 1))
147 *tptr++ = *format;
148
149 format ++;
150
151 if (*format == '*')
152 {
153 /*
154 * Get precision from argument...
155 */
156
157 format ++;
158 prec = va_arg(ap, int);
159
160 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
161 tptr += strlen(tptr);
162 }
163 else
164 {
165 prec = 0;
166
167 while (isdigit(*format & 255))
168 {
169 if (tptr < (tformat + sizeof(tformat) - 1))
170 *tptr++ = *format;
171
172 prec = prec * 10 + *format++ - '0';
173 }
174 }
175 }
176
177 if (*format == 'l' && format[1] == 'l')
178 {
179 size = 'L';
180
181 if (tptr < (tformat + sizeof(tformat) - 2))
182 {
183 *tptr++ = 'l';
184 *tptr++ = 'l';
185 }
186
187 format += 2;
188 }
189 else if (*format == 'h' || *format == 'l' || *format == 'L')
190 {
191 if (tptr < (tformat + sizeof(tformat) - 1))
192 *tptr++ = *format;
193
194 size = *format++;
195 }
196 else
197 size = 0;
198
199 if (!*format)
200 break;
201
202 if (tptr < (tformat + sizeof(tformat) - 1))
203 *tptr++ = *format;
204
205 type = *format++;
206 *tptr = '\0';
207
208 switch (type)
209 {
210 case 'E' : /* Floating point formats */
211 case 'G' :
212 case 'e' :
213 case 'f' :
214 case 'g' :
215 if ((width + 2) > sizeof(temp))
216 break;
217
218 sprintf(temp, tformat, va_arg(ap, double));
219
220 bytes += (int)strlen(temp);
221
222 if (bufptr)
223 {
224 if ((bufptr + strlen(temp)) > bufend)
225 {
226 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
227 bufptr = bufend;
228 }
229 else
230 {
231 strcpy(bufptr, temp);
232 bufptr += strlen(temp);
233 }
234 }
235 break;
236
237 case 'B' : /* Integer formats */
238 case 'X' :
239 case 'b' :
240 case 'd' :
241 case 'i' :
242 case 'o' :
243 case 'u' :
244 case 'x' :
245 if ((width + 2) > sizeof(temp))
246 break;
247
248 #ifdef HAVE_LONG_LONG
249 if (size == 'L')
250 sprintf(temp, tformat, va_arg(ap, long long));
251 else
252 #endif /* HAVE_LONG_LONG */
253 if (size == 'l')
254 sprintf(temp, tformat, va_arg(ap, long));
255 else
256 sprintf(temp, tformat, va_arg(ap, int));
257
258 bytes += (int)strlen(temp);
259
260 if (bufptr)
261 {
262 if ((bufptr + strlen(temp)) > bufend)
263 {
264 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
265 bufptr = bufend;
266 }
267 else
268 {
269 strcpy(bufptr, temp);
270 bufptr += strlen(temp);
271 }
272 }
273 break;
274
275 case 'p' : /* Pointer value */
276 if ((width + 2) > sizeof(temp))
277 break;
278
279 sprintf(temp, tformat, va_arg(ap, void *));
280
281 bytes += (int)strlen(temp);
282
283 if (bufptr)
284 {
285 if ((bufptr + strlen(temp)) > bufend)
286 {
287 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
288 bufptr = bufend;
289 }
290 else
291 {
292 strcpy(bufptr, temp);
293 bufptr += strlen(temp);
294 }
295 }
296 break;
297
298 case 'c' : /* Character or character array */
299 bytes += width;
300
301 if (bufptr)
302 {
303 if (width <= 1)
304 *bufptr++ = va_arg(ap, int);
305 else
306 {
307 if ((bufptr + width) > bufend)
308 width = (int)(bufend - bufptr);
309
310 memcpy(bufptr, va_arg(ap, char *), (size_t)width);
311 bufptr += width;
312 }
313 }
314 break;
315
316 case 's' : /* String */
317 if ((s = va_arg(ap, char *)) == NULL)
318 s = "(null)";
319
320 /*
321 * Copy the C string, replacing control chars and \ with
322 * C character escapes...
323 */
324
325 for (bufend --; *s && bufptr < bufend; s ++)
326 {
327 if (*s == '\n')
328 {
329 *bufptr++ = '\\';
330 *bufptr++ = 'n';
331 }
332 else if (*s == '\r')
333 {
334 *bufptr++ = '\\';
335 *bufptr++ = 'r';
336 }
337 else if (*s == '\t')
338 {
339 *bufptr++ = '\\';
340 *bufptr++ = 't';
341 }
342 else if (*s == '\\')
343 {
344 *bufptr++ = '\\';
345 *bufptr++ = '\\';
346 }
347 else if (*s == '\'')
348 {
349 *bufptr++ = '\\';
350 *bufptr++ = '\'';
351 }
352 else if (*s == '\"')
353 {
354 *bufptr++ = '\\';
355 *bufptr++ = '\"';
356 }
357 else if ((*s & 255) < ' ')
358 {
359 *bufptr++ = '\\';
360 *bufptr++ = '0';
361 *bufptr++ = '0' + *s / 8;
362 *bufptr++ = '0' + (*s & 7);
363 }
364 else
365 *bufptr++ = *s;
366 }
367
368 bufend ++;
369 break;
370
371 case 'n' : /* Output number of chars so far */
372 *(va_arg(ap, int *)) = bytes;
373 break;
374 }
375 }
376 else
377 {
378 bytes ++;
379
380 if (bufptr < bufend)
381 *bufptr++ = *format;
382
383 format ++;
384 }
385 }
386
387 /*
388 * Nul-terminate the string and return the number of characters needed.
389 */
390
391 *bufptr = '\0';
392
393 return (bytes);
394 }
395
396
397 /*
398 * '_cups_debug_printf()' - Write a formatted line to the log.
399 */
400
401 void
402 _cups_debug_printf(const char *format, /* I - Printf-style format string */
403 ...) /* I - Additional arguments as needed */
404 {
405 va_list ap; /* Pointer to arguments */
406 struct timeval curtime; /* Current time */
407 char buffer[2048]; /* Output buffer */
408 size_t bytes; /* Number of bytes in buffer */
409 int level; /* Log level in message */
410 const char *cups_debug_filter,
411 /* CUPS_DEBUG_FILTER environment variable */
412 *cups_debug_level,
413 /* CUPS_DEBUG_LEVEL environment variable */
414 *cups_debug_log;/* CUPS_DEBUG_LOG environment variable */
415
416
417 /*
418 * See if we need to do any logging...
419 */
420
421 if (!debug_init)
422 {
423 /*
424 * Get a lock on the debug initializer, then re-check in case another
425 * thread already did it...
426 */
427
428 pthread_mutex_lock(&debug_mutex);
429
430 if (!debug_init)
431 {
432 if ((cups_debug_log = getenv("CUPS_DEBUG_LOG")) == NULL)
433 _cups_debug_fd = -1;
434 else if (!strcmp(cups_debug_log, "-"))
435 _cups_debug_fd = 2;
436 else
437 {
438 snprintf(buffer, sizeof(buffer), cups_debug_log, getpid());
439
440 if (buffer[0] == '+')
441 _cups_debug_fd = open(buffer + 1, O_WRONLY | O_APPEND | O_CREAT, 0644);
442 else
443 _cups_debug_fd = open(buffer, O_WRONLY | O_TRUNC | O_CREAT, 0644);
444 }
445
446 if ((cups_debug_level = getenv("CUPS_DEBUG_LEVEL")) != NULL)
447 _cups_debug_level = atoi(cups_debug_level);
448
449 if ((cups_debug_filter = getenv("CUPS_DEBUG_FILTER")) != NULL)
450 {
451 if ((debug_filter = (regex_t *)calloc(1, sizeof(regex_t))) == NULL)
452 fputs("Unable to allocate memory for CUPS_DEBUG_FILTER - results not "
453 "filtered!\n", stderr);
454 else if (regcomp(debug_filter, cups_debug_filter, REG_EXTENDED))
455 {
456 fputs("Bad regular expression in CUPS_DEBUG_FILTER - results not "
457 "filtered!\n", stderr);
458 free(debug_filter);
459 debug_filter = NULL;
460 }
461 }
462
463 debug_init = 1;
464 }
465
466 pthread_mutex_unlock(&debug_mutex);
467 }
468
469 if (_cups_debug_fd < 0)
470 return;
471
472 /*
473 * Filter as needed...
474 */
475
476 if (isdigit(format[0]))
477 level = *format++ - '0';
478 else
479 level = 0;
480
481 if (level > _cups_debug_level)
482 return;
483
484 if (debug_filter)
485 {
486 int result; /* Filter result */
487
488 pthread_mutex_lock(&debug_mutex);
489 result = regexec(debug_filter, format, 0, NULL, 0);
490 pthread_mutex_unlock(&debug_mutex);
491
492 if (result)
493 return;
494 }
495
496 /*
497 * Format the message...
498 */
499
500 gettimeofday(&curtime, NULL);
501 snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%03d ",
502 (int)((curtime.tv_sec / 3600) % 24),
503 (int)((curtime.tv_sec / 60) % 60),
504 (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000));
505
506 va_start(ap, format);
507 debug_vsnprintf(buffer + 13, sizeof(buffer) - 14, format, ap);
508 va_end(ap);
509
510 bytes = strlen(buffer);
511 if (buffer[bytes - 1] != '\n')
512 {
513 buffer[bytes] = '\n';
514 bytes ++;
515 buffer[bytes] = '\0';
516 }
517
518 /*
519 * Write it out...
520 */
521
522 write(_cups_debug_fd, buffer, bytes);
523 }
524
525
526 /*
527 * '_cups_debug_puts()' - Write a single line to the log.
528 */
529
530 void
531 _cups_debug_puts(const char *s) /* I - String to output */
532 {
533 char format[4]; /* C%s */
534
535 format[0] = *s++;
536 format[1] = '%';
537 format[2] = 's';
538 format[3] = '\0';
539
540 _cups_debug_printf(format, s);
541 }
542
543
544 #elif defined(__APPLE__)
545 /* Mac OS X needs these stubbed since we reference them in the libcups.exp file */
546 void _cups_debug_printf(const char *format, ...) {}
547 void _cups_debug_puts(const char *s) {}
548 #endif /* DEBUG */
549
550
551 /*
552 * End of "$Id$".
553 */