Fix:Core:Avoid gmtime_r which isn't available on all platforms
[navit-package] / navit / log.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include <sys/time.h>
28 #include <glib.h>
29 #include "file.h"
30 #include "item.h"
31 #include "event.h"
32 #include "callback.h"
33 #include "debug.h"
34 #include "log.h"
35
36 struct log_data {
37         int len;
38         int max_len;
39         char *data;
40 };
41
42 struct log {
43         FILE *f;
44         int overwrite;
45         int empty;
46         int lazy;
47         int mkdir;
48         int flush_size;
49         int flush_time;
50         struct event_timeout *timer;
51         struct callback *timer_callback;
52         struct timeval last_flush;
53         char *filename;
54         char *filename_ex1;
55         char *filename_ex2;
56         struct log_data header;
57         struct log_data data;
58         struct log_data trailer;
59         struct attr **attrs;
60 };
61
62 static void
63 strftime_localtime(char *buffer, int size, char *fmt)
64 {
65         time_t t;
66         struct tm *tm;
67
68         t=time(NULL);
69         tm=localtime(&t);
70         strftime(buffer, 4096, fmt, tm);
71 }
72
73 static void
74 expand_filenames(struct log *this_)
75 {
76         char buffer[4096];
77         int i;
78
79         strftime_localtime(buffer, 4096, this_->filename);
80         this_->filename_ex1=g_strdup(buffer);
81         if (strstr(this_->filename_ex1,"%i")) {
82                 i=0;
83                 do {
84                         g_free(this_->filename_ex2);
85                         this_->filename_ex2=g_strdup_printf(this_->filename_ex1,i++);
86                 } while (file_exists(this_->filename_ex2));
87         } else 
88                 this_->filename_ex2=g_strdup(this_->filename_ex1);
89 }
90
91 static void
92 log_set_last_flush(struct log *this_)
93 {
94         gettimeofday(&this_->last_flush, NULL);
95 }
96
97 static void
98 log_open(struct log *this_)
99 {
100         char *mode;
101         if (this_->overwrite)
102                 mode="w";
103         else
104                 mode="r+";
105         if (this_->mkdir)
106                 file_mkdir(this_->filename_ex2, 2);
107         this_->f=fopen(this_->filename_ex2, mode);
108         if (! this_->f)
109                 this_->f=fopen(this_->filename_ex2, "w");
110         if (! this_->f)
111                 return;
112         if (!this_->overwrite) 
113                 fseek(this_->f, 0, SEEK_END);
114         this_->empty = !ftell(this_->f);
115         log_set_last_flush(this_);
116 }
117
118 static void
119 log_close(struct log *this_)
120 {
121         if (! this_->f)
122                 return;
123         if (this_->trailer.len) 
124                 fwrite(this_->trailer.data, 1, this_->trailer.len, this_->f);
125         fflush(this_->f);
126         fclose(this_->f);
127         this_->f=NULL;
128 }
129
130 static void
131 log_flush(struct log *this_, enum log_flags flags)
132 {
133         long pos;
134         if (this_->lazy && !this_->f) {
135                 if (!this_->data.len)
136                         return;
137                 log_open(this_);
138         }
139         if (! this_->f)
140                 return;
141         if (this_->empty) {
142                 if (this_->header.len) 
143                         fwrite(this_->header.data, 1, this_->header.len, this_->f);
144                 if (this_->header.len || this_->data.len)
145                         this_->empty=0;
146         }
147         fwrite(this_->data.data, 1, this_->data.len, this_->f);
148 #ifndef HAVE_API_WIN32_BASE
149         if (flags & log_flag_truncate) {
150                 pos=ftell(this_->f);
151                 ftruncate(fileno(this_->f), pos);
152         }
153 #endif
154         if (this_->trailer.len) {
155                 pos=ftell(this_->f);
156                 if (pos > 0) {
157                         fwrite(this_->trailer.data, 1, this_->trailer.len, this_->f);
158                         fseek(this_->f, pos, SEEK_SET); 
159                 }
160         }
161         if (flags & log_flag_keep_pointer)
162                 fseek(this_->f, -this_->data.len, SEEK_CUR);
163         fflush(this_->f);
164         if (!(flags & log_flag_keep_buffer)) {
165                 g_free(this_->data.data);
166                 this_->data.data=NULL;
167                 this_->data.max_len=this_->data.len=0;
168         }
169         log_set_last_flush(this_);
170 }
171
172 static int
173 log_flush_required(struct log *this_)
174 {
175         return this_->data.len > this_->flush_size;
176 }
177
178
179 static void
180 log_change(struct log *this_)
181 {
182         log_flush(this_,0);
183         log_close(this_);
184         expand_filenames(this_);
185         if (! this_->lazy)
186                 log_open(this_);
187 }
188
189 static int
190 log_change_required(struct log *this_)
191 {
192         char buffer[4096];
193
194         strftime_localtime(buffer, 4096, this_->filename);
195         return (strcmp(this_->filename_ex1, buffer) != 0);
196 }
197
198 static void
199 log_timer(struct log *this_)
200 {
201         struct timeval tv;
202         int delta;
203         gettimeofday(&tv, NULL);
204         delta=(tv.tv_sec-this_->last_flush.tv_sec)*1000+(tv.tv_usec-this_->last_flush.tv_usec)/1000;
205         dbg(1,"delta=%d flush_time=%d\n", delta, this_->flush_time);
206         if (this_->flush_time && delta >= this_->flush_time*1000)
207                 log_flush(this_,0);
208 }
209
210 int
211 log_get_attr(struct log *this_, enum attr_type type, struct attr *attr, struct attr_iter *iter)
212 {
213         return attr_generic_get_attr(this_->attrs, NULL, type, attr, iter);
214 }
215
216
217 struct log *
218 log_new(struct attr * parent,struct attr **attrs)
219 {
220         struct log *ret=g_new0(struct log, 1);
221         struct attr *data,*overwrite,*lazy,*mkdir,*flush_size,*flush_time;
222         struct file_wordexp *wexp;
223         char *filename, **wexp_data;
224
225         dbg(1,"enter\n");
226         data=attr_search(attrs, NULL, attr_data);
227         if (! data)
228                 return NULL;
229         filename=data->u.str;
230         wexp=file_wordexp_new(filename);
231         if (wexp && file_wordexp_get_count(wexp) > 0) {
232                 wexp_data=file_wordexp_get_array(wexp);
233                 filename=wexp_data[0];
234         }
235         if (filename)
236                 ret->filename=g_strdup(filename);
237         if (wexp)
238                 file_wordexp_destroy(wexp);
239         overwrite=attr_search(attrs, NULL, attr_overwrite);
240         if (overwrite)
241                 ret->overwrite=overwrite->u.num;
242         lazy=attr_search(attrs, NULL, attr_lazy);
243         if (lazy)
244                 ret->lazy=lazy->u.num;
245         mkdir=attr_search(attrs, NULL, attr_mkdir);
246         if (mkdir)
247                 ret->mkdir=mkdir->u.num;
248         flush_size=attr_search(attrs, NULL, attr_flush_size);
249         if (flush_size)
250                 ret->flush_size=flush_size->u.num;
251         flush_time=attr_search(attrs, NULL, attr_flush_time);
252         if (flush_time)
253                 ret->flush_time=flush_time->u.num;
254         if (ret->flush_time) {
255                 dbg(1,"interval %d\n", ret->flush_time*1000);
256                 ret->timer_callback=callback_new_1(callback_cast(log_timer), ret);
257                 ret->timer=event_add_timeout(ret->flush_time*1000, 1, ret->timer_callback);
258         }
259         expand_filenames(ret);
260         if (ret->lazy)
261                 log_set_last_flush(ret);
262         else
263                 log_open(ret);
264         ret->attrs=attr_list_dup(attrs);
265         return ret;
266 }
267
268 void
269 log_set_header(struct log *this_, char *data, int len)
270 {
271         this_->header.data=g_malloc(len);
272         this_->header.max_len=this_->header.len=len;
273         memcpy(this_->header.data, data, len);
274 }
275
276 void
277 log_set_trailer(struct log *this_, char *data, int len)
278 {
279         this_->trailer.data=g_malloc(len);
280         this_->trailer.max_len=this_->trailer.len=len;
281         memcpy(this_->trailer.data, data, len);
282 }
283
284 void
285 log_write(struct log *this_, char *data, int len, enum log_flags flags)
286 {
287         dbg(1,"enter\n");
288         if (log_change_required(this_)) {
289                 dbg(1,"log_change");
290                 log_change(this_);
291         }
292         if (flags & log_flag_replace_buffer)
293                 this_->data.len=0;
294         if (this_->data.len + len > this_->data.max_len) {
295                 dbg(2,"overflow\n");
296                 this_->data.max_len+=16384;
297                 this_->data.data=g_realloc(this_->data.data,this_->data.max_len);
298         }
299         memcpy(this_->data.data+this_->data.len, data, len);
300         this_->data.len+=len;
301         if (log_flush_required(this_) || (flags & log_flag_force_flush))
302                 log_flush(this_, flags);
303 }
304
305 void *
306 log_get_buffer(struct log *this_, int *len)
307 {
308         if (len)
309                 *len=this_->data.len;
310         return this_->data.data;
311 }
312
313
314 void
315 log_printf(struct log *this_, char *fmt, ...)
316 {
317         char buffer[LOG_BUFFER_SIZE];
318         int size;
319         va_list ap;
320
321         va_start(ap, fmt);
322
323         // Format the string and write it to the log
324         size = vsnprintf(buffer, LOG_BUFFER_SIZE, fmt, ap);
325         log_write(this_, buffer, size, 0);
326
327         va_end(ap);
328 }
329
330 void
331 log_destroy(struct log *this_)
332 {
333         callback_destroy(this_->timer_callback);
334         event_remove_timeout(this_->timer);
335         log_flush(this_,0);
336         log_close(this_);
337         g_free(this_);
338 }