diff options
author | Tomaz Canabrava <tomaz.canabrava@intel.com> | 2015-09-02 20:52:34 -0300 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2015-10-30 10:36:48 -0700 |
commit | 4c0156e3d51b389db8eccc3fa3da4b8f248f9b13 (patch) | |
tree | 966868d29150fdba13a5a56fb4305bc432ec7a72 /load-git.c | |
parent | a0798214231c652ac6142228f5ddfc4b65c921f8 (diff) | |
download | subsurface-4c0156e3d51b389db8eccc3fa3da4b8f248f9b13.tar.gz |
Move all core-functionality to subsurface-core
And adapt a new CMakeLists.txt file for it. On the way I've also
found out that we where double-compilling a few files. I've also
set the subsurface-core as a include_path but that was just to
reduce the noise on this commit, since I plan to remove it from
the include path to make it obligatory to specify something like
include "subsurface-core/dive.h"
for the header files. Since the app is growing quite a bit we ended
up having a few different files with almost same name that did
similar things, I want to kill that (for instance Dive.h, dive.h,
PrintDive.h and such).
Signed-off-by: Tomaz Canabrava <tomaz.canabrava@intel.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'load-git.c')
-rw-r--r-- | load-git.c | 1638 |
1 files changed, 0 insertions, 1638 deletions
diff --git a/load-git.c b/load-git.c deleted file mode 100644 index 39dab4367..000000000 --- a/load-git.c +++ /dev/null @@ -1,1638 +0,0 @@ -#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 "gettext.h" - -#include "dive.h" -#include "divelist.h" -#include "device.h" -#include "membuffer.h" -#include "git-access.h" -#include "qthelperfromc.h" - -const char *saved_git_id = NULL; - -struct picture_entry_list { - void *data; - int len; - const char *hash; - struct picture_entry_list *next; -}; -struct picture_entry_list *pel = NULL; - -struct keyword_action { - const char *keyword; - void (*fn)(char *, struct membuffer *, void *); -}; -#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) - -extern degrees_t parse_degrees(char *buf, char **end); -git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry *entry); - -static void save_picture_from_git(struct picture *picture) -{ - struct picture_entry_list *pic_entry = pel; - - while (pic_entry) { - if (same_string(pic_entry->hash, picture->hash)) { - savePictureLocal(picture, pic_entry->data, pic_entry->len); - return; - } - pic_entry = pic_entry->next; - } - fprintf(stderr, "didn't find picture entry for %s\n", picture->filename); -} - -static char *get_utf8(struct membuffer *b) -{ - int len = b->len; - char *res; - - if (!len) - return NULL; - res = malloc(len+1); - if (res) { - memcpy(res, b->buffer, len); - res[len] = 0; - } - return res; -} - -static temperature_t get_temperature(const char *line) -{ - temperature_t t; - t.mkelvin = C_to_mkelvin(ascii_strtod(line, NULL)); - return t; -} - -static depth_t get_depth(const char *line) -{ - depth_t d; - d.mm = rint(1000*ascii_strtod(line, NULL)); - return d; -} - -static volume_t get_volume(const char *line) -{ - volume_t v; - v.mliter = rint(1000*ascii_strtod(line, NULL)); - return v; -} - -static weight_t get_weight(const char *line) -{ - weight_t w; - w.grams = rint(1000*ascii_strtod(line, NULL)); - return w; -} - -static pressure_t get_pressure(const char *line) -{ - pressure_t p; - p.mbar = rint(1000*ascii_strtod(line, NULL)); - return p; -} - -static int get_salinity(const char *line) -{ - return rint(10*ascii_strtod(line, NULL)); -} - -static fraction_t get_fraction(const char *line) -{ - fraction_t f; - f.permille = rint(10*ascii_strtod(line, NULL)); - return f; -} - -static void update_date(timestamp_t *when, const char *line) -{ - unsigned yyyy, mm, dd; - struct tm tm; - - if (sscanf(line, "%04u-%02u-%02u", &yyyy, &mm, &dd) != 3) - return; - utc_mkdate(*when, &tm); - tm.tm_year = yyyy - 1900; - tm.tm_mon = mm - 1; - tm.tm_mday = dd; - *when = utc_mktime(&tm); -} - -static void update_time(timestamp_t *when, const char *line) -{ - unsigned h, m, s = 0; - struct tm tm; - - if (sscanf(line, "%02u:%02u:%02u", &h, &m, &s) < 2) - return; - utc_mkdate(*when, &tm); - tm.tm_hour = h; - tm.tm_min = m; - tm.tm_sec = s; - *when = utc_mktime(&tm); -} - -static duration_t get_duration(const char *line) -{ - int m = 0, s = 0; - duration_t d; - sscanf(line, "%d:%d", &m, &s); - d.seconds = m*60+s; - return d; -} - -static enum dive_comp_type get_dctype(const char *line) -{ - for (enum dive_comp_type i = 0; i < NUM_DC_TYPE; i++) { - if (strcmp(line, divemode_text[i]) == 0) - return i; - } - return 0; -} - -static int get_index(const char *line) -{ return atoi(line); } - -static int get_hex(const char *line) -{ return strtoul(line, NULL, 16); } - -/* this is in qthelper.cpp, so including the .h file is a pain */ -extern const char *printGPSCoords(int lat, int lon); - -static void parse_dive_gps(char *line, struct membuffer *str, void *_dive) -{ - uint32_t uuid; - degrees_t latitude = parse_degrees(line, &line); - degrees_t longitude = parse_degrees(line, &line); - struct dive *dive = _dive; - struct dive_site *ds = get_dive_site_for_dive(dive); - if (!ds) { - uuid = get_dive_site_uuid_by_gps(latitude, longitude, NULL); - if (!uuid) - uuid = create_dive_site_with_gps("", latitude, longitude, dive->when); - dive->dive_site_uuid = uuid; - } else { - if (dive_site_has_gps_location(ds) && - (ds->latitude.udeg != latitude.udeg || ds->longitude.udeg != longitude.udeg)) { - const char *coords = printGPSCoords(latitude.udeg, longitude.udeg); - // we have a dive site that already has GPS coordinates - ds->notes = add_to_string(ds->notes, translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords); - free((void *)coords); - } - ds->latitude = latitude; - ds->longitude = longitude; - } - -} - -static void parse_dive_location(char *line, struct membuffer *str, void *_dive) -{ - uint32_t uuid; - char *name = get_utf8(str); - struct dive *dive = _dive; - struct dive_site *ds = get_dive_site_for_dive(dive); - if (!ds) { - uuid = get_dive_site_uuid_by_name(name, NULL); - if (!uuid) - uuid = create_dive_site(name, dive->when); - dive->dive_site_uuid = uuid; - } else { - // we already had a dive site linked to the dive - if (same_string(ds->name, "")) { - ds->name = strdup(name); - } else { - // and that dive site had a name. that's weird - if our name is different, add it to the notes - if (!same_string(ds->name, name)) - ds->notes = add_to_string(ds->notes, translate("gettextFromC", "additional name for site: %s\n"), name); - } - } - free(name); -} - -static void parse_dive_divemaster(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->divemaster = get_utf8(str); } - -static void parse_dive_buddy(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->buddy = get_utf8(str); } - -static void parse_dive_suit(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->suit = get_utf8(str); } - -static void parse_dive_notes(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->notes = get_utf8(str); } - -static void parse_dive_divesiteid(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->dive_site_uuid = get_hex(line); } - -/* - * We can have multiple tags in the membuffer. They are separated by - * NUL bytes. - */ -static void parse_dive_tags(char *line, struct membuffer *str, void *_dive) -{ - struct dive *dive = _dive; - const char *tag; - int len = str->len; - - if (!len) - return; - - /* Make sure there is a NUL at the end too */ - tag = mb_cstring(str); - for (;;) { - int taglen = strlen(tag); - if (taglen) - taglist_add_tag(&dive->tag_list, tag); - len -= taglen; - if (!len) - return; - tag += taglen+1; - len--; - } -} - -static void parse_dive_airtemp(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->airtemp = get_temperature(line); } - -static void parse_dive_watertemp(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->watertemp = get_temperature(line); } - -static void parse_dive_duration(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->duration = get_duration(line); } - -static void parse_dive_rating(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->rating = get_index(line); } - -static void parse_dive_visibility(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->visibility = get_index(line); } - -static void parse_dive_notrip(char *line, struct membuffer *str, void *_dive) -{ struct dive *dive = _dive; dive->tripflag = NO_TRIP; } - -static void parse_site_description(char *line, struct membuffer *str, void *_ds) -{ struct dive_site *ds = _ds; ds->description = strdup(mb_cstring(str)); } - -static void parse_site_name(char *line, struct membuffer *str, void *_ds) -{ struct dive_site *ds = _ds; ds->name = strdup(mb_cstring(str)); } - -static void parse_site_notes(char *line, struct membuffer *str, void *_ds) -{ struct dive_site *ds = _ds; ds->notes = strdup(mb_cstring(str)); } - -extern degrees_t parse_degrees(char *buf, char **end); -static void parse_site_gps(char *line, struct membuffer *str, void *_ds) -{ - struct dive_site *ds = _ds; - - ds->latitude = parse_degrees(line, &line); - ds->longitude = parse_degrees(line, &line); -} - -static void parse_site_geo(char *line, struct membuffer *str, void *_ds) -{ - struct dive_site *ds = _ds; - if (ds->taxonomy.category == NULL) - ds->taxonomy.category = alloc_taxonomy(); - int nr = ds->taxonomy.nr; - if (nr < TC_NR_CATEGORIES) { - struct taxonomy *t = &ds->taxonomy.category[nr]; - t->value = strdup(mb_cstring(str)); - sscanf(line, "cat %d origin %d \"", &t->category, (int *)&t->origin); - ds->taxonomy.nr++; - } -} - -/* Parse key=val parts of samples and cylinders etc */ -static char *parse_keyvalue_entry(void (*fn)(void *, const char *, const char *), void *fndata, char *line) -{ - char *key = line, *val, c; - - while ((c = *line) != 0) { - if (isspace(c) || c == '=') - break; - line++; - } - - if (c == '=') - *line++ = 0; - val = line; - - while ((c = *line) != 0) { - if (isspace(c)) - break; - line++; - } - if (c) - *line++ = 0; - - fn(fndata, key, val); - return line; -} - -static int cylinder_index, weightsystem_index; - -static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const char *value) -{ - cylinder_t *cylinder = _cylinder; - if (!strcmp(key, "vol")) { - cylinder->type.size = get_volume(value); - return; - } - if (!strcmp(key, "workpressure")) { - cylinder->type.workingpressure = get_pressure(value); - return; - } - /* This is handled by the "get_utf8()" */ - if (!strcmp(key, "description")) - return; - if (!strcmp(key, "o2")) { - cylinder->gasmix.o2 = get_fraction(value); - return; - } - if (!strcmp(key, "he")) { - cylinder->gasmix.he = get_fraction(value); - return; - } - if (!strcmp(key, "start")) { - cylinder->start = get_pressure(value); - return; - } - if (!strcmp(key, "end")) { - cylinder->end = get_pressure(value); - return; - } - if (!strcmp(key, "use")) { - cylinder->cylinder_use = cylinderuse_from_text(value); - return; - } - report_error("Unknown cylinder key/value pair (%s/%s)", key, value); -} - -static void parse_dive_cylinder(char *line, struct membuffer *str, void *_dive) -{ - struct dive *dive = _dive; - cylinder_t *cylinder = dive->cylinder + cylinder_index; - - cylinder_index++; - cylinder->type.description = get_utf8(str); - for (;;) { - char c; - while (isspace(c = *line)) - line++; - if (!c) - break; - line = parse_keyvalue_entry(parse_cylinder_keyvalue, cylinder, line); - } -} - -static void parse_weightsystem_keyvalue(void *_ws, const char *key, const char *value) -{ - weightsystem_t *ws = _ws; - if (!strcmp(key, "weight")) { - ws->weight = get_weight(value); - return; - } - /* This is handled by the "get_utf8()" */ - if (!strcmp(key, "description")) - return; - report_error("Unknown weightsystem key/value pair (%s/%s)", key, value); -} - -static void parse_dive_weightsystem(char *line, struct membuffer *str, void *_dive) -{ - struct dive *dive = _dive; - weightsystem_t *ws = dive->weightsystem + weightsystem_index; - - weightsystem_index++; - ws->description = get_utf8(str); - for (;;) { - char c; - while (isspace(c = *line)) - line++; - if (!c) - break; - line = parse_keyvalue_entry(parse_weightsystem_keyvalue, ws, line); - } -} - -static int match_action(char *line, struct membuffer *str, void *data, - struct keyword_action *action, unsigned nr_action) -{ - char *p = line, c; - unsigned low, high; - - while ((c = *p) >= 'a' && c <= 'z') // skip over 1st word - p++; // Extract the second word from the line: - if (p == line) - return -1; - switch (c) { - case 0: // if 2nd word is C-terminated - break; - case ' ': // =end of 2nd word? - *p++ = 0; // then C-terminate that word - break; - default: - return -1; - } - - /* Standard binary search in a table */ - low = 0; - high = nr_action; - while (low < high) { - unsigned mid = (low+high)/2; - struct keyword_action *a = action + mid; - int cmp = strcmp(line, a->keyword); - if (!cmp) { // attribute found: - a->fn(p, str, data); // Execute appropriate function, - return 0; // .. passing 2n word from above - } // (p) as a function argument. - if (cmp < 0) - high = mid; - else - low = mid+1; - } -report_error("Unmatched action '%s'", line); - return -1; -} - -/* FIXME! We should do the array thing here too. */ -static void parse_sample_keyvalue(void *_sample, const char *key, const char *value) -{ - struct sample *sample = _sample; - - if (!strcmp(key, "sensor")) { - sample->sensor = atoi(value); - return; - } - if (!strcmp(key, "ndl")) { - sample->ndl = get_duration(value); - return; - } - if (!strcmp(key, "tts")) { - sample->tts = get_duration(value); - return; - } - if (!strcmp(key, "in_deco")) { - sample->in_deco = atoi(value); - return; - } - if (!strcmp(key, "stoptime")) { - sample->stoptime = get_duration(value); - return; - } - if (!strcmp(key, "stopdepth")) { - sample->stopdepth = get_depth(value); - return; - } - if (!strcmp(key, "cns")) { - sample->cns = atoi(value); - return; - } - - if (!strcmp(key, "rbt")) { - sample->rbt = get_duration(value); - return; - } - - if (!strcmp(key, "po2")) { - pressure_t p = get_pressure(value); - sample->setpoint.mbar = p.mbar; - return; - } - if (!strcmp(key, "sensor1")) { - pressure_t p = get_pressure(value); - sample->o2sensor[0].mbar = p.mbar; - return; - } - if (!strcmp(key, "sensor2")) { - pressure_t p = get_pressure(value); - sample->o2sensor[1].mbar = p.mbar; - return; - } - if (!strcmp(key, "sensor3")) { - pressure_t p = get_pressure(value); - sample->o2sensor[2].mbar = p.mbar; - return; - } - if (!strcmp(key, "o2pressure")) { - pressure_t p = get_pressure(value); - sample->o2cylinderpressure.mbar = p.mbar; - return; - } - if (!strcmp(key, "heartbeat")) { - sample->heartbeat = atoi(value); - return; - } - if (!strcmp(key, "bearing")) { - sample->bearing.degrees = atoi(value); - return; - } - - report_error("Unexpected sample key/value pair (%s/%s)", key, value); -} - -static char *parse_sample_unit(struct sample *sample, double val, char *unit) -{ - char *end = unit, c; - - /* Skip over the unit */ - while ((c = *end) != 0) { - if (isspace(c)) { - *end++ = 0; - break; - } - end++; - } - - /* The units are "°C", "m" or "bar", so let's just look at the first character */ - switch (*unit) { - case 'm': - sample->depth.mm = rint(1000*val); - break; - case 'b': - sample->cylinderpressure.mbar = rint(1000*val); - break; - default: - sample->temperature.mkelvin = C_to_mkelvin(val); - break; - } - - return end; -} - -/* - * By default the sample data does not change unless the - * save-file gives an explicit new value. So we copy the - * data from the previous sample if one exists, and then - * the parsing will update it as necessary. - * - * There are a few exceptions, like the sample pressure: - * missing sample pressure doesn't mean "same as last - * time", but "interpolate". We clear those ones - * explicitly. - */ -static struct sample *new_sample(struct divecomputer *dc) -{ - struct sample *sample = prepare_sample(dc); - if (sample != dc->sample) { - memcpy(sample, sample-1, sizeof(struct sample)); - sample->cylinderpressure.mbar = 0; - } - return sample; -} - -static void sample_parser(char *line, struct divecomputer *dc) -{ - int m, s = 0; - struct sample *sample = new_sample(dc); - - m = strtol(line, &line, 10); - if (*line == ':') - s = strtol(line+1, &line, 10); - sample->time.seconds = m*60+s; - - for (;;) { - char c; - - while (isspace(c = *line)) - line++; - if (!c) - break; - /* Less common sample entries have a name */ - if (c >= 'a' && c <= 'z') { - line = parse_keyvalue_entry(parse_sample_keyvalue, sample, line); - } else { - const char *end; - double val = ascii_strtod(line, &end); - if (end == line) { - report_error("Odd sample data: %s", line); - break; - } - line = (char *)end; - line = parse_sample_unit(sample, val, line); - } - } - finish_sample(dc); -} - -static void parse_dc_airtemp(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->airtemp = get_temperature(line); } - -static void parse_dc_date(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; update_date(&dc->when, line); } - -static void parse_dc_deviceid(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->deviceid = get_hex(line); } - -static void parse_dc_diveid(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->diveid = get_hex(line); } - -static void parse_dc_duration(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->duration = get_duration(line); } - -static void parse_dc_dctype(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->divemode = get_dctype(line); } - -static void parse_dc_maxdepth(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->maxdepth = get_depth(line); } - -static void parse_dc_meandepth(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->meandepth = get_depth(line); } - -static void parse_dc_model(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->model = get_utf8(str); } - -static void parse_dc_numberofoxygensensors(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->no_o2sensors = get_index(line); } - -static void parse_dc_surfacepressure(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->surface_pressure = get_pressure(line); } - -static void parse_dc_salinity(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->salinity = get_salinity(line); } - -static void parse_dc_surfacetime(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->surfacetime = get_duration(line); } - -static void parse_dc_time(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; update_time(&dc->when, line); } - -static void parse_dc_watertemp(char *line, struct membuffer *str, void *_dc) -{ struct divecomputer *dc = _dc; dc->watertemp = get_temperature(line); } - -static void parse_event_keyvalue(void *_event, const char *key, const char *value) -{ - struct event *event = _event; - int val = atoi(value); - - if (!strcmp(key, "type")) { - event->type = val; - } else if (!strcmp(key, "flags")) { - event->flags = val; - } else if (!strcmp(key, "value")) { - event->value = val; - } else if (!strcmp(key, "name")) { - /* We get the name from the string handling */ - } else if (!strcmp(key, "cylinder")) { - /* NOTE! We add one here as a marker that "yes, we got a cylinder index" */ - event->gas.index = 1+get_index(value); - } else if (!strcmp(key, "o2")) { - event->gas.mix.o2 = get_fraction(value); - } else if (!strcmp(key, "he")) { - event->gas.mix.he = get_fraction(value); - } else - report_error("Unexpected event key/value pair (%s/%s)", key, value); -} - -/* keyvalue "key" "value" - * so we have two strings (possibly empty) in the membuffer, separated by a '\0' */ -static void parse_dc_keyvalue(char *line, struct membuffer *str, void *_dc) -{ - const char *key, *value; - struct divecomputer *dc = _dc; - - // Let's make sure we have two strings... - int string_counter = 0; - while(*line) { - if (*line == '"') - string_counter++; - line++; - } - if (string_counter != 2) - return; - - // stupidly the second string in the membuffer isn't NUL terminated; - // asking for a cstring fixes that; interestingly enough, given that there are two - // strings in the mb, the next command at the same time assigns a pointer to the - // first string to 'key' and NUL terminates the second string (which then goes to 'value') - key = mb_cstring(str); - value = key + strlen(key) + 1; - add_extra_data(dc, key, value); -} - -static void parse_dc_event(char *line, struct membuffer *str, void *_dc) -{ - int m, s = 0; - const char *name; - struct divecomputer *dc = _dc; - struct event event = { 0 }, *ev; - - m = strtol(line, &line, 10); - if (*line == ':') - s = strtol(line+1, &line, 10); - event.time.seconds = m*60+s; - - for (;;) { - char c; - while (isspace(c = *line)) - line++; - if (!c) - break; - line = parse_keyvalue_entry(parse_event_keyvalue, &event, line); - } - - name = ""; - if (str->len) - name = mb_cstring(str); - ev = add_event(dc, event.time.seconds, event.type, event.flags, event.value, name); - if (ev && event_is_gaschange(ev)) { - /* - * We subtract one here because "0" is "no index", - * and the parsing will add one for actual cylinder - * index data (see parse_event_keyvalue) - */ - ev->gas.index = event.gas.index-1; - if (event.gas.mix.o2.permille || event.gas.mix.he.permille) - ev->gas.mix = event.gas.mix; - } -} - -static void parse_trip_date(char *line, struct membuffer *str, void *_trip) -{ dive_trip_t *trip = _trip; update_date(&trip->when, line); } - -static void parse_trip_time(char *line, struct membuffer *str, void *_trip) -{ dive_trip_t *trip = _trip; update_time(&trip->when, line); } - -static void parse_trip_location(char *line, struct membuffer *str, void *_trip) -{ dive_trip_t *trip = _trip; trip->location = get_utf8(str); } - -static void parse_trip_notes(char *line, struct membuffer *str, void *_trip) -{ dive_trip_t *trip = _trip; trip->notes = get_utf8(str); } - -static void parse_settings_autogroup(char *line, struct membuffer *str, void *_unused) -{ set_autogroup(1); } - -static void parse_settings_units(char *line, struct membuffer *str, void *unused) -{ - if (line) - set_informational_units(line); -} - -static void parse_settings_userid(char *line, struct membuffer *str, void *_unused) -{ - if (line) { - set_save_userid_local(true); - set_userid(line); - } -} - -/* - * Our versioning is a joke right now, but this is more of an example of what we - * *can* do some day. And if we do change the version, this warning will show if - * you read with a version of subsurface that doesn't know about it. - * We MUST keep this in sync with the XML version (so we can report a consistent - * minimum datafile version) - */ -static void parse_settings_version(char *line, struct membuffer *str, void *_unused) -{ - int version = atoi(line); - report_datafile_version(version); - if (version > DATAFORMAT_VERSION) - report_error("Git save file version %d is newer than version %d I know about", version, DATAFORMAT_VERSION); -} - -/* The string in the membuffer is the version string of subsurface that saved things, just FYI */ -static void parse_settings_subsurface(char *line, struct membuffer *str, void *_unused) -{ } - -struct divecomputerid { - const char *model; - const char *nickname; - const char *firmware; - const char *serial; - const char *cstr; - unsigned int deviceid; -}; - -static void parse_divecomputerid_keyvalue(void *_cid, const char *key, const char *value) -{ - struct divecomputerid *cid = _cid; - - if (*value == '"') { - value = cid->cstr; - cid->cstr += strlen(cid->cstr)+1; - } - if (!strcmp(key, "deviceid")) { - cid->deviceid = get_hex(value); - return; - } - if (!strcmp(key, "serial")) { - cid->serial = value; - return; - } - if (!strcmp(key, "firmware")) { - cid->firmware = value; - return; - } - if (!strcmp(key, "nickname")) { - cid->nickname = value; - return; - } - report_error("Unknow divecomputerid key/value pair (%s/%s)", key, value); -} - -/* - * The 'divecomputerid' is a bit harder to parse than some other things, because - * it can have multiple strings (but see the tag parsing for another example of - * that) in addition to the non-string entries. - * - * We keep the "next" string in "id.cstr" and update it as we use it. - */ -static void parse_settings_divecomputerid(char *line, struct membuffer *str, void *_unused) -{ - struct divecomputerid id = { mb_cstring(str) }; - - id.cstr = id.model + strlen(id.model) + 1; - - /* Skip the '"' that stood for the model string */ - line++; - - for (;;) { - char c; - while (isspace(c = *line)) - line++; - if (!c) - break; - line = parse_keyvalue_entry(parse_divecomputerid_keyvalue, &id, line); - } - create_device_node(id.model, id.deviceid, id.serial, id.firmware, id.nickname); -} - -static void parse_picture_filename(char *line, struct membuffer *str, void *_pic) -{ - struct picture *pic = _pic; - pic->filename = get_utf8(str); -} - -static void parse_picture_gps(char *line, struct membuffer *str, void *_pic) -{ - struct picture *pic = _pic; - - pic->latitude = parse_degrees(line, &line); - pic->longitude = parse_degrees(line, &line); -} - -static void parse_picture_hash(char *line, struct membuffer *str, void *_pic) -{ - struct picture *pic = _pic; - pic->hash = get_utf8(str); -} - -/* These need to be sorted! */ -struct keyword_action dc_action[] = { -#undef D -#define D(x) { #x, parse_dc_ ## x } - D(airtemp), D(date), D(dctype), D(deviceid), D(diveid), D(duration), - D(event), D(keyvalue), D(maxdepth), D(meandepth), D(model), D(numberofoxygensensors), - D(salinity), D(surfacepressure), D(surfacetime), D(time), D(watertemp) -}; - -/* Sample lines start with a space or a number */ -static void divecomputer_parser(char *line, struct membuffer *str, void *_dc) -{ - char c = *line; - if (c < 'a' || c > 'z') - sample_parser(line, _dc); - match_action(line, str, _dc, dc_action, ARRAY_SIZE(dc_action)); -} - -/* These need to be sorted! */ -struct keyword_action dive_action[] = { -#undef D -#define D(x) { #x, parse_dive_ ## x } - D(airtemp), D(buddy), D(cylinder), D(divemaster), D(divesiteid), D(duration), - D(gps), D(location), D(notes), D(notrip), D(rating), D(suit), - D(tags), D(visibility), D(watertemp), D(weightsystem) -}; - -static void dive_parser(char *line, struct membuffer *str, void *_dive) -{ - match_action(line, str, _dive, dive_action, ARRAY_SIZE(dive_action)); -} - -/* These need to be sorted! */ -struct keyword_action site_action[] = { -#undef D -#define D(x) { #x, parse_site_ ## x } - D(description), D(geo), D(gps), D(name), D(notes) -}; - -static void site_parser(char *line, struct membuffer *str, void *_ds) -{ - match_action(line, str, _ds, site_action, ARRAY_SIZE(site_action)); -} - -/* These need to be sorted! */ -struct keyword_action trip_action[] = { -#undef D -#define D(x) { #x, parse_trip_ ## x } - D(date), D(location), D(notes), D(time), -}; - -static void trip_parser(char *line, struct membuffer *str, void *_trip) -{ - match_action(line, str, _trip, trip_action, ARRAY_SIZE(trip_action)); -} - -/* These need to be sorted! */ -static struct keyword_action settings_action[] = { -#undef D -#define D(x) { #x, parse_settings_ ## x } - D(autogroup), D(divecomputerid), D(subsurface), D(units), D(userid), D(version), -}; - -static void settings_parser(char *line, struct membuffer *str, void *_unused) -{ - match_action(line, str, NULL, settings_action, ARRAY_SIZE(settings_action)); -} - -/* These need to be sorted! */ -static struct keyword_action picture_action[] = { -#undef D -#define D(x) { #x, parse_picture_ ## x } - D(filename), D(gps), D(hash) -}; - -static void picture_parser(char *line, struct membuffer *str, void *_pic) -{ - match_action(line, str, _pic, picture_action, ARRAY_SIZE(picture_action)); -} - -/* - * We have a very simple line-based interface, with the small - * complication that lines can have strings in the middle, and - * a string can be multiple lines. - * - * The UTF-8 string escaping is *very* simple, though: - * - * - a string starts and ends with double quotes (") - * - * - inside the string we escape: - * (a) double quotes with '\"' - * (b) backslash (\) with '\\' - * - * - additionally, for human readability, we escape - * newlines with '\n\t', with the exception that - * consecutive newlines are left unescaped (so an - * empty line doesn't become a line with just a tab - * on it). - * - * Also, while the UTF-8 string can have arbitrarily - * long lines, the non-string parts of the lines are - * never long, so we can use a small temporary buffer - * on stack for that part. - * - * Also, note that if a line has one or more strings - * in it: - * - * - each string will be represented as a single '"' - * character in the output. - * - * - all string will exist in the same 'membuffer', - * separated by NUL characters (that cannot exist - * in a string, not even quoted). - */ -static const char *parse_one_string(const char *buf, const char *end, struct membuffer *b) -{ - const char *p = buf; - - /* - * We turn multiple strings one one line (think dive tags) into one - * membuffer that has NUL characters in between strings. - */ - if (b->len) - put_bytes(b, "", 1); - - while (p < end) { - char replace; - - switch (*p++) { - default: - continue; - case '\n': - if (p < end && *p == '\t') { - replace = '\n'; - break; - } - continue; - case '\\': - if (p < end) { - replace = *p; - break; - } - continue; - case '"': - replace = 0; - break; - } - put_bytes(b, buf, p - buf - 1); - if (!replace) - break; - put_bytes(b, &replace, 1); - buf = ++p; - } - return p; -} - -typedef void (line_fn_t)(char *, struct membuffer *, void *); -#define MAXLINE 500 -static unsigned parse_one_line(const char *buf, unsigned size, line_fn_t *fn, void *fndata, struct membuffer *b) -{ - const char *end = buf + size; - const char *p = buf; - char line[MAXLINE+1]; - int off = 0; - - while (p < end) { - char c = *p++; - if (c == '\n') - break; - line[off] = c; - off++; - if (off > MAXLINE) - off = MAXLINE; - if (c == '"') - p = parse_one_string(p, end, b); - } - line[off] = 0; - fn(line, b, fndata); - return p - buf; -} - -/* - * We keep on re-using the membuffer that we use for - * strings, but the callback function can "steal" it by - * saving its value and just clear the original. - */ -static void for_each_line(git_blob *blob, line_fn_t *fn, void *fndata) -{ - const char *content = git_blob_rawcontent(blob); - unsigned int size = git_blob_rawsize(blob); - struct membuffer str = { 0 }; - - while (size) { - unsigned int n = parse_one_line(content, size, fn, fndata, &str); - content += n; - size -= n; - - /* Re-use the allocation, but forget the data */ - str.len = 0; - } - free_buffer(&str); -} - -#define GIT_WALK_OK 0 -#define GIT_WALK_SKIP 1 - -static struct divecomputer *active_dc; -static struct dive *active_dive; -static dive_trip_t *active_trip; - -static void finish_active_trip(void) -{ - dive_trip_t *trip = active_trip; - - if (trip) { - active_trip = NULL; - insert_trip(&trip); - } -} - -static void finish_active_dive(void) -{ - struct dive *dive = active_dive; - - if (dive) { - /* check if we need to save pictures */ - FOR_EACH_PICTURE(dive) { - if (!picture_exists(picture)) - save_picture_from_git(picture); - } - /* free any memory we allocated to track pictures */ - while (pel) { - free(pel->data); - void *lastone = pel; - pel = pel->next; - free(lastone); - } - active_dive = NULL; - record_dive(dive); - } -} - -static struct dive *create_new_dive(timestamp_t when) -{ - struct dive *dive = alloc_dive(); - - /* We'll fill in more data from the dive file */ - dive->when = when; - - if (active_trip) - add_dive_to_trip(dive, active_trip); - 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); - - 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; - finish_active_trip(); - active_trip = create_new_trip(yyyy, mm, dd); - return GIT_WALK_OK; -} - -/* - * Dive directory, name is [[yyyy-]mm-]nn-ddd-hh:mm:ss[~hex] in older git repositories - * but [[yyyy-]mm-]nn-ddd-hh=mm=ss[~hex] in newer repos as ':' is an illegal character for Windows files - * 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, month_off, year_off; - struct tm tm; - - /* Skip the '-' before the time */ - mday_off = timeoff; - if (!mday_off || name[--mday_off] != '-') - return GIT_WALK_SKIP; - /* Skip the day name */ - while (mday_off > 0 && name[--mday_off] != '-') - /* nothing */; - - mday_off = mday_off - 2; - month_off = mday_off - 3; - year_off = month_off - 5; - if (mday_off < 0) - return GIT_WALK_SKIP; - - /* Get the time of day -- parse both time formats so we can read old repos when not on Windows */ - if (sscanf(name+timeoff, "%d:%d:%d", &h, &m, &s) != 3 && sscanf(name+timeoff, "%d=%d=%d", &h, &m, &s) != 3) - return GIT_WALK_SKIP; - if (!validate_time(h, m, s)) - return GIT_WALK_SKIP; - - /* - * Using the "git_tree_walk()" interface is simple, but - * it kind of sucks as an interface because there is - * no sane way to pass the hierarchy to the callbacks. - * The "payload" is a fixed one-time thing: we'd like - * the "current trip" to be passed down to the dives - * that get parsed under that trip, but we can't. - * - * So "active_trip" is not the trip that is in the hierarchy - * _above_ us, it's just the trip that was _before_ us. But - * if a dive is not in a trip at all, we can't tell. - * - * We could just do a better walker that passes the - * return value around, but we hack around this by - * instead looking at the one hierarchical piece of - * data we have: the pathname to the current entry. - * - * This is pretty hacky. The magic '8' is the length - * of a pathname of the form 'yyyy/mm/'. - */ - if (strlen(root) == 8) - finish_active_trip(); - - /* - * 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; - - finish_active_dive(); - active_dive = create_new_dive(utc_mktime(&tm)); - return GIT_WALK_OK; -} - -static int picture_directory(const char *root, const char *name) -{ - if (!active_dive) - return GIT_WALK_SKIP; - 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] - * - * (older versions had this as [[yyyy-]mm-]nn-ddd-hh:mm:ss[~hex] - * but that faile on Windows) - * - * 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 is a per-dive picture directory ("Pictures") - * - * - It's some random non-dive-data directory. - * - * 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; - - if (!strcmp(name, "Pictures")) - return picture_directory(root, name); - - if (!strcmp(name, "01-Divesites")) - return GIT_WALK_OK; - - 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] == ':' || name[len-3] == '=') - return dive_directory(root, name, len-8); - - if (digits != 2) - return GIT_WALK_SKIP; - - return dive_trip_directory(root, name); -} - -git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry *entry) -{ - const git_oid *id = git_tree_entry_id(entry); - git_blob *blob; - - if (git_blob_lookup(&blob, repo, id)) - return NULL; - return blob; -} - -static struct divecomputer *create_new_dc(struct dive *dive) -{ - struct divecomputer *dc = &dive->dc; - - while (dc->next) - dc = dc->next; - /* Did we already fill that in? */ - if (dc->samples || dc->model || dc->when) { - struct divecomputer *newdc = calloc(1, sizeof(*newdc)); - if (!newdc) - return NULL; - dc->next = newdc; - dc = newdc; - } - dc->when = dive->when; - dc->duration = dive->duration; - return dc; -} - -/* - * We should *really* try to delay the dive computer data parsing - * until necessary, in order to reduce load-time. The parsing is - * cheap, but the loading of the git blob into memory can be pretty - * costly. - */ -static int parse_divecomputer_entry(git_repository *repo, const git_tree_entry *entry, const char *suffix) -{ - git_blob *blob = git_tree_entry_blob(repo, entry); - - if (!blob) - return report_error("Unable to read divecomputer file"); - - active_dc = create_new_dc(active_dive); - for_each_line(blob, divecomputer_parser, active_dc); - git_blob_free(blob); - active_dc = NULL; - return 0; -} - -static int parse_dive_entry(git_repository *repo, const git_tree_entry *entry, const char *suffix) -{ - struct dive *dive = active_dive; - git_blob *blob = git_tree_entry_blob(repo, entry); - if (!blob) - return report_error("Unable to read dive file"); - if (*suffix) - dive->number = atoi(suffix+1); - cylinder_index = weightsystem_index = 0; - for_each_line(blob, dive_parser, active_dive); - git_blob_free(blob); - return 0; -} - -static int parse_site_entry(git_repository *repo, const git_tree_entry *entry, const char *suffix) -{ - if (*suffix == '\0') - return report_error("Dive site without uuid"); - uint32_t uuid = strtoul(suffix, NULL, 16); - struct dive_site *ds = alloc_or_get_dive_site(uuid); - git_blob *blob = git_tree_entry_blob(repo, entry); - if (!blob) - return report_error("Unable to read dive site file"); - for_each_line(blob, site_parser, ds); - git_blob_free(blob); - return 0; -} - -static int parse_trip_entry(git_repository *repo, const git_tree_entry *entry) -{ - git_blob *blob = git_tree_entry_blob(repo, entry); - if (!blob) - return report_error("Unable to read trip file"); - for_each_line(blob, trip_parser, active_trip); - git_blob_free(blob); - return 0; -} - -static int parse_settings_entry(git_repository *repo, const git_tree_entry *entry) -{ - git_blob *blob = git_tree_entry_blob(repo, entry); - if (!blob) - return report_error("Unable to read settings file"); - set_save_userid_local(false); - for_each_line(blob, settings_parser, NULL); - git_blob_free(blob); - return 0; -} - -static int parse_picture_file(git_repository *repo, const git_tree_entry *entry, const char *name) -{ - /* remember the picture data so we can handle it when all dive data has been loaded - * the name of the git file is PIC-<hash> */ - git_blob *blob = git_tree_entry_blob(repo, entry); - const void *rawdata = git_blob_rawcontent(blob); - int len = git_blob_rawsize(blob); - struct picture_entry_list *new_pel = malloc(sizeof(struct picture_entry_list)); - new_pel->next = pel; - pel = new_pel; - pel->data = malloc(len); - memcpy(pel->data, rawdata, len); - pel->len = len; - pel->hash = strdup(name + 4); - git_blob_free(blob); - return 0; -} - -static int parse_picture_entry(git_repository *repo, const git_tree_entry *entry, const char *name) -{ - git_blob *blob; - struct picture *pic; - int hh, mm, ss, offset; - char sign; - - /* - * The format of the picture name files is just the offset within - * the dive in form [[+-]hh=mm=ss (previously [[+-]hh:mm:ss, but - * that didn't work on Windows), possibly followed by a hash to - * make the filename unique (which we can just ignore). - */ - if (sscanf(name, "%c%d:%d:%d", &sign, &hh, &mm, &ss) != 4 && - sscanf(name, "%c%d=%d=%d", &sign, &hh, &mm, &ss) != 4) - return report_error("Unknown file name %s", name); - offset = ss + 60*(mm + 60*hh); - if (sign == '-') - offset = -offset; - - blob = git_tree_entry_blob(repo, entry); - if (!blob) - return report_error("Unable to read picture file"); - - pic = alloc_picture(); - pic->offset.seconds = offset; - - for_each_line(blob, picture_parser, pic); - dive_add_picture(active_dive, pic); - git_blob_free(blob); - return 0; -} - -static int walk_tree_file(const char *root, const git_tree_entry *entry, git_repository *repo) -{ - struct dive *dive = active_dive; - dive_trip_t *trip = active_trip; - const char *name = git_tree_entry_name(entry); - if (verbose > 1) - fprintf(stderr, "git load handling file %s\n", name); - switch (*name) { - /* Picture file? They are saved as time offsets in the dive */ - case '-': case '+': - if (dive) - return parse_picture_entry(repo, entry, name); - break; - case 'D': - if (dive && !strncmp(name, "Divecomputer", 12)) - return parse_divecomputer_entry(repo, entry, name+12); - if (dive && !strncmp(name, "Dive", 4)) - return parse_dive_entry(repo, entry, name+4); - break; - case 'S': - if (!strncmp(name, "Site", 4)) - return parse_site_entry(repo, entry, name + 5); - break; - case '0': - if (trip && !strcmp(name, "00-Trip")) - return parse_trip_entry(repo, entry); - if (!strcmp(name, "00-Subsurface")) - return parse_settings_entry(repo, entry); - break; - case 'P': - if (dive && !strncmp(name, "PIC-", 4)) - return parse_picture_file(repo, entry, name); - break; - } - report_error("Unknown file %s%s (%p %p)", root, name, dive, trip); - return GIT_WALK_SKIP; -} - -static int walk_tree_cb(const char *root, const git_tree_entry *entry, void *payload) -{ - git_repository *repo = payload; - git_filemode_t mode = git_tree_entry_filemode(entry); - - if (mode == GIT_FILEMODE_TREE) - return walk_tree_directory(root, entry); - - walk_tree_file(root, entry, repo); - /* Ignore failed blob loads */ - return GIT_WALK_OK; -} - -static int load_dives_from_tree(git_repository *repo, git_tree *tree) -{ - git_tree_walk(tree, GIT_TREEWALK_PRE, walk_tree_cb, repo); - return 0; -} - -void clear_git_id(void) -{ - saved_git_id = NULL; -} - -void set_git_id(const struct git_oid * id) -{ - static char git_id_buffer[GIT_OID_HEXSZ+1]; - - git_oid_tostr(git_id_buffer, sizeof(git_id_buffer), id); - saved_git_id = git_id_buffer; -} - -static int do_git_load(git_repository *repo, const char *branch) -{ - int ret; - git_object *object; - git_commit *commit; - git_tree *tree; - - if (git_revparse_single(&object, repo, branch)) - return report_error("Unable to look up revision '%s'", branch); - if (git_object_peel((git_object **)&commit, object, GIT_OBJ_COMMIT)) - return report_error("Revision '%s' is not a valid commit", branch); - if (git_commit_tree(&tree, commit)) - return report_error("Could not look up tree of commit in branch '%s'", branch); - ret = load_dives_from_tree(repo, tree); - if (!ret) - set_git_id(git_commit_id(commit)); - git_object_free((git_object *)tree); - return ret; -} - -/* - * Like git_save_dives(), this silently returns a negative - * value if it's not a git repository at all (so that you - * can try to load it some other way. - * - * If it is a git repository, we return zero for success, - * or report an error and return 1 if the load failed. - */ -int git_load_dives(struct git_repository *repo, const char *branch) -{ - int ret; - - if (repo == dummy_git_repository) - return report_error("Unable to open git repository at '%s'", branch); - ret = do_git_load(repo, branch); - git_repository_free(repo); - free((void *)branch); - finish_active_dive(); - finish_active_trip(); - return ret; -} |