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