aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <me@robin-krahl.de>2016-06-01 23:44:11 +0200
committerRobin Krahl <me@robin-krahl.de>2016-06-01 23:44:11 +0200
commit47d6867cc86d5239fea690565abe84c526edaa5b (patch)
tree3c2ac1d5cd035cec7e2439a1575689bc47e4d178
parent3e0a3f7da39fdbbd2565f28c04c54fb5b51773b4 (diff)
downloaddbfp-47d6867cc86d5239fea690565abe84c526edaa5b.tar.gz
dbfp-47d6867cc86d5239fea690565abe84c526edaa5b.tar.bz2
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.
-rw-r--r--dbfp.c205
-rw-r--r--dbfp.h3
-rw-r--r--dbfp_check.c68
3 files changed, 270 insertions, 6 deletions
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)
+{
+}
diff --git a/dbfp.h b/dbfp.h
index e1e1247..02b724d 100644
--- a/dbfp.h
+++ b/dbfp.h
@@ -49,5 +49,8 @@ int dbfp_query_location_name(struct dbfp *dbfp, char *input,
size_t *n, struct dbfp_location **locations);
void dbfp_departure_close(struct dbfp_departure *departure);
+int dbfp_query_departure(struct dbfp *dbfp, struct dbfp_location *location,
+ char *location_id, time_t time, size_t *n,
+ struct dbfp_departure **departures);
#endif /* DBFP_H_ */
diff --git a/dbfp_check.c b/dbfp_check.c
index f816cfc..bda63e5 100644
--- a/dbfp_check.c
+++ b/dbfp_check.c
@@ -16,6 +16,11 @@ static void assert_location_eq(size_t n, struct dbfp_location *locations,
size_t i, const char *name, const char *id, float lon,
float lat);
+static void assert_departure_eq(size_t n, struct dbfp_departure *departures,
+ size_t i, const char *name, const char *type,
+ const char *stopid, const char *stop, time_t time,
+ const char *direction, const char *track);
+
START_TEST(test_dbfp_create)
{
struct dbfp dbfp;
@@ -85,6 +90,47 @@ START_TEST(test_dbfp_location_simple)
}
END_TEST
+START_TEST(test_dbfp_departure_simple)
+{
+ int err;
+ struct dbfp dbfp;
+ struct dbfp_departure *departures = NULL;
+ struct tm tm = { .tm_year = 116, .tm_mon = 5, .tm_mday = 1,
+ .tm_hour = 17, .tm_min = 42 };
+ time_t time = mktime(&tm);
+ size_t n;
+ size_t i;
+
+ err = dbfp_init(&dbfp, api_key);
+ ck_assert_int_eq(err, 0);
+
+ err = dbfp_query_departure(&dbfp, NULL, "8000105", 0, &n, &departures);
+ ck_assert_int_eq(err, 0);
+ ck_assert_int_gt(n, 0);
+ for (i = 0; i < n; i++)
+ dbfp_departure_close(&departures[i]);
+ free(departures);
+
+ err = dbfp_query_departure(&dbfp, NULL, "8000105", time, &n,
+ &departures);
+ ck_assert_int_eq(err, 0);
+ tm.tm_hour++;
+ time = mktime(&tm);
+ assert_departure_eq(n, departures, 0, "ICE 1556", "ICE", "8000105",
+ "Frankfurt(Main)Hbf", time, "Wiesbaden Hbf", "6");
+ tm.tm_min = 49;
+ time = mktime(&tm);
+ assert_departure_eq(n, departures, 1, "IC 2274", "IC", "8000105",
+ "Frankfurt(Main)Hbf", time, "Kassel-Wilhelmshöhe",
+ "11");
+ for (i = 0; i < n; i++)
+ dbfp_departure_close(&departures[i]);
+ free(departures);
+
+ dbfp_close(&dbfp);
+}
+END_TEST
+
int main(int argc, char **argv)
{
int num_failed = 0;
@@ -92,6 +138,7 @@ int main(int argc, char **argv)
SRunner *sr;
TCase *tc_basic;
TCase *tc_location;
+ TCase *tc_departure;
api_key = getenv("DBFP_API_KEY");
if (!api_key) {
@@ -111,6 +158,10 @@ int main(int argc, char **argv)
tcase_add_test(tc_location, test_dbfp_location_simple);
suite_add_tcase(s, tc_location);
+ tc_departure = tcase_create("departure");
+ tcase_add_test(tc_departure, test_dbfp_departure_simple);
+ suite_add_tcase(s, tc_departure);
+
srunner_run_all(sr, CK_ENV);
num_failed = srunner_ntests_failed(sr);
srunner_free(sr);
@@ -130,3 +181,20 @@ static void assert_location_eq(size_t n, struct dbfp_location *locations,
ck_assert(locations[i].lon == lon);
ck_assert(locations[i].lat == lat);
}
+
+static void assert_departure_eq(size_t n, struct dbfp_departure *departures,
+ size_t i, const char *name, const char *type,
+ const char *stopid, const char *stop, time_t time,
+ const char *direction, const char *track)
+{
+ ck_assert_ptr_ne(departures, NULL);
+ ck_assert_uint_lt(i, n);
+
+ ck_assert_str_eq(departures[i].name, name);
+ ck_assert_str_eq(departures[i].type, type);
+ ck_assert_str_eq(departures[i].stopid, stopid);
+ ck_assert_str_eq(departures[i].stop, stop);
+ ck_assert_uint_eq(departures[i].time, time);
+ ck_assert_str_eq(departures[i].direction, direction);
+ ck_assert_str_eq(departures[i].track, track);
+}