Updates to OS/2 build:
[privoxy.git] / errlog.c
1 const char errlog_rcs[] = "$Id: errlog.c,v 1.34 2002/03/24 13:25:43 swa Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/errlog.c,v $
5  *
6  * Purpose     :  Log errors to a designated destination in an elegant,
7  *                printf-like fashion.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                Privoxy team.  http://ijbswa.sourceforge.net
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and 
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it 
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  * Revisions   :
35  *    $Log: errlog.c,v $
36  *    Revision 1.34  2002/03/24 13:25:43  swa
37  *    name change related issues
38  *
39  *    Revision 1.33  2002/03/13 00:27:04  jongfoster
40  *    Killing warnings
41  *
42  *    Revision 1.32  2002/03/07 03:46:17  oes
43  *    Fixed compiler warnings
44  *
45  *    Revision 1.31  2002/03/06 23:02:57  jongfoster
46  *    Removing tabs
47  *
48  *    Revision 1.30  2002/03/05 22:43:45  david__schmidt
49  *    - Better error reporting on OS/2
50  *    - Fix double-slash comment (oops)
51  *
52  *    Revision 1.29  2002/03/04 23:45:13  jongfoster
53  *    Printing thread ID if using Win32 native threads
54  *
55  *    Revision 1.28  2002/03/04 17:59:59  oes
56  *    Deleted deletePidFile(), cosmetics
57  *
58  *    Revision 1.27  2002/03/04 02:08:01  david__schmidt
59  *    Enable web editing of actions file on OS/2 (it had been broken all this time!)
60  *
61  *    Revision 1.26  2002/01/09 19:05:45  steudten
62  *    Fix big memory leak.
63  *
64  *    Revision 1.25  2002/01/09 14:32:08  oes
65  *    Added support for gmtime_r and localtime_r.
66  *
67  *    Revision 1.24  2001/12/30 14:07:32  steudten
68  *    - Add signal handling (unix)
69  *    - Add SIGHUP handler (unix)
70  *    - Add creation of pidfile (unix)
71  *    - Add action 'top' in rc file (RH)
72  *    - Add entry 'SIGNALS' to manpage
73  *    - Add exit message to logfile (unix)
74  *
75  *    Revision 1.23  2001/11/07 00:02:13  steudten
76  *    Add line number in error output for lineparsing for
77  *    actionsfile and configfile.
78  *    Special handling for CLF added.
79  *
80  *    Revision 1.22  2001/11/05 23:43:05  steudten
81  *    Add time+date to log files.
82  *
83  *    Revision 1.21  2001/10/25 03:40:47  david__schmidt
84  *    Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
85  *    threads to call select() simultaneously.  So, it's time to do a real, live,
86  *    native OS/2 port.  See defines for __EMX__ (the porting layer) vs. __OS2__
87  *    (native). Both versions will work, but using __OS2__ offers multi-threading.
88  *
89  *    Revision 1.20  2001/09/16 23:04:34  jongfoster
90  *    Fixing a warning
91  *
92  *    Revision 1.19  2001/09/13 20:08:06  jongfoster
93  *    Adding support for LOG_LEVEL_CGI
94  *
95  *    Revision 1.18  2001/09/10 11:27:24  oes
96  *    Declaration of w32_socket_strerr now conditional
97  *
98  *    Revision 1.17  2001/09/10 10:17:13  oes
99  *    Removed unused variable; Fixed sprintf format
100  *
101  *    Revision 1.16  2001/07/30 22:08:36  jongfoster
102  *    Tidying up #defines:
103  *    - All feature #defines are now of the form FEATURE_xxx
104  *    - Permanently turned off WIN_GUI_EDIT
105  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
106  *
107  *    Revision 1.15  2001/07/29 17:41:10  jongfoster
108  *    Now prints thread ID for each message (pthreads only)
109  *
110  *    Revision 1.14  2001/07/19 19:03:48  haroon
111  *    - Added case for LOG_LEVEL_POPUPS
112  *
113  *    Revision 1.13  2001/07/13 13:58:58  oes
114  *     - Added case for LOG_LEVEL_DEANIMATE
115  *     - Removed all #ifdef PCRS
116  *
117  *    Revision 1.12  2001/06/09 10:55:28  jongfoster
118  *    Changing BUFSIZ ==> BUFFER_SIZE
119  *
120  *    Revision 1.11  2001/06/01 18:14:49  jongfoster
121  *    Changing the calls to strerr() to check HAVE_STRERR (which is defined
122  *    in config.h if appropriate) rather than the NO_STRERR macro.
123  *
124  *    Revision 1.10  2001/05/29 11:52:21  oes
125  *    Conditional compilation of w32_socket_error
126  *
127  *    Revision 1.9  2001/05/28 16:15:17  jongfoster
128  *    Improved reporting of errors under Win32.
129  *
130  *    Revision 1.8  2001/05/26 17:25:14  jongfoster
131  *    Added support for CLF (Common Log Format) and fixed LOG_LEVEL_LOG
132  *
133  *    Revision 1.7  2001/05/26 15:21:28  jongfoster
134  *    Activity animation in Win32 GUI now works even if debug==0
135  *
136  *    Revision 1.6  2001/05/25 21:55:08  jongfoster
137  *    Now cleans up properly on FATAL (removes taskbar icon etc)
138  *
139  *    Revision 1.5  2001/05/22 18:46:04  oes
140  *
141  *    - Enabled filtering banners by size rather than URL
142  *      by adding patterns that replace all standard banner
143  *      sizes with the "Junkbuster" gif to the re_filterfile
144  *
145  *    - Enabled filtering WebBugs by providing a pattern
146  *      which kills all 1x1 images
147  *
148  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
149  *      which is selected by the (nonstandard and therefore
150  *      capital) letter 'U' in the option string.
151  *      It causes the quantifiers to be ungreedy by default.
152  *      Appending a ? turns back to greedy (!).
153  *
154  *    - Added a new interceptor ijb-send-banner, which
155  *      sends back the "Junkbuster" gif. Without imagelist or
156  *      MSIE detection support, or if tinygif = 1, or the
157  *      URL isn't recognized as an imageurl, a lame HTML
158  *      explanation is sent instead.
159  *
160  *    - Added new feature, which permits blocking remote
161  *      script redirects and firing back a local redirect
162  *      to the browser.
163  *      The feature is conditionally compiled, i.e. it
164  *      can be disabled with --disable-fast-redirects,
165  *      plus it must be activated by a "fast-redirects"
166  *      line in the config file, has its own log level
167  *      and of course wants to be displayed by show-proxy-args
168  *      Note: Boy, all the #ifdefs in 1001 locations and
169  *      all the fumbling with configure.in and acconfig.h
170  *      were *way* more work than the feature itself :-(
171  *
172  *    - Because a generic redirect template was needed for
173  *      this, tinygif = 3 now uses the same.
174  *
175  *    - Moved GIFs, and other static HTTP response templates
176  *      to project.h
177  *
178  *    - Some minor fixes
179  *
180  *    - Removed some >400 CRs again (Jon, you really worked
181  *      a lot! ;-)
182  *
183  *    Revision 1.4  2001/05/21 19:32:54  jongfoster
184  *    Added another #ifdef _WIN_CONSOLE
185  *
186  *    Revision 1.3  2001/05/20 01:11:40  jongfoster
187  *    Added support for LOG_LEVEL_FATAL
188  *    Renamed LOG_LEVEL_FRC to LOG_LEVEL_FORCE,
189  *    and LOG_LEVEL_REF to LOG_LEVEL_RE_FILTER
190  *
191  *    Revision 1.2  2001/05/17 22:42:01  oes
192  *     - Cleaned CRLF's from the sources and related files
193  *     - Repaired logging for REF and FRC
194  *
195  *    Revision 1.1.1.1  2001/05/15 13:58:51  oes
196  *    Initial import of version 2.9.3 source tree
197  *
198  *
199  *********************************************************************/
200 \f
201
202 #include "config.h"
203 #include "miscutil.h"
204
205 #include <stdlib.h>
206 #include <stdio.h>
207 #include <stdarg.h>
208 #include <string.h>
209
210 #if !defined(_WIN32) && !defined(__OS2__)
211 #include <unistd.h>
212 #endif /* !defined(_WIN32) && !defined(__OS2__) */
213
214 #include <errno.h>
215 #include <assert.h>
216 #ifdef FEATURE_PTHREAD
217 #include <pthread.h>
218 #endif /* def FEATURE_PTHREAD */
219
220 #ifdef _WIN32
221 #include <windows.h>
222 #ifndef _WIN_CONSOLE
223 #include "w32log.h"
224 #endif /* ndef _WIN_CONSOLE */
225 #endif /* def _WIN32 */
226
227 #ifdef __OS2__
228 #define INCL_DOS
229 #include <os2.h>
230 #endif
231
232 #include "errlog.h"
233 #include "project.h"
234 #include "jcc.h"
235
236 const char errlog_h_rcs[] = ERRLOG_H_VERSION;
237
238
239 /*
240  * LOG_LEVEL_FATAL cannot be turned off.  (There are
241  * some exceptional situations where we need to get a
242  * message to the user).
243  */
244 #define LOG_LEVEL_MINIMUM  LOG_LEVEL_FATAL
245
246 /* where to log (default: stderr) */
247 static FILE *logfp = NULL;
248
249 /* logging detail level.  */
250 static int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);  
251
252 /* static functions */
253 static void fatal_error(const char * error_message);
254 #ifdef _WIN32
255 static char *w32_socket_strerr(int errcode, char *tmp_buf);
256 #endif
257
258 /*********************************************************************
259  *
260  * Function    :  fatal_error
261  *
262  * Description :  Displays a fatal error to standard error (or, on 
263  *                a WIN32 GUI, to a dialog box), and exits
264  *                JunkBuster with status code 1.
265  *
266  * Parameters  :
267  *          1  :  error_message = The error message to display.
268  *
269  * Returns     :  Does not return.
270  *
271  *********************************************************************/
272 static void fatal_error(const char * error_message)
273 {
274 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
275    MessageBox(g_hwndLogFrame, error_message, "Privoxy Error", 
276       MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);  
277
278    /* Cleanup - remove taskbar icon etc. */
279    TermLogWindow();
280
281 #else /* if !defined(_WIN32) || defined(_WIN_CONSOLE) */
282    fputs(error_message, stderr);
283 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
284
285 #if defined(unix)
286    unlink(pidfile);
287 #endif /* unix */
288
289    exit(1);
290 }
291
292
293 /*********************************************************************
294  *
295  * Function    :  init_error_log
296  *
297  * Description :  Initializes the logging module.  Must call before
298  *                calling log_error.
299  *
300  * Parameters  :
301  *          1  :  prog_name  = The program name.
302  *          2  :  logfname   = The logfile name, or NULL for stderr.
303  *          3  :  debuglevel = The debugging level.
304  *
305  * Returns     :  N/A
306  *
307  *********************************************************************/
308 void init_error_log(const char *prog_name, const char *logfname, int debuglevel)
309 {
310    FILE *fp;
311
312    /* FIXME RACE HAZARD: should start critical section error_log_use here */
313
314    /* set the logging detail level */
315    debug = debuglevel | LOG_LEVEL_MINIMUM;
316
317    if ((logfp != NULL) && (logfp != stderr))
318    {
319       log_error(LOG_LEVEL_INFO, "(Re-)Open logfile %s", logfname ? logfname : "none");
320       fclose(logfp);
321    }
322    logfp = stderr;
323
324    /* set the designated log file */
325    if( logfname )
326    {
327       if( NULL == (fp = fopen(logfname, "a")) )
328       {
329          log_error(LOG_LEVEL_FATAL, "init_error_log(): can't open logfile: %s", logfname);
330       }
331
332       /* set logging to be completely unbuffered */
333       setbuf(fp, NULL);
334
335       logfp = fp;
336    }
337
338    log_error(LOG_LEVEL_INFO, "Privoxy version " VERSION);
339    if (prog_name != NULL)
340    {
341       log_error(LOG_LEVEL_INFO, "Program name: %s", prog_name);
342    }
343
344    /* FIXME RACE HAZARD: should end critical section error_log_use here */
345
346 } /* init_error_log */
347
348
349 /*********************************************************************
350  *
351  * Function    :  log_error
352  *
353  * Description :  This is the error-reporting and logging function.
354  *
355  * Parameters  :
356  *          1  :  loglevel  = the type of message to be logged
357  *          2  :  fmt       = the main string we want logged, printf-like
358  *          3  :  ...       = arguments to be inserted in fmt (printf-like).
359  *
360  * Returns     :  N/A
361  *
362  *********************************************************************/
363 void log_error(int loglevel, char *fmt, ...)
364 {
365    va_list ap;
366    char *outbuf= NULL;
367    static char *outbuf_save = NULL;
368    char * src = fmt;
369    int outc = 0;
370    long this_thread = 1;  /* was: pthread_t this_thread;*/
371 #ifdef __OS2__
372    PTIB     ptib;
373    APIRET   ulrc;
374 #endif /* __OS2__ */
375
376 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
377    /*
378     * Irrespective of debug setting, a GET/POST/CONNECT makes
379     * the taskbar icon animate.  (There is an option to disable
380     * this but checking that is handled inside LogShowActivity()).
381     */
382    if (loglevel == LOG_LEVEL_GPC)
383    {
384       LogShowActivity();
385    }
386 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
387
388    /* verify if loglevel applies to current settings and bail out if negative */
389    if ((loglevel & debug) == 0)
390    {
391       return;
392    }
393
394    /* FIXME get current thread id */
395 #ifdef FEATURE_PTHREAD
396    this_thread = (long)pthread_self();
397 #elif defined(_WIN32)
398    this_thread = GetCurrentThreadId();
399 #elif defined(__OS2__)
400    ulrc = DosGetInfoBlocks(&ptib, NULL);
401    if (ulrc == 0)
402      this_thread = ptib -> tib_ptib2 -> tib2_ultid;
403 #endif /* def FEATURE_PTHREAD */
404
405    if ( !outbuf_save ) 
406    {
407       outbuf_save = outbuf = (char*)malloc(BUFFER_SIZE);
408       assert(outbuf);
409    }
410    outbuf = outbuf_save;
411
412     {
413        /*
414         * Write timestamp into tempbuf.
415         *
416         * Complex because not all OSs have tm_gmtoff or
417         * the %z field in strftime()
418         */
419        time_t now; 
420        struct tm tm_now; 
421        time (&now);
422 #ifdef HAVE_LOCALTIME_R
423        tm_now = *localtime_r(&now, &tm_now);
424 #else
425        tm_now = *localtime (&now); 
426 #endif
427        strftime(outbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); 
428        outbuf += strlen( outbuf );
429     }
430    switch (loglevel)
431    {
432       case LOG_LEVEL_ERROR:
433          outc = sprintf(outbuf, "Privoxy(%ld) Error: ", this_thread);
434          break;
435       case LOG_LEVEL_FATAL:
436          outc = sprintf(outbuf, "Privoxy(%ld) Fatal error: ", this_thread);
437          break;
438       case LOG_LEVEL_GPC:
439          outc = sprintf(outbuf, "Privoxy(%ld) Request: ", this_thread);
440          break;
441       case LOG_LEVEL_CONNECT:
442          outc = sprintf(outbuf, "Privoxy(%ld) Connect: ", this_thread);
443          break;
444       case LOG_LEVEL_LOG:
445          outc = sprintf(outbuf, "Privoxy(%ld) Writing: ", this_thread);
446          break;
447       case LOG_LEVEL_HEADER:
448          outc = sprintf(outbuf, "Privoxy(%ld) Header: ", this_thread);
449          break;
450       case LOG_LEVEL_INFO:
451          outc = sprintf(outbuf, "Privoxy(%ld) Info: ", this_thread);
452          break;
453       case LOG_LEVEL_RE_FILTER:
454          outc = sprintf(outbuf, "Privoxy(%ld) Re-Filter: ", this_thread);
455          break;
456 #ifdef FEATURE_FORCE_LOAD
457       case LOG_LEVEL_FORCE:
458          outc = sprintf(outbuf, "Privoxy(%ld) Force: ", this_thread);
459          break;
460 #endif /* def FEATURE_FORCE_LOAD */
461 #ifdef FEATURE_FAST_REDIRECTS
462       case LOG_LEVEL_REDIRECTS:
463          outc = sprintf(outbuf, "Privoxy(%ld) Redirect: ", this_thread);
464          break;
465 #endif /* def FEATURE_FAST_REDIRECTS */
466       case LOG_LEVEL_DEANIMATE:
467          outc = sprintf(outbuf, "Privoxy(%ld) Gif-Deanimate: ", this_thread);
468          break;
469       case LOG_LEVEL_CLF:
470          outbuf = outbuf_save;
471          outc = 0;
472          outbuf[0] = '\0';
473          break;
474 #ifdef FEATURE_KILL_POPUPS
475       case LOG_LEVEL_POPUPS:
476          outc = sprintf(outbuf, "Privoxy(%ld) Kill-Popups: ", this_thread);
477          break;
478 #endif /* def FEATURE_KILL_POPUPS */
479       case LOG_LEVEL_CGI:
480          outc = sprintf(outbuf, "Privoxy(%ld) CGI: ", this_thread);
481          break;
482       default:
483          outc = sprintf(outbuf, "Privoxy(%ld) UNKNOWN LOG TYPE(%d): ", this_thread, loglevel);
484          break;
485    }
486    
487    /* get ready to scan var. args. */
488    va_start( ap, fmt );
489
490    /* build formatted message from fmt and var-args */
491    while ((*src) && (outc < BUFFER_SIZE-2))
492    {
493       char tempbuf[BUFFER_SIZE];
494       char *sval = NULL;
495       int ival;
496       unsigned uval;
497       long lval;
498       unsigned long ulval;
499       int oldoutc;
500       char ch;
501       
502       ch = *src++;
503       if( ch != '%' )
504       {
505          outbuf[outc++] = ch;
506          continue;
507       }
508
509       ch = *src++;
510       switch (ch) {
511          case '%':
512             outbuf[outc++] = '%';
513             break;
514          case 'd':
515             ival = va_arg( ap, int );
516             oldoutc = outc;
517             outc += sprintf(tempbuf, "%d", ival);
518             if (outc < BUFFER_SIZE-1) 
519             {
520                strcpy(outbuf + oldoutc, tempbuf);
521             }
522             else
523             {
524                outbuf[oldoutc] = '\0';
525             }
526             break;
527          case 'u':
528             uval = va_arg( ap, unsigned );
529             oldoutc = outc;
530             outc += sprintf(tempbuf, "%u", uval);
531             if (outc < BUFFER_SIZE-1) 
532             {
533                strcpy(outbuf + oldoutc, tempbuf);
534             }
535             else
536             {
537                outbuf[oldoutc] = '\0';
538             }
539             break;
540          case 'l':
541             /* this is a modifier that must be followed by u or d */
542             ch = *src++;
543             if (ch == 'd')
544             {
545                lval = va_arg( ap, long );
546                oldoutc = outc;
547                outc += sprintf(tempbuf, "%ld", lval);
548             }
549             else if (ch == 'u')
550             {
551                ulval = va_arg( ap, unsigned long );
552                oldoutc = outc;
553                outc += sprintf(tempbuf, "%lu", ulval);
554             }
555             else
556             {
557                /* Error */
558                sprintf(outbuf, "Privoxy(%ld) Error: log_error(): Bad format string:\n"
559                                "Format = \"%s\"\n"
560                                "Exiting.", this_thread, fmt);
561                /* FIXME RACE HAZARD: should start critical section error_log_use here */
562                if( !logfp )
563                {
564                   logfp = stderr;
565                }
566                fputs(outbuf, logfp);
567                /* FIXME RACE HAZARD: should end critical section error_log_use here */
568                fatal_error(outbuf);
569                /* Never get here */
570                break;
571             }
572             if (outc < BUFFER_SIZE-1) 
573             {
574                strcpy(outbuf + oldoutc, tempbuf);
575             }
576             else
577             {
578                outbuf[oldoutc] = '\0';
579             }
580             break;
581          case 'c':
582             /*
583              * Note that char paramaters are converted to int, so we need to
584              * pass "int" to va_arg.  (See K&R, 2nd ed, section A7.3.2, page 202)
585              */
586             outbuf[outc++] = (char) va_arg( ap, int );
587             break;
588          case 's':
589             sval = va_arg( ap, char * );
590             oldoutc = outc;
591             outc += strlen(sval);
592             if (outc < BUFFER_SIZE-1) 
593             {
594                strcpy(outbuf + oldoutc, sval);
595             }
596             else
597             {
598                outbuf[oldoutc] = '\0';
599             }
600             break;
601          case 'N':
602             /* Non-standard: Print a counted string.  Takes 2 parameters:
603              * int length, const char * string
604              */
605             ival = va_arg( ap, int );
606             sval = va_arg( ap, char * );
607             if (ival < 0)
608             {
609                ival = 0;
610             }
611             oldoutc = outc;
612             outc += ival;
613             if (outc < BUFFER_SIZE-1)
614             {
615                memcpy(outbuf + oldoutc, sval, (size_t) ival);
616             }
617             else
618             {
619                outbuf[oldoutc] = '\0';
620             }
621             break;
622          case 'E':
623             /* Non-standard: Print error code from errno */
624 #ifdef _WIN32
625             ival = WSAGetLastError();
626             sval = w32_socket_strerr(ival, tempbuf);
627 #elif __OS2__
628             ival = sock_errno();
629             if (ival == 0)
630               ival = errno;
631             sval = strerror(ival);
632 #else /* ifndef _WIN32 */
633             ival = errno; 
634 #ifdef HAVE_STRERROR
635             sval = strerror(ival);
636 #else /* ifndef HAVE_STRERROR */
637             sval = NULL;
638 #endif /* ndef HAVE_STRERROR */
639             if (sval == NULL)
640             {
641                sprintf(tempbuf, "(errno = %d)", ival);
642                sval = tempbuf;
643             }
644 #endif /* ndef _WIN32 */
645             oldoutc = outc;
646             outc += strlen(sval);
647             if (outc < BUFFER_SIZE-1) 
648             {
649                strcpy(outbuf + oldoutc, sval);
650             }
651             else
652             {
653                outbuf[oldoutc] = '\0';
654             }
655             break;
656          case 'T':
657             /* Non-standard: Print a Common Log File timestamp */
658             {
659                /*
660                 * Write timestamp into tempbuf.
661                 *
662                 * Complex because not all OSs have tm_gmtoff or
663                 * the %z field in strftime()
664                 */
665                time_t now; 
666                struct tm *tm_now; 
667                struct tm gmt;
668 #ifdef HAVE_LOCALTIME_R
669                struct tm dummy;
670 #endif
671                int days, hrs, mins; 
672                time (&now); 
673 #ifdef HAVE_GMTIME_R
674                gmt = *gmtime_r(&now, &gmt);
675 #else
676                gmt = *gmtime(&now);
677 #endif
678 #ifdef HAVE_LOCALTIME_R
679                tm_now = localtime_r(&now, &dummy);
680 #else
681                tm_now = localtime (&now); 
682 #endif
683                days = tm_now->tm_yday - gmt.tm_yday; 
684                hrs = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + tm_now->tm_hour - gmt.tm_hour); 
685                mins = hrs * 60 + tm_now->tm_min - gmt.tm_min; 
686                strftime (tempbuf, BUFFER_SIZE-6, "%d/%b/%Y:%H:%M:%S ", tm_now); 
687                sprintf (tempbuf + strlen(tempbuf), "%+03d%02d", mins / 60, abs(mins) % 60); 
688             }
689             oldoutc = outc;
690             outc += strlen(tempbuf);
691             if (outc < BUFFER_SIZE-1) 
692             {
693                strcpy(outbuf + oldoutc, tempbuf);
694             }
695             else
696             {
697                outbuf[oldoutc] = '\0';
698             }
699             break;
700          default:
701             sprintf(outbuf, "Privoxy(%ld) Error: log_error(): Bad format string:\n"
702                             "Format = \"%s\"\n"
703                             "Exiting.", this_thread, fmt);
704             /* FIXME RACE HAZARD: should start critical section error_log_use here */
705             if( !logfp )
706             {
707                logfp = stderr;
708             }
709             fputs(outbuf_save, logfp);
710             /* FIXME RACE HAZARD: should end critical section error_log_use here */
711             fatal_error(outbuf_save);
712             /* Never get here */
713             break;
714
715       } /* switch( p ) */
716
717    } /* for( p ... ) */
718    
719    /* done with var. args */
720    va_end( ap );
721    
722    if (outc >= BUFFER_SIZE-2)
723    {
724       /* insufficient room for newline and trailing null. */
725
726       static const char warning[] = "... [too long, truncated]\n";
727
728       if (outc < BUFFER_SIZE)
729       {
730          /* Need to add terminating null in this case. */
731          outbuf[outc] = '\0';
732       }
733
734       /* Truncate output */
735       outbuf[BUFFER_SIZE - sizeof(warning)] = '\0';
736
737       /* Append warning */
738       strcat(outbuf, warning);
739    }
740    else
741    {
742       /* Add terminating newline and null */
743       outbuf[outc++] = '\n';
744       outbuf[outc] = '\0';
745    }
746
747    /* FIXME RACE HAZARD: should start critical section error_log_use here */
748
749    /* deal with glibc stupidity - it won't let you initialize logfp */
750    if( !logfp )
751    {
752       logfp = stderr;
753    }
754
755    fputs(outbuf_save, logfp);
756
757    if (loglevel == LOG_LEVEL_FATAL)
758    {
759       fatal_error(outbuf_save);
760       /* Never get here */
761    }
762
763    /* FIXME RACE HAZARD: should end critical section error_log_use here */
764
765 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
766    /* Write to display */
767    LogPutString(outbuf_save);
768 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
769
770 }
771
772
773 #ifdef _WIN32
774 /*********************************************************************
775  *
776  * Function    :  w32_socket_strerr
777  *
778  * Description :  Translate the return value from WSAGetLastError()
779  *                into a string.
780  *
781  * Parameters  :
782  *          1  :  errcode = The return value from WSAGetLastError().
783  *          2  :  tmp_buf = A temporary buffer that might be used to
784  *                          store the string.
785  *
786  * Returns     :  String representing the error code.  This may be
787  *                a global string constant or a string stored in
788  *                tmp_buf.
789  *
790  *********************************************************************/
791 static char *w32_socket_strerr(int errcode, char *tmp_buf)
792 {
793 #define TEXT_FOR_ERROR(code,text) \
794    if (errcode == code)           \
795    {                              \
796       return #code " - " text;    \
797    }
798
799    TEXT_FOR_ERROR(WSAEACCES, "Permission denied")
800    TEXT_FOR_ERROR(WSAEADDRINUSE, "Address already in use.")
801    TEXT_FOR_ERROR(WSAEADDRNOTAVAIL, "Cannot assign requested address.");
802    TEXT_FOR_ERROR(WSAEAFNOSUPPORT, "Address family not supported by protocol family.");
803    TEXT_FOR_ERROR(WSAEALREADY, "Operation already in progress.");
804    TEXT_FOR_ERROR(WSAECONNABORTED, "Software caused connection abort.");
805    TEXT_FOR_ERROR(WSAECONNREFUSED, "Connection refused.");
806    TEXT_FOR_ERROR(WSAECONNRESET, "Connection reset by peer.");
807    TEXT_FOR_ERROR(WSAEDESTADDRREQ, "Destination address required.");
808    TEXT_FOR_ERROR(WSAEFAULT, "Bad address.");
809    TEXT_FOR_ERROR(WSAEHOSTDOWN, "Host is down.");
810    TEXT_FOR_ERROR(WSAEHOSTUNREACH, "No route to host.");
811    TEXT_FOR_ERROR(WSAEINPROGRESS, "Operation now in progress.");
812    TEXT_FOR_ERROR(WSAEINTR, "Interrupted function call.");
813    TEXT_FOR_ERROR(WSAEINVAL, "Invalid argument.");
814    TEXT_FOR_ERROR(WSAEISCONN, "Socket is already connected.");
815    TEXT_FOR_ERROR(WSAEMFILE, "Too many open sockets.");
816    TEXT_FOR_ERROR(WSAEMSGSIZE, "Message too long.");
817    TEXT_FOR_ERROR(WSAENETDOWN, "Network is down.");
818    TEXT_FOR_ERROR(WSAENETRESET, "Network dropped connection on reset.");
819    TEXT_FOR_ERROR(WSAENETUNREACH, "Network is unreachable.");
820    TEXT_FOR_ERROR(WSAENOBUFS, "No buffer space available.");
821    TEXT_FOR_ERROR(WSAENOPROTOOPT, "Bad protocol option.");
822    TEXT_FOR_ERROR(WSAENOTCONN, "Socket is not connected.");
823    TEXT_FOR_ERROR(WSAENOTSOCK, "Socket operation on non-socket.");
824    TEXT_FOR_ERROR(WSAEOPNOTSUPP, "Operation not supported.");
825    TEXT_FOR_ERROR(WSAEPFNOSUPPORT, "Protocol family not supported.");
826    TEXT_FOR_ERROR(WSAEPROCLIM, "Too many processes.");
827    TEXT_FOR_ERROR(WSAEPROTONOSUPPORT, "Protocol not supported.");
828    TEXT_FOR_ERROR(WSAEPROTOTYPE, "Protocol wrong type for socket.");
829    TEXT_FOR_ERROR(WSAESHUTDOWN, "Cannot send after socket shutdown.");
830    TEXT_FOR_ERROR(WSAESOCKTNOSUPPORT, "Socket type not supported.");
831    TEXT_FOR_ERROR(WSAETIMEDOUT, "Connection timed out.");
832    TEXT_FOR_ERROR(WSAEWOULDBLOCK, "Resource temporarily unavailable.");
833    TEXT_FOR_ERROR(WSAHOST_NOT_FOUND, "Host not found.");
834    TEXT_FOR_ERROR(WSANOTINITIALISED, "Successful WSAStartup not yet performed.");
835    TEXT_FOR_ERROR(WSANO_DATA, "Valid name, no data record of requested type.");
836    TEXT_FOR_ERROR(WSANO_RECOVERY, "This is a non-recoverable error.");
837    TEXT_FOR_ERROR(WSASYSNOTREADY, "Network subsystem is unavailable.");
838    TEXT_FOR_ERROR(WSATRY_AGAIN, "Non-authoritative host not found.");
839    TEXT_FOR_ERROR(WSAVERNOTSUPPORTED, "WINSOCK.DLL version out of range.");
840    TEXT_FOR_ERROR(WSAEDISCON, "Graceful shutdown in progress.");
841    /*
842     * The following error codes are documented in the Microsoft WinSock
843     * reference guide, but don't actually exist.
844     *
845     * TEXT_FOR_ERROR(WSA_INVALID_HANDLE, "Specified event object handle is invalid.");
846     * TEXT_FOR_ERROR(WSA_INVALID_PARAMETER, "One or more parameters are invalid.");
847     * TEXT_FOR_ERROR(WSAINVALIDPROCTABLE, "Invalid procedure table from service provider.");
848     * TEXT_FOR_ERROR(WSAINVALIDPROVIDER, "Invalid service provider version number.");
849     * TEXT_FOR_ERROR(WSA_IO_PENDING, "Overlapped operations will complete later.");
850     * TEXT_FOR_ERROR(WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state.");
851     * TEXT_FOR_ERROR(WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available.");
852     * TEXT_FOR_ERROR(WSAPROVIDERFAILEDINIT, "Unable to initialize a service provider.");
853     * TEXT_FOR_ERROR(WSASYSCALLFAILURE, "System call failure.");
854     * TEXT_FOR_ERROR(WSA_OPERATION_ABORTED, "Overlapped operation aborted.");
855     */
856
857    sprintf(tmp_buf, "(error number %d)", errcode);
858    return tmp_buf;
859 }
860 #endif /* def _WIN32 */
861
862
863 /*
864   Local Variables:
865   tab-width: 3
866   end:
867 */