2004-06-23 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / debug.c
1 /* debug.c - helpful output in desperate situations
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 g10 Code GmbH
4  
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11  
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29 #include <ctype.h>
30 #ifndef HAVE_DOSISH_SYSTEM
31   #include <sys/types.h>
32   #include <sys/stat.h>
33   #include <fcntl.h>
34 #endif
35 #include <assert.h>
36
37 #include "util.h"
38 #include "sema.h"
39
40 \f
41 /* Lock to serialize initialization of the debug output subsystem and
42    output of actual debug messages.  */
43 DEFINE_STATIC_LOCK (debug_lock);
44
45 /* The amount of detail requested by the user, per environment
46    variable GPGME_DEBUG.  */
47 static int debug_level;
48
49 /* The output stream for the debug messages.  */
50 static FILE *errfp;
51
52 \f
53 /* Remove leading and trailing white spaces.  */
54 static char *
55 trim_spaces (char *str)
56 {
57   char *string, *p, *mark;
58
59   string = str;
60   /* Find first non space character.  */
61   for (p = string; *p && isspace (*(unsigned char *) p); p++)
62     ;
63   /* Move characters.  */
64   for (mark = NULL; (*string = *p); string++, p++)
65     if (isspace (*(unsigned char *) p))
66       {
67         if (!mark)
68           mark = string;
69       }
70     else
71       mark = NULL;
72   if (mark)
73     *mark = '\0';       /* Remove trailing spaces.  */
74
75   return str;
76 }
77
78
79 static void
80 debug_init (void)
81 {
82   static int initialized;
83
84   LOCK (debug_lock);
85   if (!initialized)
86     {
87       gpgme_error_t err;
88       char *e;
89       const char *s1, *s2;;
90
91       err = _gpgme_getenv ("GPGME_DEBUG", &e);
92       if (err)
93         {
94           UNLOCK (debug_lock);
95           return;
96         }
97
98       initialized = 1;
99       errfp = stderr;
100       if (e)
101         {
102           debug_level = atoi (e);
103           s1 = strchr (e, ':');
104           if (s1)
105             {
106 #ifndef HAVE_DOSISH_SYSTEM
107               if (getuid () == geteuid ())
108                 {
109 #endif
110                   char *p;
111                   FILE *fp;
112
113                   s1++;
114                   if (!(s2 = strchr (s1, ':')))
115                     s2 = s1 + strlen (s1);
116                   p = malloc (s2 - s1 + 1);
117                   if (p)
118                     {
119                       memcpy (p, s1, s2 - s1);
120                       p[s2-s1] = 0;
121                       trim_spaces (p);
122                       fp = fopen (p,"a");
123                       if (fp)
124                         {
125                           setvbuf (fp, NULL, _IOLBF, 0);
126                           errfp = fp;
127                         }
128                       free (p);
129                     }
130 #ifndef HAVE_DOSISH_SYSTEM
131                 }
132 #endif
133             }
134           free (e);
135         }
136
137       if (debug_level > 0)
138         fprintf (errfp, "gpgme_debug: level=%d\n", debug_level);
139     }
140   UNLOCK (debug_lock);
141 }
142
143 \f
144 /* Log the formatted string FORMAT at debug level LEVEL or higher.  */
145 void
146 _gpgme_debug (int level, const char *format, ...)
147 {
148   va_list arg_ptr;
149
150   debug_init ();
151   if (debug_level < level)
152     return;
153     
154   va_start (arg_ptr, format);
155   LOCK (debug_lock);
156   vfprintf (errfp, format, arg_ptr);
157   va_end (arg_ptr);
158   if(format && *format && format[strlen (format) - 1] != '\n')
159     putc ('\n', errfp);
160   UNLOCK (debug_lock);
161   fflush (errfp);
162 }
163
164
165 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
166    and starting with the formatted string FORMAT.  */
167 void
168 _gpgme_debug_begin (void **line, int level, const char *format, ...)
169 {
170   va_list arg_ptr;
171
172   debug_init ();
173   if (debug_level < level)
174     {
175       /* Disable logging of this line.  */
176       *line = NULL;
177       return;
178     }
179
180   va_start (arg_ptr, format);
181   vasprintf ((char **) line, format, arg_ptr);
182   va_end (arg_ptr);
183 }
184
185
186 /* Add the formatted string FORMAT to the debug line *LINE.  */
187 void
188 _gpgme_debug_add (void **line, const char *format, ...)
189 {
190   va_list arg_ptr;
191   char *toadd;
192   char *result;
193
194   if (!*line)
195     return;
196
197   va_start (arg_ptr, format);
198   vasprintf (&toadd, format, arg_ptr);
199   va_end (arg_ptr);
200   asprintf (&result, "%s%s", *(char **) line, toadd);
201   free (*line);
202   free (toadd);
203   *line = result;
204 }
205
206
207 /* Finish construction of *LINE and send it to the debug output
208    stream.  */
209 void
210 _gpgme_debug_end (void **line)
211 {
212   if (!*line)
213     return;
214
215   /* The smallest possible level is 1, so force logging here by
216      using that.  */
217   _gpgme_debug (1, "%s", *line);
218   free (*line);
219   *line = NULL;
220 }