naev 0.11.5
shiplog.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
4
11#include "shiplog.h"
12
13/*Hold a single log entry - a double linked list*/
14typedef struct {
15 int id;
16 ntime_t time;
17 char *msg;
18 void *next;
19 void *prev;
21
22/* Holding global information about the log. */
23typedef struct {
24 int *idList;
25 char **typeList;
26 char **nameList;
27 ntime_t *removeAfter;
28 char **idstrList;
29 int *maxLen;
30 int nlogs;
33} ShipLog;
34
35
38/*
39 * Prototypes.
40 */
42
43
54int shiplog_create( const char *idstr,
55 const char *logname, const char *type,
56 int overwrite, int maxLen)
57{
58 ShipLogEntry *e;
59 int i, id, indx;
60 indx = shipLog.nlogs;
61
62 id = LOG_ID_INVALID;
63 if (overwrite == 1) {
64 /* check to see whether this idstr or logname and type has been created before, and if so, remove all entries of that logid */
65 if (idstr != NULL) {
66 /* find the matching logid for this idstr */
67 for (i=0; i<shipLog.nlogs; i++) {
68 if ((shipLog.idstrList[i] != NULL)
69 && (strcmp(shipLog.idstrList[i], idstr)==0)) {
70 /* matching idstr found. */
71 id = shipLog.idList[i];
72 indx = i;
73 break;
74 }
75 }
76 } else {
77 for (i=0; i<shipLog.nlogs; i++) {
78 if ((strcmp(type, shipLog.typeList[i])==0)
79 && (strcmp(logname, shipLog.nameList[i])==0)) {
80 id = shipLog.idList[i];
81 indx = i;
82 break;
83 }
84 }
85 }
86 if (i < shipLog.nlogs) { /* prev id found - so remove all log entries of this type. */
87 e = shipLog.head;
88 while (e != NULL) {
89 if (e->id == id) {
90 /* remove this entry */
91 e = shiplog_removeEntry( e );
92 } else {
93 e = (ShipLogEntry*)e->next;
94 }
95 }
96 shipLog.maxLen[i] = maxLen;
97 }
98 } else if (overwrite == 2) {
99 /* check to see whether this type has been created before, and if so, remove all entries. */
100 int found = 0;
101 id = LOG_ID_INVALID;
102 for ( i=0; i<shipLog.nlogs; i++ ) {
103 if ( ( ( idstr != NULL ) && ( shipLog.idstrList[i] != NULL )
104 && ( strcmp(idstr, shipLog.idstrList[i]) == 0 ) )
105 || ( ( idstr == NULL )
106 && ( strcmp(type, shipLog.typeList[i]) == 0 ) ) ) {
107 e = shipLog.head;
108 while (e != NULL) {
109 if (e->id == shipLog.idList[i])
110 e = shiplog_removeEntry( e );
111 else
112 e = e->next;
113 }
114 if (found == 0) { /* This is the first entry of this type */
115 found = 1;
116 id = shipLog.idList[i];
117 indx = i;
118 free(shipLog.nameList[i]);
119 shipLog.nameList[i] = strdup(logname);
120 shipLog.maxLen[i] = maxLen;
121 } else { /* a previous entry of this type as been found, so just invalidate this logid. */
122 shipLog.idList[i] = LOG_ID_INVALID;
123 free( shipLog.idstrList[i] );
124 shipLog.idstrList[i] = NULL;
125 }
126 }
127 }
128 }
129
130 if ((indx == shipLog.nlogs) && (idstr != NULL)) {
131 /* see if existing log with this idstr exists, if so, append to it */
132 for (i=0; i<shipLog.nlogs; i++) {
133 if ((shipLog.idstrList[i] != NULL)
134 && (strcmp( idstr, shipLog.idstrList[i] ) == 0)) {
135 id = shipLog.idList[i];
136 indx = i;
137 shipLog.maxLen[i] = maxLen;
138 break;
139 }
140 }
141 }
142 if (indx == shipLog.nlogs) {
143 /* create a new id for this log */
144 id = -1;
145 for (i=0; i<shipLog.nlogs; i++) { /* get maximum id */
146 if (shipLog.idList[i] > id)
147 id = shipLog.idList[i];
148 }
149 id++;
150 shipLog.nlogs++;
151 shipLog.idList = realloc(shipLog.idList, sizeof(int) * shipLog.nlogs);
152 shipLog.nameList = realloc(shipLog.nameList, sizeof(char*) * shipLog.nlogs);
153 shipLog.typeList = realloc(shipLog.typeList, sizeof(char*) * shipLog.nlogs);
154 shipLog.removeAfter = realloc(shipLog.removeAfter, sizeof(ntime_t) * shipLog.nlogs);
155 shipLog.idstrList = realloc(shipLog.idstrList, sizeof(char*) * shipLog.nlogs);
156 shipLog.maxLen = realloc(shipLog.maxLen, sizeof(int) * shipLog.nlogs);
157 shipLog.removeAfter[indx] = 0;
158 shipLog.idList[indx] = id;
159 shipLog.nameList[indx] = strdup(logname);
160 shipLog.typeList[indx] = strdup(type);
161 shipLog.maxLen[indx] = maxLen;
162 shipLog.idstrList[indx]= (idstr==NULL) ? NULL : strdup(idstr);
163 }
164 return id;
165}
166
174int shiplog_append( const char *idstr, const char *msg )
175{
176 int i, id;
177 for (i=0; i<shipLog.nlogs; i++) {
178 if (((idstr == NULL) && (shipLog.idstrList[i] == NULL) )
179 || ((idstr != NULL) && (shipLog.idstrList[i] != NULL)
180 && (strcmp(idstr, shipLog.idstrList[i]) == 0))) {
181 break;
182 }
183 }
184 if (i==shipLog.nlogs) {
185 WARN(_("Warning - log not found: creating it"));
186 id = shiplog_create( idstr, _( "Please report this log as an error to github.com/naev" ),
187 idstr != NULL ? idstr : "", 0, 0 );
188 } else {
189 id = shipLog.idList[i];
190 }
191 return shiplog_appendByID( id, msg);
192}
193
194
202int shiplog_appendByID( int logid,const char *msg )
203{
204 ShipLogEntry *e;
205 ntime_t now = ntime_get();
206 int i,maxLen=0;
207
208 if (logid < 0)
209 return -1;
210
211 /* Check that the log hasn't already been added (e.g. if reloading) */
212 e = shipLog.head;
213 /* check for identical logs */
214 while (e != NULL) {
215 if (e->time != now) { /* logs are created in chronological order */
216 break;
217 }
218 if ((logid == e->id) && (strcmp(e->msg,msg) == 0)) {
219 /* Identical log already exists */
220 return 0;
221 }
222 e = e->next;
223 }
224 if ((e = calloc(sizeof(ShipLogEntry),1)) == NULL) {
225 WARN(_("Error creating new log entry - crash imminent!\n"));
226 return -1;
227 }
228 e->next = shipLog.head;
229 shipLog.head = e;
230 if (shipLog.tail == NULL) /* first entry - point to both head and tail.*/
231 shipLog.tail = e;
232 if (e->next != NULL)
233 ((ShipLogEntry*)e->next)->prev = (void*)e;
234 e->id = logid;
235 e->msg = strdup(msg);
236 e->time = now;
237 for (i=0; i<shipLog.nlogs; i++) {
238 if (shipLog.idList[i] == logid) {
239 maxLen = shipLog.maxLen[i];
240 break;
241 }
242 }
243 if (maxLen > 0) {
244 /* prune log entries if necessary */
245 i = 0;
246 e = shipLog.head;
247 while (e != NULL) {
248 if (e->id == logid) {
249 i++;
250 if (i > maxLen)
251 e = shiplog_removeEntry( e );
252 else
253 e = (ShipLogEntry*) e->next;
254 } else {
255 e = (ShipLogEntry*) e->next;
256 }
257 }
258 }
259 return 0;
260}
261
267void shiplog_delete( int logid )
268{
269 ShipLogEntry *e, *tmp;
270 int i;
271
272 if ((logid < 0) && (logid != LOG_ID_ALL))
273 return;
274
275 e = shipLog.head;
276 while ( e != NULL ) {
277 if ( logid == LOG_ID_ALL || logid == e->id ) {
278 if ( e->prev != NULL )
279 ((ShipLogEntry*)e->prev)->next = e->next;
280 if ( e->next != NULL )
281 ((ShipLogEntry*)e->next)->prev = e->prev;
282 free( e->msg );
283 if ( e == shipLog.head )
284 shipLog.head = e->next;
285 if ( e == shipLog.tail )
286 shipLog.tail = e->prev;
287 tmp = e;
288 e = (ShipLogEntry*) e->next;
289 free( tmp );
290 } else {
291 e = (ShipLogEntry*)e->next;
292 }
293 }
294
295 for ( i=0; i<shipLog.nlogs; i++) {
296 if ( logid == LOG_ID_ALL || logid == shipLog.idList[i] ) {
297 shipLog.idList[i] = LOG_ID_INVALID;
298 free(shipLog.nameList[i]);
299 shipLog.nameList[i] = NULL;
300 free(shipLog.typeList[i]);
301 shipLog.typeList[i] = NULL;
302 free(shipLog.idstrList[i]);
303 shipLog.idstrList[i] = NULL;
304 shipLog.maxLen[i]=0;
305 shipLog.removeAfter[i] = 0;
306 }
307 }
308}
309
317void shiplog_setRemove( int logid, ntime_t when )
318{
319 int i;
320 if (when == 0)
321 when = ntime_get();
322 else if (when < 0) /* add this to ntime */
323 when = ntime_get() - when;
324
325 for (i=0; i<shipLog.nlogs; i++) {
326 if (shipLog.idList[i] == logid) {
327 shipLog.removeAfter[i] = when;
328 break;
329 }
330 }
331}
332
338void shiplog_deleteType( const char *type )
339{
340 int i;
341 if (type == NULL)
342 return;
343 for (i=0; i<shipLog.nlogs; i++) {
344 if ((shipLog.idList[i] >= 0) &&
345 (strcmp(type, shipLog.typeList[i])==0)) {
346 shiplog_delete( shipLog.idList[i] );
347 }
348 }
349}
350
351
355void shiplog_clear (void)
356{
357 shiplog_delete( LOG_ID_ALL );
358 free( shipLog.idList );
359 free( shipLog.nameList );
360 free( shipLog.typeList );
361 free( shipLog.idstrList );
362 free( shipLog.maxLen );
363 free( shipLog.removeAfter );
364 memset(&shipLog, 0, sizeof(ShipLog));
365}
366
370void shiplog_new (void)
371{
373}
374
375/*
376 * @brief Saves the logfiile
377 */
378int shiplog_save( xmlTextWriterPtr writer )
379{
380 int i;
381 ShipLogEntry *e;
382 ntime_t t = ntime_get();
383 xmlw_startElem(writer,"shiplog");
384
385 for (i=0; i<shipLog.nlogs; i++) {
386 if ( shipLog.removeAfter[i]>0 && shipLog.removeAfter[i]<t )
387 shiplog_delete( shipLog.idList[i] );
388 if ( shipLog.idList[i] >= 0 ) {
389 xmlw_startElem(writer, "entry");
390 xmlw_attr(writer,"id","%d",shipLog.idList[i]);
391 xmlw_attr(writer,"t","%s",shipLog.typeList[i]);
392 if ( shipLog.removeAfter[i]!=0 )
393 xmlw_attr(writer,"r","%"PRIu64,shipLog.removeAfter[i]);
394 if ( shipLog.idstrList[i] != NULL )
395 xmlw_attr(writer,"s","%s",shipLog.idstrList[i]);
396 if ( shipLog.maxLen[i] != 0)
397 xmlw_attr(writer,"m","%d",shipLog.maxLen[i]);
398 xmlw_str(writer,"%s",shipLog.nameList[i]);
399 xmlw_endElem(writer);/* entry */
400 }
401 }
402 e=shipLog.head;
403 while ( e != NULL ) {
404 if ( e->id >= 0 ) {
405 xmlw_startElem(writer, "log");
406 xmlw_attr(writer,"id","%d",e->id);
407 xmlw_attr(writer,"t","%"PRIu64,e->time);
408 xmlw_str(writer,"%s",e->msg);
409 xmlw_endElem(writer);/* log */
410 }
411 e=(ShipLogEntry*)e->next;
412 }
413 xmlw_endElem(writer); /* economy */
414 return 0;
415}
416
422int shiplog_load( xmlNodePtr parent )
423{
424 xmlNodePtr node, cur;
425 ShipLogEntry *e;
426 int id,i;
428
429 node = parent->xmlChildrenNode;
430 do {
431 if (xml_isNode(node,"shiplog")) {
432 cur = node->xmlChildrenNode;
433 do {
434 if (xml_isNode(cur, "entry")) {
435 xmlr_attr_int(cur, "id", id);
436 /* check this ID isn't already present */
437 for ( i=0; i<shipLog.nlogs; i++ ) {
438 if ( shipLog.idList[i] == id )
439 break;
440 }
441 if ( i==shipLog.nlogs ) { /* a new ID */
442 shipLog.nlogs++;
443 shipLog.idList = realloc( shipLog.idList, sizeof(int) * shipLog.nlogs);
444 shipLog.nameList = realloc( shipLog.nameList, sizeof(char*) * shipLog.nlogs);
445 shipLog.typeList = realloc( shipLog.typeList, sizeof(char*) * shipLog.nlogs);
446 shipLog.removeAfter = realloc( shipLog.removeAfter, sizeof(ntime_t) * shipLog.nlogs);
447 shipLog.idstrList = realloc( shipLog.idstrList, sizeof(char*) * shipLog.nlogs);
448 shipLog.maxLen = realloc( shipLog.maxLen, sizeof(int) * shipLog.nlogs);
449 shipLog.idList[shipLog.nlogs-1] = id;
450 xmlr_attr_strd( cur, "t", shipLog.typeList[shipLog.nlogs-1] );
451 xmlr_attr_long( cur, "r", shipLog.removeAfter[shipLog.nlogs-1] );
452 xmlr_attr_strd( cur, "s", shipLog.idstrList[shipLog.nlogs-1] );
453 xmlr_attr_int( cur, "m", shipLog.maxLen[shipLog.nlogs-1] );
454 if (shipLog.typeList[shipLog.nlogs-1] == NULL) {
455 shipLog.typeList[shipLog.nlogs-1] = strdup("No type");
456 WARN(_("No ID in shipLog entry"));
457 }
458 shipLog.nameList[shipLog.nlogs-1] = strdup(xml_raw(cur));
459 }
460 } else if (xml_isNode(cur, "log")) {
461 e = calloc( sizeof(ShipLogEntry), 1);
462 /* put this one at the end */
463 e->prev = shipLog.tail;
464 if ( shipLog.tail == NULL )
465 shipLog.head = e;
466 else
467 shipLog.tail->next = e;
468 shipLog.tail = e;
469
470 xmlr_attr_int( cur, "id", e->id );
471 xmlr_attr_long( cur, "t", e->time );
472 e->msg = strdup(xml_raw(cur));
473 }
474 } while (xml_nextNode(cur));
475 }
476 } while (xml_nextNode(node));
477 return 0;
478}
479
480void shiplog_listTypes( int *ntypes, char ***logTypes, int includeAll )
481{
482 int i, j, n=0;
483 char **types = NULL;
484 ntime_t t = ntime_get();
485
486 if ( includeAll ) {
487 types = malloc( sizeof( char * ) );
488 n = 1;
489 types[0] = strdup( _("All") );
490 }
491 for ( i=0; i<shipLog.nlogs; i++ ) {
492 if ( shipLog.removeAfter[i] > 0 && shipLog.removeAfter[i]<t ) {
493 /* log expired, so remove (which sets id to LOG_ID_INVALID) */
494 shiplog_delete(shipLog.idList[i]);
495 }
496 if ( shipLog.idList[i] >= 0 ) {
497 /* log valid */
498 for ( j=0; j<n; j++ ) {
499 if ( strcmp(shipLog.typeList[i], types[j]) == 0 )
500 break;
501 }
502 if ( j==n ) {/*This log type not found, so add.*/
503 n++;
504 types = realloc( types, sizeof( char * ) * n );
505 types[n-1] = strdup(shipLog.typeList[i]);
506 }
507 }
508 }
509 *ntypes=n;
510 *logTypes=types;
511}
512
522void shiplog_listLogsOfType( const char *type,
523 int *nlogs, char ***logsOut, int **logIDs, int includeAll )
524{
525 int i, n;
526 char **logs;
527 int *logid;
528 ntime_t t = ntime_get();
529
530 n = !!includeAll;
531 logs = realloc(*logsOut, sizeof(char*) * n);
532 logid = realloc(*logIDs, sizeof(int) * n);
533 if ( includeAll ) {
534 logs[0] = strdup( _("All") );
535 logid[0] = LOG_ID_ALL;
536 }
537 if ( shipLog.nlogs > 0 ) {
538 for ( i=shipLog.nlogs-1; i>=0; i-- ) {
539 if ( shipLog.removeAfter[i] > 0 && shipLog.removeAfter[i]<t ) {
540 /* log expired, so remove (which sets id to LOG_ID_INVALID) */
541 shiplog_delete(shipLog.idList[i]);
542 }
543 if ( ( shipLog.idList[i] >= 0 )
544 && ( (type == NULL) || ( strcmp(type, shipLog.typeList[i]) == 0 ) ) ) {
545 n++;
546 logs = realloc( logs, sizeof( char * ) * n );
547 logs[n-1] = strdup(shipLog.nameList[i]);
548 logid = realloc( logid, sizeof( int ) * n );
549 logid[n-1] = shipLog.idList[i];
550 }
551 }
552 }
553 *nlogs = n;
554 *logsOut = logs;
555 *logIDs = logid;
556}
557
558int shiplog_getIdOfLogOfType( const char *type, int selectedLog )
559{
560 int i, n = 0;
561 ntime_t t = ntime_get();
562
563 for ( i=shipLog.nlogs-1; i>=0; i-- ) {
564 if ( (shipLog.removeAfter[i] > 0) && (shipLog.removeAfter[i] < t) ) {
565 /* log expired, so remove (which sets id to -1) */
566 shiplog_delete(shipLog.idList[i]);
567 }
568 if ( ( shipLog.idList[i] >= 0 )
569 && ((type == NULL) || ( strcmp(type, shipLog.typeList[i]) == 0 ) ) ) {
570 if ( n == selectedLog )
571 break;
572 n++;
573 }
574 }
575 if ( i>=0 )
576 i = shipLog.idList[i];
577 return i; /* -1 if not found */
578}
579
586{
587 ShipLogEntry *tmp;
588 /* remove this entry */
589 if ( e->prev != NULL)
590 ((ShipLogEntry*)e->prev)->next = e->next;
591 if ( e->next != NULL )
592 ((ShipLogEntry*)e->next)->prev = e->prev;
593 if ( shipLog.head == e )
594 shipLog.head = e->next;
595 if ( shipLog.tail == e )
596 shipLog.tail = e->prev;
597 free(e->msg);
598 tmp=e;
599 e=(ShipLogEntry*)e->next;
600 free(tmp);
601 return e;
602}
603
607void shiplog_listLog( int logid,
608 const char *type,int *nentries, char ***logentries, int incempty )
609{
610 int i,n = 0,all = 0;
611 char **entries = NULL;
612 ShipLogEntry *e, *use;
613 char buf[5000];
614 int pos;
615 e = shipLog.head;
616 if ( logid == LOG_ID_ALL ) {
617 all = 1;
618 }
619 while ( e != NULL ) {
620 use = NULL;
621 if ( logid == LOG_ID_ALL ) {
622 if ( all ) { /* add the log */
623 if ( e->id >= 0)
624 use=e;
625 } else { /* see if this log is of type */
626 for ( i=0; i<shipLog.nlogs; i++ ) {
627 if ( ( shipLog.idList[i] >= 0 )
628 && ( e->id == shipLog.idList[i] )
629 && ( strcmp(shipLog.typeList[i], type) == 0 ) ) {
630 /* the type matches current messages */
631 use = e;
632 break; /* there should only be 1 log of this type and id. */
633 }
634 }
635 }
636 } else { /* just this particular log*/
637 if ( e->id == logid ) {
638 use = e;
639 }
640 }
641 if ( use != NULL ) {
642 n++;
643 entries = realloc(entries, sizeof(char*) * n);
644 ntime_prettyBuf(buf, sizeof(buf), use->time, 2);
645 pos = strlen(buf);
646 pos += scnprintf(&buf[pos], sizeof(buf)-pos, ": %s", use->msg);
647 entries[n-1] = strdup(buf);
648 }
649
650 e = e -> next;
651 }
652 (void)pos;
653 if ( ( n == 0 ) && ( incempty != 0 ) ) {
654 /*empty list, so add "Empty" */
655 n = 1;
656 entries = realloc(entries,sizeof(char*));
657 entries[0] = strdup(_("Empty"));
658 }
659 *logentries = entries;
660 *nentries = n;
661}
662
668int shiplog_getID( const char *idstr )
669{
670 int id = -1;
671 int i;
672 for ( i=0; i<shipLog.nlogs; i++ ) {
673 if ( ( ( shipLog.idstrList[i] == NULL ) && ( idstr == NULL) )
674 || ( ( shipLog.idstrList[i] != NULL ) && ( idstr != NULL )
675 && ( strcmp(idstr, shipLog.idstrList[i]) == 0 ) ) ) {
676 id = shipLog.idList[i];
677 break;
678 }
679 }
680 return id;
681}
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition nstring.c:99
ntime_t ntime_get(void)
Gets the current time.
Definition ntime.c:108
void ntime_prettyBuf(char *str, int max, ntime_t t, int d)
Gets the time in a pretty human readable format filling a preset buffer.
Definition ntime.c:188
void shiplog_new(void)
Set up the shiplog.
Definition shiplog.c:370
int shiplog_appendByID(int logid, const char *msg)
Adds to the log file.
Definition shiplog.c:202
static ShipLogEntry * shiplog_removeEntry(ShipLogEntry *e)
removes an entry from the log
Definition shiplog.c:585
int shiplog_load(xmlNodePtr parent)
Loads the logfiile.
Definition shiplog.c:422
void shiplog_listLog(int logid, const char *type, int *nentries, char ***logentries, int incempty)
Get all log entries matching logid, or if logid==LOG_ID_ALL, matching type, or if type==NULL,...
Definition shiplog.c:607
void shiplog_listLogsOfType(const char *type, int *nlogs, char ***logsOut, int **logIDs, int includeAll)
Lists matching logs (which haven't expired via "removeAfter") into the provided arrays.
Definition shiplog.c:522
void shiplog_clear(void)
Clear the shiplog.
Definition shiplog.c:355
void shiplog_delete(int logid)
Deletes a log (e.g. a cancelled mission may wish to do this, or the user might).
Definition shiplog.c:267
void shiplog_setRemove(int logid, ntime_t when)
Sets the remove flag for a log - it will be removed once time increases, eg after a player takes off.
Definition shiplog.c:317
int shiplog_append(const char *idstr, const char *msg)
Appends to the log file.
Definition shiplog.c:174
void shiplog_deleteType(const char *type)
Deletes all logs of given type.
Definition shiplog.c:338
int shiplog_create(const char *idstr, const char *logname, const char *type, int overwrite, int maxLen)
Creates a new log with given title of given type.
Definition shiplog.c:54
int shiplog_getID(const char *idstr)
Checks to see if the log family exists.
Definition shiplog.c:668
static ShipLog shipLog
Definition shiplog.c:36
ShipLogEntry * head
Definition shiplog.c:31
ShipLogEntry * tail
Definition shiplog.c:32