2003-01-29 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       const char *e = getenv ("GPGME_DEBUG");
88       const char *s1, *s2;;
89
90       initialized = 1;
91       errfp = stderr;
92       if (e)
93         {
94           debug_level = atoi (e);
95           s1 = strchr (e, ':');
96           if (s1)
97             {
98 #ifndef HAVE_DOSISH_SYSTEM
99               if (getuid () == geteuid ())
100                 {
101 #endif
102                   char *p;
103                   FILE *fp;
104
105                   s1++;
106                   if (!(s2 = strchr (s1, ':')))
107                     s2 = s1 + strlen (s1);
108                   p = malloc (s2 - s1 + 1);
109                   if (p)
110                     {
111                       memcpy (p, s1, s2 - s1);
112                       p[s2-s1] = 0;
113                       trim_spaces (p);
114                       fp = fopen (p,"a");
115                       if (fp)
116                         {
117                           setvbuf (fp, NULL, _IOLBF, 0);
118                           errfp = fp;
119                         }
120                       free (p);
121                     }
122 #ifndef HAVE_DOSISH_SYSTEM
123                 }
124 #endif
125             }
126         }
127
128       if (debug_level > 0)
129         fprintf (errfp, "gpgme_debug: level=%d\n", debug_level);
130     }
131   UNLOCK (debug_lock);
132 }
133
134 \f
135 /* Log the formatted string FORMAT at debug level LEVEL or higher.  */
136 void
137 _gpgme_debug (int level, const char *format, ...)
138 {
139   va_list arg_ptr;
140
141   debug_init ();
142   if (debug_level < level)
143     return;
144     
145   va_start (arg_ptr, format);
146   LOCK (debug_lock);
147   vfprintf (errfp, format, arg_ptr);
148   va_end (arg_ptr);
149   if(format && *format && format[strlen (format) - 1] != '\n')
150     putc ('\n', errfp);
151   UNLOCK (debug_lock);
152   fflush (errfp);
153 }
154
155
156 /* Start a new debug line in *LINE, logged at level LEVEL or higher,
157    and starting with the formatted string FORMAT.  */
158 void
159 _gpgme_debug_begin (void **line, int level, const char *format, ...)
160 {
161   va_list arg_ptr;
162
163   debug_init ();
164   if (debug_level < level)
165     {
166       /* Disable logging of this line.  */
167       *line = NULL;
168       return;
169     }
170
171   va_start (arg_ptr, format);
172   vasprintf ((char **) line, format, arg_ptr);
173   va_end (arg_ptr);
174 }
175
176
177 /* Add the formatted string FORMAT to the debug line *LINE.  */
178 void
179 _gpgme_debug_add (void **line, const char *format, ...)
180 {
181   va_list arg_ptr;
182   char *toadd;
183   char *result;
184
185   if (!*line)
186     return;
187
188   va_start (arg_ptr, format);
189   vasprintf (&toadd, format, arg_ptr);
190   va_end (arg_ptr);
191   asprintf (&result, "%s%s", *(char **) line, toadd);
192   free (*line);
193   free (toadd);
194   *line = result;
195 }
196
197
198 /* Finish construction of *LINE and send it to the debug output
199    stream.  */
200 void
201 _gpgme_debug_end (void **line)
202 {
203   if (!*line)
204     return;
205
206   /* The smallest possible level is 1, so force logging here by
207      using that.  */
208   _gpgme_debug (1, "%s", *line);
209   free (*line);
210   *line = NULL;
211 }