add support for first last
This commit is contained in:
parent
c314f65feb
commit
9d619c44f3
@ -1,6 +1,7 @@
|
|||||||
#ifndef EDAPI_JOURNAL_FILE_H
|
#ifndef EDAPI_JOURNAL_FILE_H
|
||||||
#define EDAPI_JOURNAL_FILE_H
|
#define EDAPI_JOURNAL_FILE_H
|
||||||
|
|
||||||
|
#include <edapi/journal/entry.h>
|
||||||
#include <edapi/error.h>
|
#include <edapi/error.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
@ -25,7 +26,11 @@ EDErrorCode ed_journal_file_parse_filename(
|
|||||||
* function fails if the given file cannot be opened. Loading
|
* function fails if the given file cannot be opened. Loading
|
||||||
* entries is a time consuming task (especially with multiple
|
* entries is a time consuming task (especially with multiple
|
||||||
* files in a journal), so this function only peeks the first
|
* files in a journal), so this function only peeks the first
|
||||||
* few entries to figure out game version and CMDR name.
|
* few entries to figure out game version and CMDR name, and
|
||||||
|
* it peeks the last entry to determine date range of the
|
||||||
|
* journal file.
|
||||||
|
*
|
||||||
|
* To fully load all entries call ed_journal_file_load().
|
||||||
*/
|
*/
|
||||||
EDErrorCode ed_journal_file_open(EDJournalFile *file,
|
EDErrorCode ed_journal_file_open(EDJournalFile *file,
|
||||||
char const *filename,
|
char const *filename,
|
||||||
@ -42,6 +47,9 @@ gchar const *ed_journal_file_get_commander(EDJournalFile *self);
|
|||||||
|
|
||||||
gchar const *ed_journal_file_get_gameversion(EDJournalFile *self);
|
gchar const *ed_journal_file_get_gameversion(EDJournalFile *self);
|
||||||
|
|
||||||
|
EDJournalEntry *ed_journal_file_get_first(EDJournalFile *self);
|
||||||
|
EDJournalEntry *ed_journal_file_get_last(EDJournalFile *self);
|
||||||
|
|
||||||
gint ed_journal_file_compare(EDJournalFile *lhs, EDJournalFile *rhs);
|
gint ed_journal_file_compare(EDJournalFile *lhs, EDJournalFile *rhs);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
@ -13,6 +13,9 @@ typedef struct {
|
|||||||
GList *entries;
|
GList *entries;
|
||||||
gchar *commander;
|
gchar *commander;
|
||||||
gchar *gameversion;
|
gchar *gameversion;
|
||||||
|
|
||||||
|
EDJournalEntry *first;
|
||||||
|
EDJournalEntry *last;
|
||||||
} EDJournalFilePrivate;
|
} EDJournalFilePrivate;
|
||||||
|
|
||||||
struct _EDJournalFile {
|
struct _EDJournalFile {
|
||||||
@ -31,6 +34,17 @@ G_DEFINE_TYPE_EXTENDED(
|
|||||||
G_ADD_PRIVATE(EDJournalFile)
|
G_ADD_PRIVATE(EDJournalFile)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static void ed_journal_file_dispose(GObject *obj)
|
||||||
|
{
|
||||||
|
EDJournalFile *self = ED_JOURNALFILE(obj);
|
||||||
|
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
|
||||||
|
|
||||||
|
g_clear_object(&p->first);
|
||||||
|
g_clear_object(&p->last);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(ed_journal_file_parent_class)->dispose(obj);
|
||||||
|
}
|
||||||
|
|
||||||
static void ed_journal_file_finalize(GObject *obj)
|
static void ed_journal_file_finalize(GObject *obj)
|
||||||
{
|
{
|
||||||
EDJournalFile *self = ED_JOURNALFILE(obj);
|
EDJournalFile *self = ED_JOURNALFILE(obj);
|
||||||
@ -64,6 +78,7 @@ static void ed_journal_file_finalize(GObject *obj)
|
|||||||
|
|
||||||
static void ed_journal_file_class_init(EDJournalFileClass *klass)
|
static void ed_journal_file_class_init(EDJournalFileClass *klass)
|
||||||
{
|
{
|
||||||
|
G_OBJECT_CLASS(klass)->dispose = ed_journal_file_dispose;
|
||||||
G_OBJECT_CLASS(klass)->finalize = ed_journal_file_finalize;
|
G_OBJECT_CLASS(klass)->finalize = ed_journal_file_finalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,6 +259,183 @@ ed_journal_file_parse_commander(EDJournalFile *self,
|
|||||||
return ed_error_success;
|
return ed_error_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static EDErrorCode ed_journal_file_read_first_(EDJournalFile *self)
|
||||||
|
{
|
||||||
|
EDErrorCode ret = ed_error_internal;
|
||||||
|
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
|
||||||
|
|
||||||
|
GFile *file = NULL;
|
||||||
|
GFileInputStream *stream = NULL;
|
||||||
|
GDataInputStream *reader = NULL;
|
||||||
|
EDJournalEntry *entry = NULL;
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
gsize linelen = 0;
|
||||||
|
|
||||||
|
file = g_file_new_for_path(p->filename);
|
||||||
|
goto_if_true(file == NULL, done);
|
||||||
|
|
||||||
|
stream = g_file_read(file, NULL, NULL);
|
||||||
|
if (stream == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader = g_data_input_stream_new(G_INPUT_STREAM(stream));
|
||||||
|
if (reader == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (TRUE) {
|
||||||
|
line = g_data_input_stream_read_line_utf8(
|
||||||
|
reader, &linelen, NULL, NULL
|
||||||
|
);
|
||||||
|
goto_if_true(line == NULL, done);
|
||||||
|
|
||||||
|
/* ignore empty lines
|
||||||
|
*/
|
||||||
|
g_strchomp(line);
|
||||||
|
if (strlen(line) <= 0) {
|
||||||
|
g_free(line);
|
||||||
|
line = NULL;
|
||||||
|
linelen = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = ed_journal_entry_new_parse(line, NULL);
|
||||||
|
|
||||||
|
g_free(line);
|
||||||
|
line = NULL;
|
||||||
|
linelen = 0;
|
||||||
|
|
||||||
|
if (entry == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_object(&p->first);
|
||||||
|
p->first = g_object_ref(entry);
|
||||||
|
|
||||||
|
g_clear_object(&entry);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret = ed_error_success;
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
g_clear_object(&reader);
|
||||||
|
g_clear_object(&stream);
|
||||||
|
g_clear_object(&file);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EDErrorCode ed_journal_file_read_last_(EDJournalFile *self)
|
||||||
|
{
|
||||||
|
EDErrorCode ret = ed_error_internal;
|
||||||
|
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
|
||||||
|
|
||||||
|
GFile *file = NULL;
|
||||||
|
GFileInputStream *stream = NULL;
|
||||||
|
GDataInputStream *reader = NULL;
|
||||||
|
EDJournalEntry *entry = NULL;
|
||||||
|
|
||||||
|
goffset end = 0, pos = 0;
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
char *last = NULL;
|
||||||
|
gsize linelen = 0;
|
||||||
|
|
||||||
|
file = g_file_new_for_path(p->filename);
|
||||||
|
goto_if_true(file == NULL, done);
|
||||||
|
|
||||||
|
stream = g_file_read(file, NULL, NULL);
|
||||||
|
if (stream == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader = g_data_input_stream_new(G_INPUT_STREAM(stream));
|
||||||
|
if (reader == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_seekable_seek(G_SEEKABLE(stream), 0, G_SEEK_END, NULL, NULL)) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = g_seekable_tell(G_SEEKABLE(stream));
|
||||||
|
pos = end;
|
||||||
|
|
||||||
|
while (TRUE) {
|
||||||
|
/* arbitrary */
|
||||||
|
pos = end - 100;
|
||||||
|
if (pos <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_seekable_seek(G_SEEKABLE(stream), pos, G_SEEK_SET,
|
||||||
|
NULL, NULL)) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to read last line if we accidentally seeked over two
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
line = g_data_input_stream_read_line_utf8(
|
||||||
|
reader, &linelen, NULL, NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (line == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ignore empty lines
|
||||||
|
*/
|
||||||
|
g_strchomp(line);
|
||||||
|
if (strlen(line) <= 0) {
|
||||||
|
g_free(line);
|
||||||
|
line = NULL;
|
||||||
|
linelen = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(last);
|
||||||
|
last = line;
|
||||||
|
} while (TRUE);
|
||||||
|
|
||||||
|
if (last == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = ed_journal_entry_new_parse(last, NULL);
|
||||||
|
|
||||||
|
g_free(last);
|
||||||
|
last = NULL;
|
||||||
|
|
||||||
|
if (entry == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_object(&p->last);
|
||||||
|
p->last = g_object_ref(entry);
|
||||||
|
|
||||||
|
g_clear_object(&entry);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ed_error_success;
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
g_clear_object(&reader);
|
||||||
|
g_clear_object(&stream);
|
||||||
|
g_clear_object(&file);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static EDErrorCode
|
static EDErrorCode
|
||||||
ed_journal_file_load_(EDJournalFile *self,
|
ed_journal_file_load_(EDJournalFile *self,
|
||||||
GError **error,
|
GError **error,
|
||||||
@ -348,6 +540,10 @@ EDErrorCode ed_journal_file_open(EDJournalFile *file,
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* files may be empty */
|
||||||
|
ed_journal_file_read_first_(file);
|
||||||
|
ed_journal_file_read_last_(file);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,6 +574,20 @@ gchar const *ed_journal_file_get_gameversion(EDJournalFile *self)
|
|||||||
return p->gameversion;
|
return p->gameversion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EDJournalEntry *ed_journal_file_get_first(EDJournalFile *self)
|
||||||
|
{
|
||||||
|
return_if_true(self == NULL, NULL);
|
||||||
|
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
|
||||||
|
return p->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
EDJournalEntry *ed_journal_file_get_last(EDJournalFile *self)
|
||||||
|
{
|
||||||
|
return_if_true(self == NULL, NULL);
|
||||||
|
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
|
||||||
|
return p->last;
|
||||||
|
}
|
||||||
|
|
||||||
gint ed_journal_file_compare(EDJournalFile *lhs, EDJournalFile *rhs)
|
gint ed_journal_file_compare(EDJournalFile *lhs, EDJournalFile *rhs)
|
||||||
{
|
{
|
||||||
return_if_true(lhs == NULL || rhs == NULL, 0);
|
return_if_true(lhs == NULL || rhs == NULL, 0);
|
||||||
|
4
lib/tests/Journal.2024-04-18T061507.01.log
Normal file
4
lib/tests/Journal.2024-04-18T061507.01.log
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{ "timestamp":"2023-04-18T04:14:56Z", "event":"Fileheader", "part":1, "language":"English/UK", "Odyssey":true, "gameversion":"4.0.0.1477", "build":"r291050/r0 " }
|
||||||
|
{ "timestamp":"2023-04-18T04:15:39Z", "event":"Commander", "FID":"F123456", "Name":"DeiMuata" }
|
||||||
|
{ "timestamp":"2023-04-18T05:15:39Z", "event":"Something" }
|
||||||
|
{ "timestamp":"2023-04-18T06:15:39Z", "event":"Shutdown" }
|
@ -146,6 +146,34 @@ static void test_valid_peek(void **state)
|
|||||||
g_clear_object(&file);
|
g_clear_object(&file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_first_last(void **state)
|
||||||
|
{
|
||||||
|
char const *filename = "Journal.2024-04-18T061507.01.log";
|
||||||
|
|
||||||
|
EDJournalFile *file = NULL;
|
||||||
|
EDErrorCode ret = 0;
|
||||||
|
EDJournalEntry *e = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
file = ed_journal_file_new();
|
||||||
|
assert_non_null(file);
|
||||||
|
|
||||||
|
ret = ed_journal_file_open(file, filename, &error);
|
||||||
|
|
||||||
|
assert_null(error);
|
||||||
|
assert_int_equal(ret, ed_error_success);
|
||||||
|
|
||||||
|
e = ed_journal_file_get_first(file);
|
||||||
|
assert_non_null(e);
|
||||||
|
assert_true(ed_journal_entry_is(e, "Fileheader"));
|
||||||
|
|
||||||
|
e = ed_journal_file_get_last(file);
|
||||||
|
assert_non_null(e);
|
||||||
|
assert_true(ed_journal_entry_is(e, "Shutdown"));
|
||||||
|
|
||||||
|
g_clear_object(&file);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int ac, char **av)
|
int main(int ac, char **av)
|
||||||
{
|
{
|
||||||
static const struct CMUnitTest tests[] = {
|
static const struct CMUnitTest tests[] = {
|
||||||
@ -155,6 +183,7 @@ int main(int ac, char **av)
|
|||||||
cmocka_unit_test(test_new_datetime),
|
cmocka_unit_test(test_new_datetime),
|
||||||
cmocka_unit_test(test_old_datetime),
|
cmocka_unit_test(test_old_datetime),
|
||||||
cmocka_unit_test(test_valid_peek),
|
cmocka_unit_test(test_valid_peek),
|
||||||
|
cmocka_unit_test(test_first_last),
|
||||||
};
|
};
|
||||||
|
|
||||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user