From cc3a184adfcf08744de1caf930430300656349a2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 6 Mar 2014 19:27:28 -0800 Subject: [PATCH] Add initial parser for git trees It doesn't actually parse the files themselves, but it does walk the object tree and print out the dives and trips it finds. Signed-off-by: Linus Torvalds Signed-off-by: Dirk Hohndel --- dive.h | 3 + file.c | 4 + load-git.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ save-git.c | 2 +- subsurface.pro | 1 + 5 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 load-git.c diff --git a/dive.h b/dive.h index a87d05d7d..6d6edaae9 100644 --- a/dive.h +++ b/dive.h @@ -662,6 +662,8 @@ static inline struct dive *getDiveById(int id) extern "C" { #endif +extern int report_error(const char *fmt, ...); + extern struct dive *find_dive_including(timestamp_t when); extern bool dive_within_time_range(struct dive *dive, timestamp_t when, timestamp_t offset); struct dive *find_dive_n_near(timestamp_t when, int n, timestamp_t offset); @@ -686,6 +688,7 @@ extern void save_dives_logic(const char *filename, bool select_only); extern void save_dive(FILE *f, struct dive *dive); extern void export_dives_uddf(const char *filename, const bool selected); extern int git_save_dives(int fd, bool select_only); +extern int git_load_dives(char *where); extern int subsurface_rename(const char *path, const char *newpath); extern int subsurface_open(const char *path, int oflags, mode_t mode); diff --git a/file.c b/file.c index 302989408..826f3eb51 100644 --- a/file.c +++ b/file.c @@ -356,6 +356,10 @@ static void parse_file_buffer(const char *filename, struct memblock *mem, char * if (!mem->size || !mem->buffer) return; + if (mem->size > 3 && !memcmp(mem->buffer, "git", 3)) { + git_load_dives(mem->buffer); + return; + } parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, NULL, error); } diff --git a/load-git.c b/load-git.c new file mode 100644 index 000000000..9a1b4708d --- /dev/null +++ b/load-git.c @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dive.h" +#include "device.h" +#include "membuffer.h" + +#define GIT_WALK_OK 0 +#define GIT_WALK_SKIP 1 + +static struct dive *active_dive; +static dive_trip_t *active_trip; + +static struct dive *create_new_dive(timestamp_t when) +{ + struct tm tm; + struct dive *dive = alloc_dive(); + + /* We'll fill in more data from the dive file */ + dive->when = when; + + utc_mkdate(when, &tm); + report_error("New dive: %04u-%02u-%02u %02u:%02u:%02u", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + return dive; +} + +static dive_trip_t *create_new_trip(int yyyy, int mm, int dd) +{ + dive_trip_t *trip = calloc(1, sizeof(dive_trip_t)); + struct tm tm = { 0 }; + + /* We'll fill in the real data from the trip descriptor file */ + tm.tm_year = yyyy; + tm.tm_mon = mm-1; + tm.tm_mday = dd; + trip->when = utc_mktime(&tm); + + report_error("New trip: %04u-%02u-%02u", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + + return trip; +} + +static bool validate_date(int yyyy, int mm, int dd) +{ + return yyyy > 1970 && yyyy < 3000 && + mm > 0 && mm < 13 && + dd > 0 && dd < 32; +} + +static bool validate_time(int h, int m, int s) +{ + return h >= 0 && h < 24 && + m >= 0 && m < 60 && + s >=0 && s <= 60; +} + +/* + * Dive trip directory, name is 'nn-alphabetic[~hex]' + */ +static int dive_trip_directory(const char *root, const char *name) +{ + int yyyy = -1, mm = -1, dd = -1; + + if (sscanf(root, "%d/%d", &yyyy, &mm) != 2) + return GIT_WALK_SKIP; + dd = atoi(name); + if (!validate_date(yyyy, mm, dd)) + return GIT_WALK_SKIP; + active_trip = create_new_trip(yyyy, mm, dd); + return GIT_WALK_OK; +} + +/* + * Dive directory, name is [[yyyy-]mm-]nn-ddd-hh:mm:ss[~hex], + * and 'timeoff' points to what should be the time part of + * the name (the first digit of the hour). + * + * The root path will be of the form yyyy/mm[/tripdir], + */ +static int dive_directory(const char *root, const char *name, int timeoff) +{ + int yyyy = -1, mm = -1, dd = -1; + int h, m, s; + int mday_off = timeoff - 7; + int month_off = mday_off - 3; + int year_off = month_off - 5; + struct tm tm; + + /* There has to be a mday */ + if (mday_off < 0) + return GIT_WALK_SKIP; + if (name[timeoff-1] != '-') + return GIT_WALK_SKIP; + + /* Get the time of day */ + if (sscanf(name+timeoff, "%d:%d:%d", &h, &m, &s) != 3) + return GIT_WALK_SKIP; + if (!validate_time(h, m, s)) + return GIT_WALK_SKIP; + + /* + * Get the date. The day of the month is in the dive directory + * name, the year and month might be in the path leading up + * to it. + */ + dd = atoi(name + mday_off); + if (year_off < 0) { + if (sscanf(root, "%d/%d", &yyyy, &mm) != 2) + return GIT_WALK_SKIP; + } else + yyyy = atoi(name + year_off); + if (month_off >= 0) + mm = atoi(name + month_off); + + if (!validate_date(yyyy, mm, dd)) + return GIT_WALK_SKIP; + + /* Ok, close enough. We've gotten sufficient information */ + memset(&tm, 0, sizeof(tm)); + tm.tm_hour = h; + tm.tm_min = m; + tm.tm_sec = s; + tm.tm_year = yyyy - 1900; + tm.tm_mon = mm-1; + tm.tm_mday = dd; + + active_dive = create_new_dive(utc_mktime(&tm)); + return GIT_WALK_OK; +} + +/* + * Return the length of the string without the unique part. + */ +static int nonunique_length(const char *str) +{ + int len = 0; + + for (;;) { + char c = *str++; + if (!c || c == '~') + return len; + len++; + } +} + +/* + * When hitting a directory node, we have a couple of cases: + * + * - It's just a date entry - all numeric (either year or month): + * + * [yyyy|mm] + * + * We don't do anything with these, we just traverse into them. + * The numeric data will show up as part of the full path when + * we hit more interesting entries. + * + * - It's a trip directory. The name will be of the form + * + * nn-alphabetic[~hex] + * + * where 'nn' is the day of the month (year and month will be + * encoded in the path leading up to this). + * + * - It's a dive directory. The name will be of the form + * + * [[yyyy-]mm-]nn-ddd-hh:mm:ss[~hex] + * + * which describes the date and time of a dive (yyyy and mm + * are optional, and may be encoded in the path leading up to + * the dive). + * + * - It's some random non-dive-data directory. + * + * Subsurface doesn't create these yet, but maybe we'll encode + * pictures etc. If it doesn't match the above patterns, we'll + * ignore them for dive loading purposes, and not even recurse + * into them. + */ +static int walk_tree_directory(const char *root, const git_tree_entry *entry) +{ + const char *name = git_tree_entry_name(entry); + int digits = 0, len; + char c; + + while (isdigit(c = name[digits])) + digits++; + + /* Doesn't start with two or four digits? Skip */ + if (digits != 4 && digits != 2) + return GIT_WALK_SKIP; + + /* Only digits? Do nothing, but recurse into it */ + if (!c) + return GIT_WALK_OK; + + /* All valid cases need to have a slash following */ + if (c != '-') + return GIT_WALK_SKIP; + + /* Do a quick check for a common dive case */ + len = nonunique_length(name); + + /* + * We know the len is at least 3, because we had at least + * two digits and a dash + */ + if (name[len-3] == ':') + return dive_directory(root, name, len-8); + + if (digits != 2) + return GIT_WALK_SKIP; + + return dive_trip_directory(root, name); +} + +static int walk_tree_file(const char *root, const git_tree_entry *entry) +{ + /* Parse actual dive/trip data here */ + return GIT_WALK_OK; +} + +static int walk_tree_cb(const char *root, const git_tree_entry *entry, void *payload) +{ + git_filemode_t mode = git_tree_entry_filemode(entry); + + if (mode == GIT_FILEMODE_TREE) + return walk_tree_directory(root, entry); + + return walk_tree_file(root, entry); +} + +static int load_dives_from_tree(git_repository *repo, git_tree *tree) +{ + git_tree_walk(tree, GIT_TREEWALK_PRE, walk_tree_cb, NULL); + return 0; +} + +static int do_git_load(git_repository *repo, const char *branch) +{ + int ret; + git_reference *ref; + git_object *tree; + + ret = git_branch_lookup(&ref, repo, branch, GIT_BRANCH_LOCAL); + if (ret) + return report_error("Unable to look up branch '%s'", branch); + if (git_reference_peel(&tree, ref, GIT_OBJ_TREE)) + return report_error("Could not look up tree of branch '%s'", branch); + ret = load_dives_from_tree(repo, (git_tree *) tree); + git_object_free(tree); + return ret; +} + +int git_load_dives(char *where) +{ + int ret, len; + git_repository *repo; + char *loc, *branch; + + /* Jump over the "git" marker */ + loc = where + 3; + while (isspace(*loc)) + loc++; + + /* Trim whitespace from the end */ + len = strlen(loc); + while (len && isspace(loc[len-1])) + loc[--len] = 0; + + /* Find a branch name if there is any */ + branch = strrchr(loc, ':'); + if (branch) + *branch++ = 0; + + if (git_repository_open(&repo, loc)) + return report_error("Unable to open git repository at '%s' (branch '%s')", loc, branch); + + ret = do_git_load(repo, branch); + git_repository_free(repo); + return ret; +} diff --git a/save-git.c b/save-git.c index e2950d42c..0f9dea95e 100644 --- a/save-git.c +++ b/save-git.c @@ -344,7 +344,7 @@ static void create_dive_buffer(struct dive *dive, struct membuffer *b) save_dive_temperature(b, dive); } -static int report_error(const char *fmt, ...) +int report_error(const char *fmt, ...) { struct membuffer b = { 0 }; VA_BUF(&b, fmt); diff --git a/subsurface.pro b/subsurface.pro index 803930e0f..cca3e3ec5 100644 --- a/subsurface.pro +++ b/subsurface.pro @@ -84,6 +84,7 @@ SOURCES = \ file.c \ gettextfromc.cpp \ libdivecomputer.c \ + load-git.c \ main.cpp \ membuffer.c \ parse-xml.c \