summaryrefslogtreecommitdiffstats
path: root/load-git.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2014-03-06 19:27:28 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2014-03-08 07:46:50 -0800
commitcc3a184adfcf08744de1caf930430300656349a2 (patch)
treec84150e3110222adcb424f50b899cdd2df4b06ca /load-git.c
parent6d0011947b0675de21e4010dedb14a94cd7f7873 (diff)
downloadsubsurface-cc3a184adfcf08744de1caf930430300656349a2.tar.gz
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 <torvalds@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'load-git.c')
-rw-r--r--load-git.c294
1 files changed, 294 insertions, 0 deletions
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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <git2.h>
+
+#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;
+}