From 47d6867cc86d5239fea690565abe84c526edaa5b Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 1 Jun 2016 23:44:11 +0200 Subject: dbfp: implement dbfp_query_departure Retrieves the next departures of a given station. The time handling needs to be checked, as the DB API always uses Europe/Berlin. --- dbfp.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 199 insertions(+), 6 deletions(-) (limited to 'dbfp.c') diff --git a/dbfp.c b/dbfp.c index 807ba19..b2d8074 100644 --- a/dbfp.c +++ b/dbfp.c @@ -27,10 +27,16 @@ struct location_data { struct dbfp_location *locations; }; +struct departure_data { + size_t n; + struct dbfp_departure *departures; +}; + struct object_data { const char *name; int (*read)(const char **, size_t, void *); void *value; + int required; }; static int dbfp_parse(void *contents, size_t len, size_t n, void *data); @@ -38,10 +44,16 @@ static int dbfp_request(struct dbfp *dbfp, char *service, char *query, XML_Parser parser, struct parse_data *pd); static int dbfp_url(struct dbfp *dbfp, char *service, char *query, char **url); static int dbfp_format_url(char **out, const char *format, ...); +static int dbfp_format_time(char **out, size_t n, const char *format, + const struct tm *tm); static int check_ele_error(const char *name, const char **atts); static int read_string(const char **atts, size_t idx, void *value); static int read_float(const char **atts, size_t idx, void *value); +static int read_datetime(const char **atts, size_t idx, void *value, + const char *format); +static int read_date(const char **atts, size_t idx, void *value); +static int read_time(const char **atts, size_t idx, void *value); static int read_object_indizes(const char **atts, struct object_data *data, size_t *indizes); static int read_object(const char **atts, struct object_data *data); @@ -49,6 +61,9 @@ static int read_object(const char **atts, struct object_data *data); static void location_start(void *data, const char *name, const char **atts); static void location_end(void *data, const char *name); +static void departure_start(void *data, const char *name, const char **atts); +static void departure_end(void *data, const char *name); + int dbfp_init(struct dbfp *dbfp, char *key) { int err = 0; @@ -168,6 +183,74 @@ void dbfp_departure_close(struct dbfp_departure *departure) free(departure->track); } +int dbfp_query_departure(struct dbfp *dbfp, struct dbfp_location *location, + char *location_id, time_t time, size_t *n, + struct dbfp_departure **departures) +{ + int err = 0; + char *query = 0; + char *query_time = NULL; + char *query_date = NULL; + XML_Parser parser = NULL; + struct departure_data dd = { .n = 0, .departures = NULL }; + struct parse_data pd = { .error = 0, .data = &dd }; + struct tm *tm; + + if (!dbfp || !n || !departures) + return EINVAL; + if ((!location && !location_id) || (location && !location->id)) + return EINVAL; + + *n = 0; + *departures = 0; + + if (time) { + tm = localtime(&time); + if (!tm) { + err = DBFP_ERROR_FORMAT; + goto cleanup; + } + + err = dbfp_format_time(&query_time, 5, "%H:%M", tm); + if (err) + goto cleanup; + + err = dbfp_format_time(&query_date, 10, "%Y-%m-%d", tm); + if (err) + goto cleanup; + + err = dbfp_format_url(&query, "id=%s&date=%s&time=%s", + location ? location->id : location_id, + query_date, query_time); + } else { + err = dbfp_format_url(&query, "id=%s", + location ? location->id : location_id); + } + if (err) + goto cleanup; + + parser = XML_ParserCreateNS(NULL, '\0'); + XML_SetElementHandler(parser, departure_start, departure_end); + + err = dbfp_request(dbfp, "departureBoard", query, parser, &pd); + if (err) + goto cleanup; + + *n = dd.n; + *departures = dd.departures; + +cleanup: + // TODO check wheterh the elements of dd.departures should be closed + if (err) + free(dd.departures); + XML_ParserFree(parser); + free(query_time); + free(query_date); + free(query); + + return err; +} + static int dbfp_parse(void *contents, size_t len, size_t n, void *data) { XML_Parser parser = (XML_Parser)data; @@ -251,6 +334,26 @@ static int dbfp_format_url(char **out, const char *format, ...) return 0; } +static int dbfp_format_time(char **out, size_t n, const char *format, + const struct tm *tm) +{ + size_t len; + + *out = malloc((n + 1) * sizeof(**out)); + if (!*out) + return ENOMEM; + + /* TODO: check timezone */ + len = strftime(*out, n + 1, format, tm); + if (len != n) { + free(*out); + *out = NULL; + return DBFP_ERROR_FORMAT; + } + + return 0; +} + static int check_ele_error(const char *name, const char **atts) { if (strcmp(name, "Error") == 0) { @@ -284,6 +387,31 @@ static int read_float(const char **atts, size_t idx, void *value) return 0; } +static int read_datetime(const char **atts, size_t idx, void *value, + const char *format) +{ + struct tm *tm = (struct tm *)value; + + /* + * TODO: check whether it is safe to rely on the fact that strptime + * does not touch the unhandled values + */ + if (!strptime(atts[idx], format, tm)) + return DBFP_ERROR_STRUCTURE; + + return 0; +} + +static int read_date(const char **atts, size_t idx, void *value) +{ + return read_datetime(atts, idx, value, "%Y-%m-%d"); +} + +static int read_time(const char **atts, size_t idx, void *value) +{ + return read_datetime(atts, idx, value, "%H:%M"); +} + static int read_object_indizes(const char **atts, struct object_data *data, size_t *indizes) { @@ -303,7 +431,11 @@ static int read_object_indizes(const char **atts, struct object_data *data, } for (i = 0; data[i].name; i++) { - if (!indizes[i]) + /* + * allow some items to be missing due to the type/tyoe/tyte + * confusion + */ + if (!indizes[i] && data[i].required) return DBFP_ERROR_STRUCTURE; } @@ -329,6 +461,8 @@ static int read_object(const char **atts, struct object_data *data) goto cleanup; for (i = 0; data[i].name; i++) { + if (!indizes[i]) + continue; err = data[i].read(atts, indizes[i], data[i].value); if (err) goto cleanup; @@ -347,11 +481,11 @@ static void location_start(void *data, const char *name, const char **atts) struct location_data *ld = (struct location_data *)pd->data; struct dbfp_location location = { NULL, NULL, 0, 0 }; struct object_data od[] = { - { "name", &read_string, &location.name }, - { "id", &read_string, &location.id }, - { "lon", &read_float, &location.lon }, - { "lat", &read_float, &location.lat }, - { NULL, NULL, NULL } + { "name", &read_string, &location.name, 1 }, + { "id", &read_string, &location.id, 1 }, + { "lon", &read_float, &location.lon, 1 }, + { "lat", &read_float, &location.lat, 1 }, + { NULL, NULL, NULL, 0 } }; err = check_ele_error(name, atts); @@ -385,3 +519,62 @@ cleanup: static void location_end(void *data, const char *name) { } + +static void departure_start(void *data, const char *name, const char **atts) +{ + int err = 0; + struct parse_data *pd = (struct parse_data *)data; + struct departure_data *dd = (struct departure_data *)pd->data; + struct tm tm; + struct dbfp_departure departure = { NULL, NULL, NULL, NULL, 0, NULL, + NULL }; + struct object_data od[] = { + { "name", &read_string, &departure.name, 1 }, + { "type", &read_string, &departure.type, 0 }, + { "tyte", &read_string, &departure.type, 0 }, + { "tyoe", &read_string, &departure.type, 0 }, + { "stopid", &read_string, &departure.stopid, 1 }, + { "stop", &read_string, &departure.stop, 1 }, + { "date", &read_date, &tm, 1 }, + { "time", &read_time, &tm, 1 }, + { "direction", &read_string, &departure.direction, 1 }, + { "track", &read_string, &departure.track, 1 }, + { NULL, NULL, NULL, 0 } + }; + + err = check_ele_error(name, atts); + if (err) + goto cleanup; + + if (strcmp(name, "Departure") != 0) + goto cleanup; + + memset(&tm, 0, sizeof(tm)); + + err = read_object(atts, od); + if (err) + goto cleanup; + + dd->departures = realloc(dd->departures, + (dd->n + 1) * sizeof(*dd->departures)); + if (!dd->departures) { + err = ENOMEM; + goto cleanup; + } + + /* TODO: check timezone */ + departure.time = mktime(&tm); + + dd->departures[dd->n] = departure; + dd->n++; + +cleanup: + if (err) { + dbfp_departure_close(&departure); + pd->error = err; + } +} + +static void departure_end(void *data, const char *name) +{ +} -- cgit v1.2.1