naev 0.12.5
shiplog.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
4
10
11#include "shiplog.h"
12#include <inttypes.h>
13
14/*Hold a single log entry - a double linked list*/
15typedef struct {
16 int id;
17 ntime_t time;
18 char *msg;
19 void *next;
20 void *prev;
22
23/* Holding global information about the log. */
24typedef struct {
25 int *idList;
26 char **typeList;
27 char **nameList;
28 ntime_t *removeAfter;
29 char **idstrList;
30 int *maxLen;
31 int nlogs;
34} ShipLog;
35
37
38/*
39 * Prototypes.
40 */
42
56int shiplog_create( const char *idstr, const char *logname, const char *type,
57 int overwrite, int maxLen )
58{
59 ShipLogEntry *e;
60 int i, id, indx;
61 indx = shipLog.nlogs;
62
63 id = LOG_ID_INVALID;
64 if (overwrite == 1) {
65 /* check to see whether this idstr or logname and type has been created
66 * before, and if so, remove all entries of that logid */
67 if (idstr != NULL) {
68 /* find the matching logid for this idstr */
69 for (i = 0; i < shipLog.nlogs; i++) {
70 if (( shipLog.idstrList[i] != NULL ) &&
71 ( strcmp( shipLog.idstrList[i], idstr ) == 0 )) {
72 /* matching idstr found. */
73 id = shipLog.idList[i];
74 indx = i;
75 break;
76 }
77 }
78 } else {
79 for (i = 0; i < shipLog.nlogs; i++) {
80 if (( strcmp( type, shipLog.typeList[i] ) == 0 ) &&
81 ( strcmp( logname, shipLog.nameList[i] ) == 0 )) {
82 id = shipLog.idList[i];
83 indx = i;
84 break;
85 }
86 }
87 }
88 if (i < shipLog.nlogs) { /* prev id found - so remove all log entries of
89 this type. */
90 e = shipLog.head;
91 while (e != NULL) {
92 if (e->id == id) {
93 /* remove this entry */
94 e = shiplog_removeEntry( e );
95 } else {
96 e = (ShipLogEntry *)e->next;
97 }
98 }
99 shipLog.maxLen[i] = maxLen;
100 }
101 } else if (overwrite == 2) {
102 /* check to see whether this type has been created before, and if so,
103 * remove all entries. */
104 int found = 0;
105 id = LOG_ID_INVALID;
106 for (i = 0; i < shipLog.nlogs; i++) {
107 if (( ( idstr != NULL ) && ( shipLog.idstrList[i] != NULL ) &&
108 ( strcmp( idstr, shipLog.idstrList[i] ) == 0 ) ) ||
109 ( ( idstr == NULL ) &&
110 ( strcmp( type, shipLog.typeList[i] ) == 0 ) )) {
111 e = shipLog.head;
112 while (e != NULL) {
113 if (e->id == shipLog.idList[i])
114 e = shiplog_removeEntry( e );
115 else
116 e = e->next;
117 }
118 if (found == 0) { /* This is the first entry of this type */
119 found = 1;
120 id = shipLog.idList[i];
121 indx = i;
122 free( shipLog.nameList[i] );
123 shipLog.nameList[i] = strdup( logname );
124 shipLog.maxLen[i] = maxLen;
125 } else { /* a previous entry of this type as been found, so just
126 invalidate this logid. */
127 shipLog.idList[i] = LOG_ID_INVALID;
128 free( shipLog.idstrList[i] );
129 shipLog.idstrList[i] = NULL;
130 }
131 }
132 }
133 }
134
135 if (( indx == shipLog.nlogs ) && ( idstr != NULL )) {
136 /* see if existing log with this idstr exists, if so, append to it */
137 for (i = 0; i < shipLog.nlogs; i++) {
138 if (( shipLog.idstrList[i] != NULL ) &&
139 ( strcmp( idstr, shipLog.idstrList[i] ) == 0 )) {
140 id = shipLog.idList[i];
141 indx = i;
142 shipLog.maxLen[i] = maxLen;
143 break;
144 }
145 }
146 }
147 if (indx == shipLog.nlogs) {
148 /* create a new id for this log */
149 id = -1;
150 for (i = 0; i < shipLog.nlogs; i++) { /* get maximum id */
151 if (shipLog.idList[i] > id)
152 id = shipLog.idList[i];
153 }
154 id++;
155 shipLog.nlogs++;
156 shipLog.idList = realloc( shipLog.idList, sizeof( int ) * shipLog.nlogs );
157 shipLog.nameList =
158 realloc( shipLog.nameList, sizeof( char * ) * shipLog.nlogs );
159 shipLog.typeList =
160 realloc( shipLog.typeList, sizeof( char * ) * shipLog.nlogs );
161 shipLog.removeAfter =
162 realloc( shipLog.removeAfter, sizeof( ntime_t ) * shipLog.nlogs );
163 shipLog.idstrList =
164 realloc( shipLog.idstrList, sizeof( char * ) * shipLog.nlogs );
165 shipLog.maxLen = realloc( shipLog.maxLen, sizeof( int ) * shipLog.nlogs );
166 shipLog.removeAfter[indx] = 0;
167 shipLog.idList[indx] = id;
168 shipLog.nameList[indx] = strdup( logname );
169 shipLog.typeList[indx] = strdup( type );
170 shipLog.maxLen[indx] = maxLen;
171 shipLog.idstrList[indx] = ( idstr == NULL ) ? NULL : strdup( idstr );
172 }
173 return id;
174}
175
183int shiplog_append( const char *idstr, const char *msg )
184{
185 int i, id;
186 for (i = 0; i < shipLog.nlogs; i++) {
187 if (( ( idstr == NULL ) && ( shipLog.idstrList[i] == NULL ) ) ||
188 ( ( idstr != NULL ) && ( shipLog.idstrList[i] != NULL ) &&
189 ( strcmp( idstr, shipLog.idstrList[i] ) == 0 ) )) {
190 break;
191 }
192 }
193 if (i == shipLog.nlogs) {
194 WARN( _( "Warning - log not found: creating it" ) );
195 id = shiplog_create(
196 idstr, _( "Please report this log as an error to github.com/naev" ),
197 idstr != NULL ? idstr : "", 0, 0 );
198 } else {
199 id = shipLog.idList[i];
200 }
201 return shiplog_appendByID( id, msg );
202}
203
211int shiplog_appendByID( int logid, const char *msg )
212{
213 ShipLogEntry *e;
214 ntime_t now = ntime_get();
215 int i, maxLen = 0;
216
217 if (logid < 0)
218 return -1;
219
220 /* Check that the log hasn't already been added (e.g. if reloading) */
221 e = shipLog.head;
222 /* check for identical logs */
223 while (e != NULL) {
224 if (e->time != now) { /* logs are created in chronological order */
225 break;
226 }
227 if (( logid == e->id ) && ( strcmp( e->msg, msg ) == 0 )) {
228 /* Identical log already exists */
229 return 0;
230 }
231 e = e->next;
232 }
233 if (( e = calloc( 1, sizeof( ShipLogEntry ) ) ) == NULL) {
234 WARN( _( "Error creating new log entry - crash imminent!\n" ) );
235 return -1;
236 }
237 e->next = shipLog.head;
238 shipLog.head = e;
239 if (shipLog.tail == NULL) /* first entry - point to both head and tail.*/
240 shipLog.tail = e;
241 if (e->next != NULL)
242 ( (ShipLogEntry *)e->next )->prev = (void *)e;
243 e->id = logid;
244 e->msg = strdup( msg );
245 e->time = now;
246 for (i = 0; i < shipLog.nlogs; i++) {
247 if (shipLog.idList[i] == logid) {
248 maxLen = shipLog.maxLen[i];
249 break;
250 }
251 }
252 if (maxLen > 0) {
253 /* prune log entries if necessary */
254 i = 0;
255 e = shipLog.head;
256 while (e != NULL) {
257 if (e->id == logid) {
258 i++;
259 if (i > maxLen)
260 e = shiplog_removeEntry( e );
261 else
262 e = (ShipLogEntry *)e->next;
263 } else {
264 e = (ShipLogEntry *)e->next;
265 }
266 }
267 }
268 return 0;
269}
270
277void shiplog_delete( int logid )
278{
279 ShipLogEntry *e, *tmp;
280 int i;
281
282 if (( logid < 0 ) && ( logid != LOG_ID_ALL ))
283 return;
284
285 e = shipLog.head;
286 while (e != NULL) {
287 if (logid == LOG_ID_ALL || logid == e->id) {
288 if (e->prev != NULL)
289 ( (ShipLogEntry *)e->prev )->next = e->next;
290 if (e->next != NULL)
291 ( (ShipLogEntry *)e->next )->prev = e->prev;
292 free( e->msg );
293 if (e == shipLog.head)
294 shipLog.head = e->next;
295 if (e == shipLog.tail)
296 shipLog.tail = e->prev;
297 tmp = e;
298 e = (ShipLogEntry *)e->next;
299 free( tmp );
300 } else {
301 e = (ShipLogEntry *)e->next;
302 }
303 }
304
305 for (i = 0; i < shipLog.nlogs; i++) {
306 if (logid == LOG_ID_ALL || logid == shipLog.idList[i]) {
307 shipLog.idList[i] = LOG_ID_INVALID;
308 free( shipLog.nameList[i] );
309 shipLog.nameList[i] = NULL;
310 free( shipLog.typeList[i] );
311 shipLog.typeList[i] = NULL;
312 free( shipLog.idstrList[i] );
313 shipLog.idstrList[i] = NULL;
314 shipLog.maxLen[i] = 0;
315 shipLog.removeAfter[i] = 0;
316 }
317 }
318}
319
330void shiplog_setRemove( int logid, ntime_t when )
331{
332 int i;
333 if (when == 0)
334 when = ntime_get();
335 else if (when < 0) /* add this to ntime */
336 when = ntime_get() - when;
337
338 for (i = 0; i < shipLog.nlogs; i++) {
339 if (shipLog.idList[i] == logid) {
340 shipLog.removeAfter[i] = when;
341 break;
342 }
343 }
344}
345
351void shiplog_deleteType( const char *type )
352{
353 int i;
354 if (type == NULL)
355 return;
356 for (i = 0; i < shipLog.nlogs; i++) {
357 if (( shipLog.idList[i] >= 0 ) &&
358 ( strcmp( type, shipLog.typeList[i] ) == 0 )) {
359 shiplog_delete( shipLog.idList[i] );
360 }
361 }
362}
363
367void shiplog_clear( void )
368{
369 shiplog_delete( LOG_ID_ALL );
370 free( shipLog.idList );
371 free( shipLog.nameList );
372 free( shipLog.typeList );
373 free( shipLog.idstrList );
374 free( shipLog.maxLen );
375 free( shipLog.removeAfter );
376 memset( &shipLog, 0, sizeof( ShipLog ) );
377}
378
382void shiplog_new( void )
383{
385}
386
387/*
388 * @brief Saves the logfiile
389 */
390int shiplog_save( xmlTextWriterPtr writer )
391{
392 int i;
393 ShipLogEntry *e;
394 ntime_t t = ntime_get();
395 xmlw_startElem( writer, "shiplog" );
396
397 for (i = 0; i < shipLog.nlogs; i++) {
398 if (shipLog.removeAfter[i] > 0 && shipLog.removeAfter[i] < t)
399 shiplog_delete( shipLog.idList[i] );
400 if (shipLog.idList[i] >= 0) {
401 xmlw_startElem( writer, "entry" );
402 xmlw_attr( writer, "id", "%d", shipLog.idList[i] );
403 xmlw_attr( writer, "t", "%s", shipLog.typeList[i] );
404 if (shipLog.removeAfter[i] != 0)
405 xmlw_attr( writer, "r", "%" PRIu64, shipLog.removeAfter[i] );
406 if (shipLog.idstrList[i] != NULL)
407 xmlw_attr( writer, "s", "%s", shipLog.idstrList[i] );
408 if (shipLog.maxLen[i] != 0)
409 xmlw_attr( writer, "m", "%d", shipLog.maxLen[i] );
410 xmlw_str( writer, "%s", shipLog.nameList[i] );
411 xmlw_endElem( writer ); /* entry */
412 }
413 }
414 e = shipLog.head;
415 while (e != NULL) {
416 if (e->id >= 0) {
417 xmlw_startElem( writer, "log" );
418 xmlw_attr( writer, "id", "%d", e->id );
419 xmlw_attr( writer, "t", "%" PRIu64, e->time );
420 xmlw_str( writer, "%s", e->msg );
421 xmlw_endElem( writer ); /* log */
422 }
423 e = (ShipLogEntry *)e->next;
424 }
425 xmlw_endElem( writer ); /* economy */
426 return 0;
427}
428
434int shiplog_load( xmlNodePtr parent )
435{
436 xmlNodePtr node, cur;
437 ShipLogEntry *e;
438 int id, i;
440
441 node = parent->xmlChildrenNode;
442 do {
443 if (xml_isNode( node, "shiplog" )) {
444 cur = node->xmlChildrenNode;
445 do {
446 if (xml_isNode( cur, "entry" )) {
447 xmlr_attr_int( cur, "id", id );
448 /* check this ID isn't already present */
449 for (i = 0; i < shipLog.nlogs; i++) {
450 if (shipLog.idList[i] == id)
451 break;
452 }
453 if (i == shipLog.nlogs) { /* a new ID */
454 shipLog.nlogs++;
455 shipLog.idList =
456 realloc( shipLog.idList, sizeof( int ) * shipLog.nlogs );
457 shipLog.nameList = realloc(
458 shipLog.nameList, sizeof( char * ) * shipLog.nlogs );
459 shipLog.typeList = realloc(
460 shipLog.typeList, sizeof( char * ) * shipLog.nlogs );
461 shipLog.removeAfter = realloc(
462 shipLog.removeAfter, sizeof( ntime_t ) * shipLog.nlogs );
463 shipLog.idstrList = realloc(
464 shipLog.idstrList, sizeof( char * ) * shipLog.nlogs );
465 shipLog.maxLen =
466 realloc( shipLog.maxLen, sizeof( int ) * shipLog.nlogs );
467 shipLog.idList[shipLog.nlogs - 1] = id;
468 xmlr_attr_strd( cur, "t",
469 shipLog.typeList[shipLog.nlogs - 1] );
470 xmlr_attr_long( cur, "r",
471 shipLog.removeAfter[shipLog.nlogs - 1] );
472 xmlr_attr_strd( cur, "s",
473 shipLog.idstrList[shipLog.nlogs - 1] );
474 xmlr_attr_int( cur, "m", shipLog.maxLen[shipLog.nlogs - 1] );
475 if (shipLog.typeList[shipLog.nlogs - 1] == NULL) {
476 shipLog.typeList[shipLog.nlogs - 1] = strdup( "No type" );
477 WARN( _( "No ID in shipLog entry" ) );
478 }
479 shipLog.nameList[shipLog.nlogs - 1] =
480 strdup( xml_raw( cur ) );
481 }
482 } else if (xml_isNode( cur, "log" )) {
483 e = calloc( 1, sizeof( ShipLogEntry ) );
484 /* put this one at the end */
485 e->prev = shipLog.tail;
486 if (shipLog.tail == NULL)
487 shipLog.head = e;
488 else
489 shipLog.tail->next = e;
490 shipLog.tail = e;
491
492 xmlr_attr_int( cur, "id", e->id );
493 xmlr_attr_long( cur, "t", e->time );
494 e->msg = strdup( xml_raw( cur ) );
495 }
496 } while (xml_nextNode( cur ));
497 }
498 } while (xml_nextNode( node ));
499 return 0;
500}
501
502void shiplog_listTypes( int *ntypes, char ***logTypes, int includeAll )
503{
504 int i, j, n = 0;
505 char **types = NULL;
506 ntime_t t = ntime_get();
507
508 if (includeAll) {
509 types = malloc( sizeof( char * ) );
510 n = 1;
511 types[0] = strdup( _( "All" ) );
512 }
513 for (i = 0; i < shipLog.nlogs; i++) {
514 if (shipLog.removeAfter[i] > 0 && shipLog.removeAfter[i] < t) {
515 /* log expired, so remove (which sets id to LOG_ID_INVALID) */
516 shiplog_delete( shipLog.idList[i] );
517 }
518 if (shipLog.idList[i] >= 0) {
519 /* log valid */
520 for (j = 0; j < n; j++) {
521 if (strcmp( shipLog.typeList[i], types[j] ) == 0)
522 break;
523 }
524 if (j == n) { /*This log type not found, so add.*/
525 n++;
526 types = realloc( types, sizeof( char * ) * n );
527 types[n - 1] = strdup( shipLog.typeList[i] );
528 }
529 }
530 }
531 *ntypes = n;
532 *logTypes = types;
533}
534
547void shiplog_listLogsOfType( const char *type, int *nlogs, char ***logsOut,
548 int **logIDs, int includeAll )
549{
550 int i, n;
551 char **logs;
552 int *logid;
553 ntime_t t = ntime_get();
554
555 n = !!includeAll;
556 logs = realloc( *logsOut, sizeof( char * ) * n );
557 logid = realloc( *logIDs, sizeof( int ) * n );
558 if (includeAll) {
559 logs[0] = strdup( _( "All" ) );
560 logid[0] = LOG_ID_ALL;
561 }
562 if (shipLog.nlogs > 0) {
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 LOG_ID_INVALID) */
566 shiplog_delete( shipLog.idList[i] );
567 }
568 if (( shipLog.idList[i] >= 0 ) &&
569 ( ( type == NULL ) ||
570 ( strcmp( type, shipLog.typeList[i] ) == 0 ) )) {
571 n++;
572 logs = realloc( logs, sizeof( char * ) * n );
573 logs[n - 1] = strdup( shipLog.nameList[i] );
574 logid = realloc( logid, sizeof( int ) * n );
575 logid[n - 1] = shipLog.idList[i];
576 }
577 }
578 }
579 *nlogs = n;
580 *logsOut = logs;
581 *logIDs = logid;
582}
583
584int shiplog_getIdOfLogOfType( const char *type, int selectedLog )
585{
586 int i, n = 0;
587 ntime_t t = ntime_get();
588
589 for (i = shipLog.nlogs - 1; i >= 0; i--) {
590 if (( shipLog.removeAfter[i] > 0 ) && ( shipLog.removeAfter[i] < t )) {
591 /* log expired, so remove (which sets id to -1) */
592 shiplog_delete( shipLog.idList[i] );
593 }
594 if (( shipLog.idList[i] >= 0 ) &&
595 ( ( type == NULL ) ||
596 ( strcmp( type, shipLog.typeList[i] ) == 0 ) )) {
597 if (n == selectedLog)
598 break;
599 n++;
600 }
601 }
602 if (i >= 0)
603 i = shipLog.idList[i];
604 return i; /* -1 if not found */
605}
606
613{
614 ShipLogEntry *tmp;
615 /* remove this entry */
616 if (e->prev != NULL)
617 ( (ShipLogEntry *)e->prev )->next = e->next;
618 if (e->next != NULL)
619 ( (ShipLogEntry *)e->next )->prev = e->prev;
620 if (shipLog.head == e)
621 shipLog.head = e->next;
622 if (shipLog.tail == e)
623 shipLog.tail = e->prev;
624 free( e->msg );
625 tmp = e;
626 e = (ShipLogEntry *)e->next;
627 free( tmp );
628 return e;
629}
630
635void shiplog_listLog( int logid, const char *type, int *nentries,
636 char ***logentries, int incempty )
637{
638 int i, n = 0, all = 0;
639 char **entries = NULL;
640 ShipLogEntry *e, *use;
641 char buf[5000];
642 int pos;
643 e = shipLog.head;
644 if (logid == LOG_ID_ALL) {
645 all = 1;
646 }
647 while (e != NULL) {
648 use = NULL;
649 if (logid == LOG_ID_ALL) {
650 if (all) { /* add the log */
651 if (e->id >= 0)
652 use = e;
653 } else { /* see if this log is of type */
654 for (i = 0; i < shipLog.nlogs; i++) {
655 if (( shipLog.idList[i] >= 0 ) &&
656 ( e->id == shipLog.idList[i] ) &&
657 ( strcmp( shipLog.typeList[i], type ) == 0 )) {
658 /* the type matches current messages */
659 use = e;
660 break; /* there should only be 1 log of this type and id. */
661 }
662 }
663 }
664 } else { /* just this particular log*/
665 if (e->id == logid) {
666 use = e;
667 }
668 }
669 if (use != NULL) {
670 n++;
671 entries = realloc( entries, sizeof( char * ) * n );
672 ntime_prettyBuf( buf, sizeof( buf ), use->time, 2 );
673 pos = strlen( buf );
674 pos += scnprintf( &buf[pos], sizeof( buf ) - pos, ": %s", use->msg );
675 entries[n - 1] = strdup( buf );
676 }
677
678 e = e->next;
679 }
680 (void)pos;
681 if (( n == 0 ) && ( incempty != 0 )) {
682 /*empty list, so add "Empty" */
683 n = 1;
684 entries = realloc( entries, sizeof( char * ) );
685 entries[0] = strdup( _( "Empty" ) );
686 }
687 *logentries = entries;
688 *nentries = n;
689}
690
696int shiplog_getID( const char *idstr )
697{
698 int id = -1;
699 int i;
700 for (i = 0; i < shipLog.nlogs; i++) {
701 if (( ( shipLog.idstrList[i] == NULL ) && ( idstr == NULL ) ) ||
702 ( ( shipLog.idstrList[i] != NULL ) && ( idstr != NULL ) &&
703 ( strcmp( idstr, shipLog.idstrList[i] ) == 0 ) )) {
704 id = shipLog.idList[i];
705 break;
706 }
707 }
708 return id;
709}
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:102
ntime_t ntime_get(void)
Gets the current time.
Definition ntime.c:113
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:194
void shiplog_new(void)
Set up the shiplog.
Definition shiplog.c:382
int shiplog_appendByID(int logid, const char *msg)
Adds to the log file.
Definition shiplog.c:211
static ShipLogEntry * shiplog_removeEntry(ShipLogEntry *e)
removes an entry from the log
Definition shiplog.c:612
int shiplog_load(xmlNodePtr parent)
Loads the logfiile.
Definition shiplog.c:434
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:635
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:547
void shiplog_clear(void)
Clear the shiplog.
Definition shiplog.c:367
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:277
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:330
int shiplog_append(const char *idstr, const char *msg)
Appends to the log file.
Definition shiplog.c:183
void shiplog_deleteType(const char *type)
Deletes all logs of given type.
Definition shiplog.c:351
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:56
int shiplog_getID(const char *idstr)
Checks to see if the log family exists.
Definition shiplog.c:696
static ShipLog shipLog
Definition shiplog.c:36
ShipLogEntry * head
Definition shiplog.c:32
ShipLogEntry * tail
Definition shiplog.c:33