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>
This commit is contained in:
Linus Torvalds 2014-03-06 19:27:28 -08:00 committed by Dirk Hohndel
parent 6d0011947b
commit cc3a184adf
5 changed files with 303 additions and 1 deletions

3
dive.h
View file

@ -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);

4
file.c
View file

@ -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);
}

294
load-git.c Normal file
View file

@ -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;
}

View file

@ -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);

View file

@ -84,6 +84,7 @@ SOURCES = \
file.c \
gettextfromc.cpp \
libdivecomputer.c \
load-git.c \
main.cpp \
membuffer.c \
parse-xml.c \