Ported error output to CGI platform
[privoxy.git] / errlog.c
1 const char errlog_rcs[] = "$Id: errlog.c,v 1.11 2001/06/01 18:14:49 jongfoster 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  *                IJBSWA 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.11  2001/06/01 18:14:49  jongfoster
37  *    Changing the calls to strerr() to check HAVE_STRERR (which is defined
38  *    in config.h if appropriate) rather than the NO_STRERR macro.
39  *
40  *    Revision 1.10  2001/05/29 11:52:21  oes
41  *    Conditional compilation of w32_socket_error
42  *
43  *    Revision 1.9  2001/05/28 16:15:17  jongfoster
44  *    Improved reporting of errors under Win32.
45  *
46  *    Revision 1.8  2001/05/26 17:25:14  jongfoster
47  *    Added support for CLF (Common Log Format) and fixed LOG_LEVEL_LOG
48  *
49  *    Revision 1.7  2001/05/26 15:21:28  jongfoster
50  *    Activity animation in Win32 GUI now works even if debug==0
51  *
52  *    Revision 1.6  2001/05/25 21:55:08  jongfoster
53  *    Now cleans up properly on FATAL (removes taskbar icon etc)
54  *
55  *    Revision 1.5  2001/05/22 18:46:04  oes
56  *
57  *    - Enabled filtering banners by size rather than URL
58  *      by adding patterns that replace all standard banner
59  *      sizes with the "Junkbuster" gif to the re_filterfile
60  *
61  *    - Enabled filtering WebBugs by providing a pattern
62  *      which kills all 1x1 images
63  *
64  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
65  *      which is selected by the (nonstandard and therefore
66  *      capital) letter 'U' in the option string.
67  *      It causes the quantifiers to be ungreedy by default.
68  *      Appending a ? turns back to greedy (!).
69  *
70  *    - Added a new interceptor ijb-send-banner, which
71  *      sends back the "Junkbuster" gif. Without imagelist or
72  *      MSIE detection support, or if tinygif = 1, or the
73  *      URL isn't recognized as an imageurl, a lame HTML
74  *      explanation is sent instead.
75  *
76  *    - Added new feature, which permits blocking remote
77  *      script redirects and firing back a local redirect
78  *      to the browser.
79  *      The feature is conditionally compiled, i.e. it
80  *      can be disabled with --disable-fast-redirects,
81  *      plus it must be activated by a "fast-redirects"
82  *      line in the config file, has its own log level
83  *      and of course wants to be displayed by show-proxy-args
84  *      Note: Boy, all the #ifdefs in 1001 locations and
85  *      all the fumbling with configure.in and acconfig.h
86  *      were *way* more work than the feature itself :-(
87  *
88  *    - Because a generic redirect template was needed for
89  *      this, tinygif = 3 now uses the same.
90  *
91  *    - Moved GIFs, and other static HTTP response templates
92  *      to project.h
93  *
94  *    - Some minor fixes
95  *
96  *    - Removed some >400 CRs again (Jon, you really worked
97  *      a lot! ;-)
98  *
99  *    Revision 1.4  2001/05/21 19:32:54  jongfoster
100  *    Added another #ifdef _WIN_CONSOLE
101  *
102  *    Revision 1.3  2001/05/20 01:11:40  jongfoster
103  *    Added support for LOG_LEVEL_FATAL
104  *    Renamed LOG_LEVEL_FRC to LOG_LEVEL_FORCE,
105  *    and LOG_LEVEL_REF to LOG_LEVEL_RE_FILTER
106  *
107  *    Revision 1.2  2001/05/17 22:42:01  oes
108  *     - Cleaned CRLF's from the sources and related files
109  *     - Repaired logging for REF and FRC
110  *
111  *    Revision 1.1.1.1  2001/05/15 13:58:51  oes
112  *    Initial import of version 2.9.3 source tree
113  *
114  *
115  *********************************************************************/
116 \f
117
118 #include "config.h"
119
120 #include <stdlib.h>
121 #include <stdio.h>
122 #include <stdarg.h>
123 #include <string.h>
124
125 #ifndef _WIN32
126 #include <unistd.h>
127 #endif /* ndef _WIN32 */
128
129 #include <errno.h>
130 /* #include <pthread.h> */
131
132 #ifdef _WIN32
133 #include <windows.h>
134 #ifndef _WIN_CONSOLE
135 #include "w32log.h"
136 #endif /* ndef _WIN_CONSOLE */
137 #endif /* def _WIN32 */
138
139 #include "errlog.h"
140 #include "project.h"
141
142 const char errlog_h_rcs[] = ERRLOG_H_VERSION;
143
144
145 /*
146  * LOG_LEVEL_FATAL cannot be turned off.  (There are
147  * some exceptional situations where we need to get a
148  * message to the user).
149  */
150 #define LOG_LEVEL_MINIMUM  LOG_LEVEL_FATAL
151
152 /* where to log (default: stderr) */
153 static FILE *logfp = NULL;
154
155 /* where to log (NULL == stderr) */
156 static char * logfilename = NULL;
157
158 /* logging detail level.  */
159 static int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);  
160
161 /* static functions */
162 static void fatal_error(const char * error_message);
163 static char * w32_socket_strerr(int errcode, char * tmp_buf);
164
165
166 /*********************************************************************
167  *
168  * Function    :  fatal_error
169  *
170  * Description :  Displays a fatal error to standard error (or, on 
171  *                a WIN32 GUI, to a dialog box), and exits
172  *                JunkBuster with status code 1.
173  *
174  * Parameters  :
175  *          1  :  error_message = The error message to display.
176  *
177  * Returns     :  Does not return.
178  *
179  *********************************************************************/
180 static void fatal_error(const char * error_message)
181 {
182 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
183    MessageBox(g_hwndLogFrame, error_message, "Internet JunkBuster Error", 
184       MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);  
185
186    /* Cleanup - remove taskbar icon etc. */
187    TermLogWindow();
188
189 #else /* if !defined(_WIN32) || defined(_WIN_CONSOLE) */
190    fputs(error_message, stderr);
191 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
192
193    exit(1);
194 }
195
196
197 /*********************************************************************
198  *
199  * Function    :  init_errlog
200  *
201  * Description :  Initializes the logging module.  Must call before
202  *                calling log_error.
203  *
204  * Parameters  :
205  *          1  :  prog_name  = The program name.
206  *          2  :  logfname   = The logfile name, or NULL for stderr.
207  *          3  :  debuglevel = The debugging level.
208  *
209  * Returns     :  N/A
210  *
211  *********************************************************************/
212 void init_error_log(const char *prog_name, const char *logfname, int debuglevel)
213 {
214    FILE *fp;
215
216    /* FIXME RACE HAZARD: should start critical section error_log_use here */
217
218    /* set the logging detail level */
219    debug = debuglevel | LOG_LEVEL_MINIMUM;
220
221    if ((logfp != NULL) && (logfp != stderr))
222    {
223       fclose(logfp);
224    }
225    logfp = stderr;
226
227    /* set the designated log file */
228    if( logfname )
229    {
230       if( !(fp = fopen(logfname, "a")) )
231       {
232          log_error(LOG_LEVEL_FATAL, "init_errlog(): can't open logfile: %s", logfname);
233       }
234
235       /* set logging to be completely unbuffered */
236       setbuf(fp, NULL);
237
238       logfp = fp;
239    }
240
241    log_error(LOG_LEVEL_INFO, "Internet JunkBuster version " VERSION);
242    if (prog_name != NULL)
243    {
244       log_error(LOG_LEVEL_INFO, "Program name: %s", prog_name);
245    }
246
247    /* FIXME RACE HAZARD: should end critical section error_log_use here */
248
249 } /* init_error_log */
250
251
252 /*********************************************************************
253  *
254  * Function    :  log_error
255  *
256  * Description :  This is the error-reporting and logging function.
257  *
258  * Parameters  :
259  *          1  :  loglevel  = the type of message to be logged
260  *          2  :  fmt       = the main string we want logged, printf-like
261  *          3  :  ...       = arguments to be inserted in fmt (printf-like).
262  *
263  * Returns     :  N/A
264  *
265  *********************************************************************/
266 void log_error(int loglevel, char *fmt, ...)
267 {
268    va_list ap;
269    char outbuf[BUFFER_SIZE];
270    char * src = fmt;
271    int outc = 0;
272    long this_thread = 1;  /* was: pthread_t this_thread;*/
273
274 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
275    /*
276     * Irrespective of debug setting, a GET/POST/CONNECT makes
277     * the taskbar icon animate.  (There is an option to disable
278     * this but checking that is handled inside LogShowActivity()).
279     */
280    if (loglevel == LOG_LEVEL_GPC)
281    {
282       LogShowActivity();
283    }
284 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
285
286    /* verify if loglevel applies to current settings and bail out if negative */
287    if ((loglevel & debug) == 0)
288    {
289       return;
290    }
291
292    /* FIXME get current thread id */
293    /* this_thread = (long)pthread_self(); */
294
295    switch (loglevel)
296    {
297       case LOG_LEVEL_ERROR:
298          outc = sprintf(outbuf, "IJB(%d) Error: ", this_thread);
299          break;
300       case LOG_LEVEL_FATAL:
301          outc = sprintf(outbuf, "IJB(%d) Fatal error: ", this_thread);
302          break;
303       case LOG_LEVEL_GPC:
304          outc = sprintf(outbuf, "IJB(%d) Request: ", this_thread);
305          break;
306       case LOG_LEVEL_CONNECT:
307          outc = sprintf(outbuf, "IJB(%d) Connect: ", this_thread);
308          break;
309       case LOG_LEVEL_LOG:
310          outc = sprintf(outbuf, "IJB(%d) Writing: ", this_thread);
311          break;
312       case LOG_LEVEL_HEADER:
313          outc = sprintf(outbuf, "IJB(%d) Header: ", this_thread);
314          break;
315       case LOG_LEVEL_INFO:
316          outc = sprintf(outbuf, "IJB(%d) Info: ", this_thread);
317          break;
318 #ifdef PCRS
319       case LOG_LEVEL_RE_FILTER:
320          outc = sprintf(outbuf, "IJB(%d) Re-Filter: ", this_thread);
321          break;
322 #endif /* def PCRS */
323 #ifdef FORCE_LOAD
324       case LOG_LEVEL_FORCE:
325          outc = sprintf(outbuf, "IJB(%d) Force: ", this_thread);
326          break;
327 #endif /* def FORCE_LOAD */
328 #ifdef FAST_REDIRECTS
329       case LOG_LEVEL_REDIRECTS:
330          outc = sprintf(outbuf, "IJB(%d) Redirect: ", this_thread);
331          break;
332 #endif /* def FAST_REDIRECTS */
333       case LOG_LEVEL_CLF:
334          outc = 0;
335          outbuf[0] = '\0';
336          break;
337       default:
338          outc = sprintf(outbuf, "IJB(%d) UNKNOWN LOG TYPE(%d): ", this_thread, loglevel);
339          break;
340    }
341    
342    /* get ready to scan var. args. */
343    va_start( ap, fmt );
344
345    /* build formatted message from fmt and var-args */
346    while ((*src) && (outc < BUFFER_SIZE-2))
347    {
348       char tempbuf[BUFFER_SIZE];
349       char *sval;
350       int ival;
351       unsigned uval;
352       long lval;
353       unsigned long ulval;
354       int oldoutc;
355       char ch;
356       
357       ch = *src++;
358       if( ch != '%' )
359       {
360          outbuf[outc++] = ch;
361          continue;
362       }
363
364       ch = *src++;
365       switch (ch) {
366          case '%':
367             outbuf[outc++] = '%';
368             break;
369          case 'd':
370             ival = va_arg( ap, int );
371             oldoutc = outc;
372             outc += sprintf(tempbuf, "%d", ival);
373             if (outc < BUFFER_SIZE-1) 
374             {
375                strcpy(outbuf + oldoutc, tempbuf);
376             }
377             else
378             {
379                outbuf[oldoutc] = '\0';
380             }
381             break;
382          case 'u':
383             uval = va_arg( ap, unsigned );
384             oldoutc = outc;
385             outc += sprintf(tempbuf, "%u", uval);
386             if (outc < BUFFER_SIZE-1) 
387             {
388                strcpy(outbuf + oldoutc, tempbuf);
389             }
390             else
391             {
392                outbuf[oldoutc] = '\0';
393             }
394             break;
395          case 'l':
396             /* this is a modifier that must be followed by u or d */
397             ch = *src++;
398             if (ch == 'd')
399             {
400                lval = va_arg( ap, long );
401                oldoutc = outc;
402                outc += sprintf(tempbuf, "%ld", lval);
403             }
404             else if (ch == 'u')
405             {
406                ulval = va_arg( ap, unsigned long );
407                oldoutc = outc;
408                outc += sprintf(tempbuf, "%lu", ulval);
409             }
410             else
411             {
412                /* Error */
413                sprintf(outbuf, "IJB(%d) Error: log_error(): Bad format string:\n"
414                                "Format = \"%s\"\n"
415                                "Exiting.", this_thread, fmt);
416                /* FIXME RACE HAZARD: should start critical section error_log_use here */
417                if( !logfp )
418                {
419                   logfp = stderr;
420                }
421                fputs(outbuf, logfp);
422                /* FIXME RACE HAZARD: should end critical section error_log_use here */
423                fatal_error(outbuf);
424                /* Never get here */
425                break;
426             }
427             if (outc < BUFFER_SIZE-1) 
428             {
429                strcpy(outbuf + oldoutc, tempbuf);
430             }
431             else
432             {
433                outbuf[oldoutc] = '\0';
434             }
435             break;
436          case 'c':
437             /*
438              * Note that char paramaters are converted to int, so we need to
439              * pass "int" to va_arg.  (See K&R, 2nd ed, section A7.3.2, page 202)
440              */
441             outbuf[outc++] = (char) va_arg( ap, int );
442             break;
443          case 's':
444             sval = va_arg( ap, char * );
445             oldoutc = outc;
446             outc += strlen(sval);
447             if (outc < BUFFER_SIZE-1) 
448             {
449                strcpy(outbuf + oldoutc, sval);
450             }
451             else
452             {
453                outbuf[oldoutc] = '\0';
454             }
455             break;
456          case 'N':
457             /* Non-standard: Print a counted string.  Takes 2 parameters:
458              * int length, const char * string
459              */
460             ival = va_arg( ap, int );
461             sval = va_arg( ap, char * );
462             if (ival < 0)
463             {
464                ival = 0;
465             }
466             oldoutc = outc;
467             outc += ival;
468             if (outc < BUFFER_SIZE-1)
469             {
470                memcpy(outbuf + oldoutc, sval, ival);
471             }
472             else
473             {
474                outbuf[oldoutc] = '\0';
475             }
476             break;
477          case 'E':
478             /* Non-standard: Print error code from errno */
479 #ifdef _WIN32
480             ival = WSAGetLastError();
481             sval = w32_socket_strerr(ival, tempbuf);
482 #else /* ifndef _WIN32 */
483             ival = errno; 
484 #ifdef HAVE_STRERROR
485             sval = strerror(ival);
486 #else /* ifndef HAVE_STRERROR */
487             sval = NULL;
488 #endif /* ndef HAVE_STRERROR */
489             if (sval == NULL)
490             {
491                sprintf(tempbuf, "(errno = %d)", ival);
492                sval = tempbuf;
493             }
494 #endif /* ndef _WIN32 */
495             oldoutc = outc;
496             outc += strlen(sval);
497             if (outc < BUFFER_SIZE-1) 
498             {
499                strcpy(outbuf + oldoutc, sval);
500             }
501             else
502             {
503                outbuf[oldoutc] = '\0';
504             }
505             break;
506          case 'T':
507             /* Non-standard: Print a Common Log File timestamp */
508             {
509                /*
510                 * Write timestamp into tempbuf.
511                 *
512                 * Complex because not all OSs have tm_gmtoff or
513                 * the %z field in strftime()
514                 */
515                time_t now; 
516                struct tm *tm_now; 
517                struct tm gmt; 
518                int days, hrs, mins; 
519                time (&now); 
520                gmt = *gmtime (&now); 
521                tm_now = localtime (&now); 
522                days = tm_now->tm_yday - gmt.tm_yday; 
523                hrs = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + tm_now->tm_hour - gmt.tm_hour); 
524                mins = hrs * 60 + tm_now->tm_min - gmt.tm_min; 
525                strftime (tempbuf, BUFFER_SIZE-6, "%d/%b/%Y:%H:%M:%S ", tm_now); 
526                sprintf (tempbuf + strlen(tempbuf), "%+03d%02d", mins / 60, abs(mins) % 60); 
527             }
528             oldoutc = outc;
529             outc += strlen(tempbuf);
530             if (outc < BUFFER_SIZE-1) 
531             {
532                strcpy(outbuf + oldoutc, tempbuf);
533             }
534             else
535             {
536                outbuf[oldoutc] = '\0';
537             }
538             break;
539          default:
540             sprintf(outbuf, "IJB(%d) Error: log_error(): Bad format string:\n"
541                             "Format = \"%s\"\n"
542                             "Exiting.", this_thread, fmt);
543             /* FIXME RACE HAZARD: should start critical section error_log_use here */
544             if( !logfp )
545             {
546                logfp = stderr;
547             }
548             fputs(outbuf, logfp);
549             /* FIXME RACE HAZARD: should end critical section error_log_use here */
550             fatal_error(outbuf);
551             /* Never get here */
552             break;
553
554       } /* switch( p ) */
555
556    } /* for( p ... ) */
557    
558    /* done with var. args */
559    va_end( ap );
560    
561    if (outc >= BUFFER_SIZE-2)
562    {
563       /* insufficient room for newline and trailing null. */
564
565       static const char warning[] = "... [too long, truncated]\n";
566
567       if (outc < BUFFER_SIZE)
568       {
569          /* Need to add terminating null in this case. */
570          outbuf[outc] = '\0';
571       }
572
573       /* Truncate output */
574       outbuf[BUFFER_SIZE - sizeof(warning)] = '\0';
575
576       /* Append warning */
577       strcat(outbuf, warning);
578    }
579    else
580    {
581       /* Add terminating newline and null */
582       outbuf[outc++] = '\n';
583       outbuf[outc] = '\0';
584    }
585
586    /* FIXME RACE HAZARD: should start critical section error_log_use here */
587
588    /* deal with glibc stupidity - it won't let you initialize logfp */
589    if( !logfp )
590    {
591       logfp = stderr;
592    }
593
594    fputs(outbuf, logfp);
595
596    if (loglevel == LOG_LEVEL_FATAL)
597    {
598       fatal_error(outbuf);
599       /* Never get here */
600    }
601
602    /* FIXME RACE HAZARD: should end critical section error_log_use here */
603
604 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
605    /* Write to display */
606    LogPutString(outbuf);
607 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
608
609 }
610
611
612 #ifdef _WIN32
613 /*********************************************************************
614  *
615  * Function    :  w32_socket_strerr
616  *
617  * Description :  Translate the return value from WSAGetLastError()
618  *                into a string.
619  *
620  * Parameters  :
621  *          1  :  errcode = The return value from WSAGetLastError().
622  *          2  :  tmp_buf = A temporary buffer that might be used to
623  *                          store the string.
624  *
625  * Returns     :  String representing the error code.  This may be
626  *                a global string constant or a string stored in
627  *                tmp_buf.
628  *
629  *********************************************************************/
630 static char * w32_socket_strerr(int errcode, char * tmp_buf)
631 {
632 #define TEXT_FOR_ERROR(code,text) \
633    if (errcode == code)           \
634    {                              \
635       return #code " - " text;    \
636    }
637
638    TEXT_FOR_ERROR(WSAEACCES, "Permission denied")
639    TEXT_FOR_ERROR(WSAEADDRINUSE, "Address already in use.")
640    TEXT_FOR_ERROR(WSAEADDRNOTAVAIL, "Cannot assign requested address.");
641    TEXT_FOR_ERROR(WSAEAFNOSUPPORT, "Address family not supported by protocol family.");
642    TEXT_FOR_ERROR(WSAEALREADY, "Operation already in progress.");
643    TEXT_FOR_ERROR(WSAECONNABORTED, "Software caused connection abort.");
644    TEXT_FOR_ERROR(WSAECONNREFUSED, "Connection refused.");
645    TEXT_FOR_ERROR(WSAECONNRESET, "Connection reset by peer.");
646    TEXT_FOR_ERROR(WSAEDESTADDRREQ, "Destination address required.");
647    TEXT_FOR_ERROR(WSAEFAULT, "Bad address.");
648    TEXT_FOR_ERROR(WSAEHOSTDOWN, "Host is down.");
649    TEXT_FOR_ERROR(WSAEHOSTUNREACH, "No route to host.");
650    TEXT_FOR_ERROR(WSAEINPROGRESS, "Operation now in progress.");
651    TEXT_FOR_ERROR(WSAEINTR, "Interrupted function call.");
652    TEXT_FOR_ERROR(WSAEINVAL, "Invalid argument.");
653    TEXT_FOR_ERROR(WSAEISCONN, "Socket is already connected.");
654    TEXT_FOR_ERROR(WSAEMFILE, "Too many open sockets.");
655    TEXT_FOR_ERROR(WSAEMSGSIZE, "Message too long.");
656    TEXT_FOR_ERROR(WSAENETDOWN, "Network is down.");
657    TEXT_FOR_ERROR(WSAENETRESET, "Network dropped connection on reset.");
658    TEXT_FOR_ERROR(WSAENETUNREACH, "Network is unreachable.");
659    TEXT_FOR_ERROR(WSAENOBUFS, "No buffer space available.");
660    TEXT_FOR_ERROR(WSAENOPROTOOPT, "Bad protocol option.");
661    TEXT_FOR_ERROR(WSAENOTCONN, "Socket is not connected.");
662    TEXT_FOR_ERROR(WSAENOTSOCK, "Socket operation on non-socket.");
663    TEXT_FOR_ERROR(WSAEOPNOTSUPP, "Operation not supported.");
664    TEXT_FOR_ERROR(WSAEPFNOSUPPORT, "Protocol family not supported.");
665    TEXT_FOR_ERROR(WSAEPROCLIM, "Too many processes.");
666    TEXT_FOR_ERROR(WSAEPROTONOSUPPORT, "Protocol not supported.");
667    TEXT_FOR_ERROR(WSAEPROTOTYPE, "Protocol wrong type for socket.");
668    TEXT_FOR_ERROR(WSAESHUTDOWN, "Cannot send after socket shutdown.");
669    TEXT_FOR_ERROR(WSAESOCKTNOSUPPORT, "Socket type not supported.");
670    TEXT_FOR_ERROR(WSAETIMEDOUT, "Connection timed out.");
671    TEXT_FOR_ERROR(WSAEWOULDBLOCK, "Resource temporarily unavailable.");
672    TEXT_FOR_ERROR(WSAHOST_NOT_FOUND, "Host not found.");
673    TEXT_FOR_ERROR(WSANOTINITIALISED, "Successful WSAStartup not yet performed.");
674    TEXT_FOR_ERROR(WSANO_DATA, "Valid name, no data record of requested type.");
675    TEXT_FOR_ERROR(WSANO_RECOVERY, "This is a non-recoverable error.");
676    TEXT_FOR_ERROR(WSASYSNOTREADY, "Network subsystem is unavailable.");
677    TEXT_FOR_ERROR(WSATRY_AGAIN, "Non-authoritative host not found.");
678    TEXT_FOR_ERROR(WSAVERNOTSUPPORTED, "WINSOCK.DLL version out of range.");
679    TEXT_FOR_ERROR(WSAEDISCON, "Graceful shutdown in progress.");
680    /*
681     * The following error codes are documented in the Microsoft WinSock
682     * reference guide, but don't actually exist.
683     *
684     * TEXT_FOR_ERROR(WSA_INVALID_HANDLE, "Specified event object handle is invalid.");
685     * TEXT_FOR_ERROR(WSA_INVALID_PARAMETER, "One or more parameters are invalid.");
686     * TEXT_FOR_ERROR(WSAINVALIDPROCTABLE, "Invalid procedure table from service provider.");
687     * TEXT_FOR_ERROR(WSAINVALIDPROVIDER, "Invalid service provider version number.");
688     * TEXT_FOR_ERROR(WSA_IO_PENDING, "Overlapped operations will complete later.");
689     * TEXT_FOR_ERROR(WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state.");
690     * TEXT_FOR_ERROR(WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available.");
691     * TEXT_FOR_ERROR(WSAPROVIDERFAILEDINIT, "Unable to initialize a service provider.");
692     * TEXT_FOR_ERROR(WSASYSCALLFAILURE, "System call failure.");
693     * TEXT_FOR_ERROR(WSA_OPERATION_ABORTED, "Overlapped operation aborted.");
694     */
695
696    sprintf(tmp_buf, "(error number %d)", errcode);
697    return tmp_buf;
698 }
699 #endif /* def _WIN32 */
700
701
702 /*
703   Local Variables:
704   tab-width: 3
705   end:
706 */
707