mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
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:
parent
6d0011947b
commit
cc3a184adf
5 changed files with 303 additions and 1 deletions
3
dive.h
3
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);
|
||||
|
|
4
file.c
4
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);
|
||||
}
|
||||
|
|
294
load-git.c
Normal file
294
load-git.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -84,6 +84,7 @@ SOURCES = \
|
|||
file.c \
|
||||
gettextfromc.cpp \
|
||||
libdivecomputer.c \
|
||||
load-git.c \
|
||||
main.cpp \
|
||||
membuffer.c \
|
||||
parse-xml.c \
|
||||
|
|
Loading…
Reference in a new issue