naev 0.12.6
log.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "physfs.h"
11#include <stdarg.h>
12#include <stdio.h>
13#include <time.h> /* strftime */
14
15#include "naev.h"
17
18#include "log.h"
19
20#include "conf.h"
21#include "debug.h"
22#include "ndata.h"
23
25static char *outcopy = NULL;
26static char *errcopy = NULL;
27
28static size_t moutcopy; /* Allocated size of outcopy. */
29static size_t merrcopy; /* Allocated size of errcopy. */
30
31static int noutcopy = 0; /* Number of bytes written to outcopy. */
32static int nerrcopy = 0; /* Number of bytes written to errcopy. */
33
35static char *outfiledouble = NULL;
36static char *errfiledouble = NULL;
37
38/* Whether to copy stdout and stderr to temporary buffers. */
39static int copying = 0;
40
41/* File descriptors */
42static PHYSFS_File *logout_file = NULL;
43static PHYSFS_File *logerr_file = NULL;
44
45/*
46 * Prototypes
47 */
48static int slogprintf( FILE *stream, int newline, const char *str, size_t n );
49static int vlogprintf( FILE *stream, int newline, const char *fmt, va_list ap );
50static void log_copy( int enable );
51static void log_append( const FILE *stream, const char *str );
52static void log_cleanStream( PHYSFS_File **file, const char *fname,
53 const char *filedouble );
54static void log_purge( void );
55
59static int slogprintf( FILE *stream, int newline, const char *str, size_t n )
60{
61 /* Append to strfer. */
62 if ( copying )
63 log_append( stream, str );
64
65 if ( stream == stdout && logout_file != NULL ) {
66 PHYSFS_writeBytes( logout_file, str, newline ? n + 1 : n );
67 if ( newline )
68 PHYSFS_flush( logout_file );
69 }
70
71 if ( stream == stderr && logerr_file != NULL ) {
72 PHYSFS_writeBytes( logerr_file, str, newline ? n + 1 : n );
73 if ( newline )
74 PHYSFS_flush( logerr_file );
75 }
76
77 /* Also print to the stream. */
78 n = fprintf( stream, "%s", str );
79 if ( newline )
80 fflush( stream );
81 return n;
82}
83
87static int vlogprintf( FILE *stream, int newline, const char *fmt, va_list ap )
88{
89 va_list aq;
90 char *buf;
91 size_t n;
92
93 /* Offset to add error colour header as necessary. */
94 va_copy( aq, ap );
95 n = vsnprintf( NULL, 0, fmt, aq );
96 va_end( aq );
97 buf = malloc( n + 2 );
98 n = vsnprintf( buf, n + 1, fmt, ap );
99
100 /* Finally add newline if necessary. */
101 if ( newline ) {
102 buf[n] = '\n';
103 buf[n + 1] = '\0';
104 } else
105 buf[n] = '\0';
106
107 slogprintf( stream, newline, buf, n );
108 free( buf );
109 return n;
110}
111
116int logprintf( FILE *stream, int newline, const char *fmt, ... )
117{
118 int ret;
119 va_list ap;
120 va_start( ap, fmt );
121 ret = vlogprintf( stream, newline, fmt, ap );
122 va_end( ap );
123 return ret;
124}
125
130void log_redirect( void )
131{
132 time_t cur;
133 struct tm *ts;
134 char timestr[20];
135
136 if ( !conf.redirect_file )
137 return;
138
139 time( &cur );
140 ts = localtime( &cur );
141 strftime( timestr, sizeof( timestr ), "%Y-%m-%d_%H-%M-%S", ts );
142
143 PHYSFS_mkdir( "logs" );
144 logout_file = PHYSFS_openWrite( "logs/stdout.txt" );
145 if ( logout_file == NULL )
146 WARN( _( "Unable to redirect stdout to file" ) );
147
148 logerr_file = PHYSFS_openWrite( "logs/stderr.txt" );
149 if ( logerr_file == NULL )
150 WARN( _( "Unable to redirect stderr to file" ) );
151
152 SDL_asprintf( &outfiledouble, "logs/%s_stdout.txt", timestr );
153 SDL_asprintf( &errfiledouble, "logs/%s_stderr.txt", timestr );
154
155 log_copy( 0 );
156}
157
164void log_init( void )
165{
166 log_copy( conf.redirect_file );
167}
168
179void log_copy( int enable )
180{
181 /* Nothing to do. */
182 if ( copying == enable )
183 return;
184
185 if ( enable ) {
186 copying = 1;
187
188 moutcopy = 1;
189 noutcopy = 0;
190 outcopy = calloc( moutcopy, BUFSIZ );
191
192 merrcopy = 1;
193 nerrcopy = 0;
194 errcopy = calloc( merrcopy, BUFSIZ );
195
196 return;
197 }
198
199 if ( noutcopy && logout_file != NULL )
200 PHYSFS_writeBytes( logout_file, outcopy, strlen( outcopy ) );
201
202 if ( nerrcopy && logerr_file != NULL )
203 PHYSFS_writeBytes( logerr_file, errcopy, strlen( errcopy ) );
204
205 log_purge();
206}
207
211static void log_purge( void )
212{
213 if ( !copying )
214 return;
215
216 free( outcopy );
217 free( errcopy );
218
219 outcopy = NULL;
220 errcopy = NULL;
221
222 copying = 0;
223}
224
228void log_clean( void )
229{
230 log_cleanStream( &logout_file, "logs/stdout.txt", outfiledouble );
231 log_cleanStream( &logerr_file, "logs/stderr.txt", errfiledouble );
232}
233
237static void log_cleanStream( PHYSFS_File **file, const char *fname,
238 const char *filedouble )
239{
240 PHYSFS_Stat stat;
241
242 if ( *file == NULL )
243 return;
244
245 PHYSFS_close( *file );
246 *file = NULL;
247
248 if ( PHYSFS_stat( fname, &stat ) == 0 )
249 return;
250
251 if ( stat.filesize == 0 )
252 PHYSFS_delete( fname );
253 else
254 ndata_copyIfExists( fname, filedouble );
255}
256
263static void log_append( const FILE *stream, const char *str )
264{
265 int len = strlen( str );
266 if ( stream == stdout ) {
267 while ( ( len + noutcopy ) >= (int)moutcopy ) {
268 moutcopy *= 2;
269 outcopy = realloc( outcopy, moutcopy );
270 if ( outcopy == NULL )
271 goto copy_err;
272 }
273
274 strncpy( &outcopy[noutcopy], str, len + 1 );
275 noutcopy += len;
276 } else if ( stream == stderr ) {
277 while ( ( len + nerrcopy ) >= (int)merrcopy ) {
278 merrcopy *= 2;
279 errcopy = realloc( errcopy, merrcopy );
280 if ( errcopy == NULL )
281 goto copy_err;
282 }
283
284 strncpy( &errcopy[nerrcopy], str, len + 1 );
285 nerrcopy += len;
286 }
287
288 return;
289
290copy_err:
291 log_purge();
292 WARN( _( "An error occurred while buffering %s!" ),
293 stream == stdout ? "stdout" : "stderr" );
294}
295
299int log_warn( const char *file, size_t line, const char *func, const char *fmt,
300 ... )
301{
302 static char *warn_last_msg = NULL;
303 static int warn_last_num;
304 va_list ap;
305 char *buf;
306 size_t n;
307
308 /* Create the new message. */
309 va_start( ap, fmt );
310 n = vsnprintf( NULL, 0, fmt, ap );
311 va_end( ap );
312 buf = malloc( n + 2 );
313 va_start( ap, fmt );
314 n = vsnprintf( buf, n + 1, fmt, ap );
315 va_end( ap );
316 buf[n] = '\n';
317 buf[n + 1] = '\0';
318
319 /* See if we are repeating ourselves. */
320 if ( ( warn_last_msg != NULL ) && strcmp( warn_last_msg, buf ) == 0 ) {
321 warn_last_num++;
322 if ( warn_last_num == 10 )
323 logprintf( stderr, 1,
324 _( "LAST WARNING PRINTED %d TIMES, SKIPPING FROM NOW ON" ),
325 10 );
326 if ( warn_last_num >= 10 ) {
327 free( buf );
328 return 0;
329 }
330 }
331
332 /* First do a backtrace, if possible. */
333 debug_logBacktrace();
334
335 /* Display messages. */
336 logprintf( stderr, 0, _( "WARNING %s:%lu [%s]: " ), file,
337 (unsigned long)line, func );
338 slogprintf( stderr, 1, buf, n );
339
340 /* Reset last message. */
341 free( warn_last_msg );
342 warn_last_msg = buf;
343
344#ifdef DEBUG_PARANOID
345#if __WIN32__
346 __debugbreak();
347#else
348 raise( SIGINT );
349#endif /* __WIN32__ */
350#endif /* DEBUG_PARANOID */
351 return n;
352}
void log_clean(void)
Deletes useless (empty) log files from the current session.
Definition log.c:228
int logprintf(FILE *stream, int newline, const char *fmt,...)
Like fprintf, but automatically teed to log files (and line-terminated if newline is true).
Definition log.c:116
static void log_append(const FILE *stream, const char *str)
Appends a message to a stream's in-memory buffer.
Definition log.c:263
void log_init(void)
Sets up the logging subsystem. (Calling this ensures logging output is preserved until we have a plac...
Definition log.c:164
static void log_copy(int enable)
Sets up or terminates copying of standard streams into memory.
Definition log.c:179
static int vlogprintf(FILE *stream, int newline, const char *fmt, va_list ap)
va_list version of logprintf and backend.
Definition log.c:87
void log_redirect(void)
Sets up redirection of stdout and stderr to files. PhysicsFS must be initialized for this to work.
Definition log.c:130
static void log_cleanStream(PHYSFS_File **file, const char *fname, const char *filedouble)
Definition log.c:237
int log_warn(const char *file, size_t line, const char *func, const char *fmt,...)
Prints warnings, but skips if they are repeated too much.
Definition log.c:299
static int slogprintf(FILE *stream, int newline, const char *str, size_t n)
va_list version of logprintf and backend.
Definition log.c:59
static void log_purge(void)
Deletes copied output without printing the contents.
Definition log.c:211
static char * outfiledouble
Definition log.c:35
static char * outcopy
Definition log.c:25
Header file with generic functions and naev-specifics.
int ndata_copyIfExists(const char *file1, const char *file2)
Copy a file, if it exists.
Definition ndata.c:358