nspr: import 3.0 RC1 cutoff from CVS
[mozilla-nspr.git] / nsprpub / pr / src / io / prlog.c
blobe159958891fce2ab3b81dba5017837b0be24371f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is the Netscape Portable Runtime (NSPR).
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-2000
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * Contributors:
42 * This Original Code has been modified by IBM Corporation.
43 * Modifications made by IBM described herein are
44 * Copyright (c) International Business Machines Corporation, 2000.
45 * Modifications to Mozilla code or documentation identified per
46 * MPL Section 3.3
48 * Date Modified by Description of modification
49 * 04/10/2000 IBM Corp. Added DebugBreak() definitions for OS/2
52 #include "primpl.h"
53 #include "prenv.h"
54 #include "prprf.h"
55 #include <string.h>
58 * Lock used to lock the log.
60 * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may
61 * contain assertions. We have to avoid assertions in _PR_LOCK_LOG
62 * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG.
63 * This can lead to infinite recursion.
65 static PRLock *_pr_logLock;
66 #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
67 #define _PR_LOCK_LOG() PR_Lock(_pr_logLock);
68 #define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock);
69 #elif defined(_PR_GLOBAL_THREADS_ONLY)
70 #define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock)
71 #define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); }
72 #else
74 #define _PR_LOCK_LOG() \
75 { \
76 PRIntn _is; \
77 PRThread *_me = _PR_MD_CURRENT_THREAD(); \
78 if (!_PR_IS_NATIVE_THREAD(_me)) \
79 _PR_INTSOFF(_is); \
80 _PR_LOCK_LOCK(_pr_logLock)
82 #define _PR_UNLOCK_LOG() \
83 _PR_LOCK_UNLOCK(_pr_logLock); \
84 PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
85 if (!_PR_IS_NATIVE_THREAD(_me)) \
86 _PR_INTSON(_is); \
89 #endif
91 #if defined(XP_PC)
92 #define strcasecmp stricmp
93 #define strncasecmp strnicmp
94 #endif
97 * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
98 * because every asynchronous file io operation leads to a fiber context
99 * switch. So we define _PUT_LOG as fputs (from stdio.h). A side
100 * benefit is that fputs handles the LF->CRLF translation. This
101 * code can also be used on other platforms with file stream io.
103 #if defined(WIN32) || defined(XP_OS2)
104 #define _PR_USE_STDIO_FOR_LOGGING
105 #endif
108 ** Coerce Win32 log output to use OutputDebugString() when
109 ** NSPR_LOG_FILE is set to "WinDebug".
111 #if defined(XP_PC)
112 #define WIN32_DEBUG_FILE (FILE*)-2
113 #endif
115 /* Macros used to reduce #ifdef pollution */
117 #if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
118 #define _PUT_LOG(fd, buf, nb) \
119 PR_BEGIN_MACRO \
120 if (logFile == WIN32_DEBUG_FILE) { \
121 char savebyte = buf[nb]; \
122 buf[nb] = '\0'; \
123 OutputDebugString(buf); \
124 buf[nb] = savebyte; \
125 } else { \
126 fwrite(buf, 1, nb, fd); \
127 fflush(fd); \
129 PR_END_MACRO
130 #elif defined(_PR_USE_STDIO_FOR_LOGGING)
131 #define _PUT_LOG(fd, buf, nb) {fwrite(buf, 1, nb, fd); fflush(fd);}
132 #elif defined(_PR_PTHREADS)
133 #define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
134 #elif defined(XP_MAC)
135 #define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE_SYNC(fd, buf, nb)
136 #else
137 #define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
138 #endif
140 /************************************************************************/
142 static PRLogModuleInfo *logModules;
144 static char *logBuf = NULL;
145 static char *logp;
146 static char *logEndp;
147 #ifdef _PR_USE_STDIO_FOR_LOGGING
148 static FILE *logFile = NULL;
149 #else
150 static PRFileDesc *logFile = 0;
151 #endif
153 #define LINE_BUF_SIZE 512
154 #define DEFAULT_BUF_SIZE 16384
156 #ifdef _PR_NEED_STRCASECMP
159 * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
160 * such as NCR and Unixware. Linking with both libc and libucb
161 * may cause some problem, so I just provide our own implementation
162 * of strcasecmp here.
165 static const unsigned char uc[] =
167 '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
168 '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
169 '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
170 '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
171 ' ', '!', '"', '#', '$', '%', '&', '\'',
172 '(', ')', '*', '+', ',', '-', '.', '/',
173 '0', '1', '2', '3', '4', '5', '6', '7',
174 '8', '9', ':', ';', '<', '=', '>', '?',
175 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
176 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
177 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
178 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
179 '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
180 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
181 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
182 'X', 'Y', 'Z', '{', '|', '}', '~', '\177'
185 PRIntn strcasecmp(const char *a, const char *b)
187 const unsigned char *ua = (const unsigned char *)a;
188 const unsigned char *ub = (const unsigned char *)b;
190 if( ((const char *)0 == a) || (const char *)0 == b )
191 return (PRIntn)(a-b);
193 while( (uc[*ua] == uc[*ub]) && ('\0' != *a) )
195 a++;
196 ua++;
197 ub++;
200 return (PRIntn)(uc[*ua] - uc[*ub]);
203 #endif /* _PR_NEED_STRCASECMP */
205 void _PR_InitLog(void)
207 char *ev;
209 _pr_logLock = PR_NewLock();
211 ev = PR_GetEnv("NSPR_LOG_MODULES");
212 if (ev && ev[0]) {
213 char module[64]; /* Security-Critical: If you change this
214 * size, you must also change the sscanf
215 * format string to be size-1.
217 PRBool isSync = PR_FALSE;
218 PRIntn evlen = strlen(ev), pos = 0;
219 PRInt32 bufSize = DEFAULT_BUF_SIZE;
220 while (pos < evlen) {
221 PRIntn level = 1, count = 0, delta = 0;
222 count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
223 module, &delta, &level, &delta);
224 pos += delta;
225 if (count == 0) break;
228 ** If count == 2, then we got module and level. If count
229 ** == 1, then level defaults to 1 (module enabled).
231 if (strcasecmp(module, "sync") == 0) {
232 isSync = PR_TRUE;
233 } else if (strcasecmp(module, "bufsize") == 0) {
234 if (level >= LINE_BUF_SIZE) {
235 bufSize = level;
237 } else {
238 PRLogModuleInfo *lm = logModules;
239 PRBool skip_modcheck =
240 (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE;
242 while (lm != NULL) {
243 if (skip_modcheck) lm -> level = (PRLogModuleLevel)level;
244 else if (strcasecmp(module, lm->name) == 0) {
245 lm->level = (PRLogModuleLevel)level;
246 break;
248 lm = lm->next;
251 /*found:*/
252 count = sscanf(&ev[pos], " , %n", &delta);
253 pos += delta;
254 if (count == EOF) break;
256 PR_SetLogBuffering(isSync ? 0 : bufSize);
258 #ifdef XP_UNIX
259 if ((getuid() != geteuid()) || (getgid() != getegid())) {
260 return;
262 #endif /* XP_UNIX */
264 ev = PR_GetEnv("NSPR_LOG_FILE");
265 if (ev && ev[0]) {
266 if (!PR_SetLogFile(ev)) {
267 #ifdef XP_PC
268 char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
269 if (str) {
270 OutputDebugString(str);
271 PR_smprintf_free(str);
273 #else
274 fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
275 #endif
277 } else {
278 #ifdef _PR_USE_STDIO_FOR_LOGGING
279 logFile = stderr;
280 #else
281 logFile = _pr_stderr;
282 #endif
287 void _PR_LogCleanup(void)
289 PRLogModuleInfo *lm = logModules;
291 PR_LogFlush();
293 #ifdef _PR_USE_STDIO_FOR_LOGGING
294 if (logFile
295 && logFile != stdout
296 && logFile != stderr
297 #ifdef XP_PC
298 && logFile != WIN32_DEBUG_FILE
299 #endif
301 fclose(logFile);
302 logFile = NULL;
304 #else
305 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
306 PR_Close(logFile);
307 logFile = NULL;
309 #endif
311 if (logBuf)
312 PR_DELETE(logBuf);
314 while (lm != NULL) {
315 PRLogModuleInfo *next = lm->next;
316 free((/*const*/ char *)lm->name);
317 PR_Free(lm);
318 lm = next;
320 logModules = NULL;
322 if (_pr_logLock) {
323 PR_DestroyLock(_pr_logLock);
324 _pr_logLock = NULL;
328 static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm )
330 char *ev;
332 ev = PR_GetEnv("NSPR_LOG_MODULES");
333 if (ev && ev[0]) {
334 char module[64]; /* Security-Critical: If you change this
335 * size, you must also change the sscanf
336 * format string to be size-1.
338 PRIntn evlen = strlen(ev), pos = 0;
339 while (pos < evlen) {
340 PRIntn level = 1, count = 0, delta = 0;
342 count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
343 module, &delta, &level, &delta);
344 pos += delta;
345 if (count == 0) break;
348 ** If count == 2, then we got module and level. If count
349 ** == 1, then level defaults to 1 (module enabled).
351 if (lm != NULL)
353 if ((strcasecmp(module, "all") == 0)
354 || (strcasecmp(module, lm->name) == 0))
356 lm->level = (PRLogModuleLevel)level;
359 count = sscanf(&ev[pos], " , %n", &delta);
360 pos += delta;
361 if (count == EOF) break;
364 } /* end _PR_SetLogModuleLevel() */
366 PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name)
368 PRLogModuleInfo *lm;
370 if (!_pr_initialized) _PR_ImplicitInitialization();
372 lm = PR_NEWZAP(PRLogModuleInfo);
373 if (lm) {
374 lm->name = strdup(name);
375 lm->level = PR_LOG_NONE;
376 lm->next = logModules;
377 logModules = lm;
378 _PR_SetLogModuleLevel(lm);
380 return lm;
383 PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
385 #ifdef _PR_USE_STDIO_FOR_LOGGING
386 FILE *newLogFile;
388 #ifdef XP_PC
389 if ( strcmp( file, "WinDebug") == 0)
391 newLogFile = WIN32_DEBUG_FILE;
393 else
394 #endif
396 newLogFile = fopen(file, "w");
397 if (!newLogFile)
398 return PR_FALSE;
400 /* We do buffering ourselves. */
401 setvbuf(newLogFile, NULL, _IONBF, 0);
403 if (logFile
404 && logFile != stdout
405 && logFile != stderr
406 #ifdef XP_PC
407 && logFile != WIN32_DEBUG_FILE
408 #endif
410 fclose(logFile);
412 logFile = newLogFile;
413 return PR_TRUE;
414 #else
415 PRFileDesc *newLogFile;
417 newLogFile = PR_Open(file, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0666);
418 if (newLogFile) {
419 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
420 PR_Close(logFile);
422 logFile = newLogFile;
423 #if defined(XP_MAC)
424 SetLogFileTypeCreator(file);
425 #endif
427 return (PRBool) (newLogFile != 0);
428 #endif /* _PR_USE_STDIO_FOR_LOGGING */
431 PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size)
433 PR_LogFlush();
435 if (logBuf)
436 PR_DELETE(logBuf);
438 if (buffer_size >= LINE_BUF_SIZE) {
439 logp = logBuf = (char*) PR_MALLOC(buffer_size);
440 logEndp = logp + buffer_size;
444 PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...)
446 va_list ap;
447 char line[LINE_BUF_SIZE];
448 char *line_long = NULL;
449 PRUint32 nb_tid, nb;
450 PRThread *me;
452 if (!_pr_initialized) _PR_ImplicitInitialization();
454 if (!logFile) {
455 return;
458 me = PR_GetCurrentThread();
459 nb_tid = PR_snprintf(line, sizeof(line)-1, "%ld[%p]: ",
460 #if defined(_PR_DCETHREADS)
461 /* The problem is that for _PR_DCETHREADS, pthread_t is not a
462 * pointer, but a structure; so you can't easily print it...
464 me ? &(me->id): 0L, me);
465 #elif defined(_PR_BTHREADS)
466 me, me);
467 #else
468 me ? me->id : 0L, me);
469 #endif
471 va_start(ap, fmt);
472 nb = nb_tid + PR_vsnprintf(line+nb_tid, sizeof(line)-nb_tid-1, fmt, ap);
473 va_end(ap);
476 * Check if we might have run out of buffer space (in case we have a
477 * long line), and malloc a buffer just this once.
479 if (nb == sizeof(line)-2) {
480 va_start(ap, fmt);
481 line_long = PR_vsmprintf(fmt, ap);
482 va_end(ap);
483 /* If this failed, we'll fall back to writing the truncated line. */
486 if (line_long) {
487 nb = strlen(line_long);
488 _PR_LOCK_LOG();
489 if (logBuf != 0) {
490 _PUT_LOG(logFile, logBuf, logp - logBuf);
491 logp = logBuf;
493 /* Write out the thread id and the malloc'ed buffer. */
494 _PUT_LOG(logFile, line, nb_tid);
495 _PUT_LOG(logFile, line_long, nb);
496 /* Ensure there is a trailing newline. */
497 if (!nb || (line_long[nb-1] != '\n')) {
498 char eol[2];
499 eol[0] = '\n';
500 eol[1] = '\0';
501 _PUT_LOG(logFile, eol, 1);
503 _PR_UNLOCK_LOG();
504 PR_smprintf_free(line_long);
505 } else {
506 /* Ensure there is a trailing newline. */
507 if (nb && (line[nb-1] != '\n')) {
508 line[nb++] = '\n';
509 line[nb] = '\0';
511 _PR_LOCK_LOG();
512 if (logBuf == 0) {
513 _PUT_LOG(logFile, line, nb);
514 } else {
515 /* If nb can't fit into logBuf, write out logBuf first. */
516 if (logp + nb > logEndp) {
517 _PUT_LOG(logFile, logBuf, logp - logBuf);
518 logp = logBuf;
520 /* nb is guaranteed to fit into logBuf. */
521 memcpy(logp, line, nb);
522 logp += nb;
524 _PR_UNLOCK_LOG();
526 PR_LogFlush();
529 PR_IMPLEMENT(void) PR_LogFlush(void)
531 if (logBuf && logFile) {
532 _PR_LOCK_LOG();
533 if (logp > logBuf) {
534 _PUT_LOG(logFile, logBuf, logp - logBuf);
535 logp = logBuf;
537 _PR_UNLOCK_LOG();
541 PR_IMPLEMENT(void) PR_Abort(void)
543 PR_LogPrint("Aborting");
544 abort();
547 #if defined(XP_OS2)
549 * Added definitions for DebugBreak() for 2 different OS/2 compilers.
550 * Doing the int3 on purpose for Visual Age so that a developer can
551 * step over the instruction if so desired. Not always possible if
552 * trapping due to exception handling IBM-AKR
554 #if defined(XP_OS2_VACPP)
555 #include <builtin.h>
556 static void DebugBreak(void) { _interrupt(3); }
557 #elif defined(XP_OS2_EMX)
558 static void DebugBreak(void) { asm("int $3"); }
559 #else
560 static void DebugBreak(void) { }
561 #endif
562 #endif /* XP_OS2 */
564 PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln)
566 PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
567 #if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
568 fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
569 #endif
570 #ifdef XP_MAC
571 dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln);
572 #endif
573 #if defined(WIN32) || defined(XP_OS2)
574 DebugBreak();
575 #endif
576 #ifndef XP_MAC
577 abort();
578 #endif
581 #ifdef XP_MAC
582 PR_IMPLEMENT(void) PR_Init_Log(void)
584 _PR_InitLog();
586 #endif