diff --git a/README.md b/README.md
index 2647c2e..7132967 100644
--- a/README.md
+++ b/README.md
@@ -99,6 +99,32 @@ Some example uses we consider useful:
print the LAST position of all users, devices. Can be combined with `--user` and `--device`.
* `ocat ... --format csv`
produces CSV. Limit the fields you want extracted with `--fields lat,lon,cc` for example.
+* `ocat ... --format xml`
+ produces XML. Limit the fields you want extracted with `--fields lat,lon,cc` for example.
+```
+
+
+
+
+ 1440395361
+ 3000.000000
+ 51
+ 10.027857
+ 29.000000
+ -1
+ 52.378886
+ -1
+ NE
+ 96
+ u1r1upq
+ DE
+ Heidecker Weg 86, 31275 Lehrte, Germany
+ 2015-08-24T05:55:07Z
+ 2015-08-24T05:49:21Z
+
+ ...
+
+```
* `ocat ... --limit 10`
prints data for the current month, starting now and going backwards; only 10 locations will be printed. Generally, the `--limit` option reads the storage back to front which makes no sense in some combinations.
diff --git a/ocat.c b/ocat.c
index 4d12fdd..cc73425 100644
--- a/ocat.c
+++ b/ocat.c
@@ -55,6 +55,44 @@ static void print_one(JsonNode *j, JsonNode *inttypes)
}
}
+/*
+ * Print the value in a single JSON node as XML. If string, easy. If number account for
+ * what we call 'integer' types which shouldn't be printed as floats.
+ */
+
+static void emit_one(JsonNode *j, JsonNode *inttypes)
+{
+ if (!strcmp(j->key, "_type"))
+ return;
+
+ printf(" <%s>", j->key);
+ /* Check if the value should be an "integer" (ie not float) */
+ if (j->tag == JSON_NUMBER) {
+ if (json_find_member(inttypes, j->key)) {
+ printf("%.lf", j->number_);
+ } else {
+ printf("%lf", j->number_);
+ }
+ } else if (j->tag == JSON_STRING) {
+ char *bp;
+
+ for (bp = j->string_; bp && *bp; bp++) {
+ switch (*bp) {
+ case '&': printf("&"); break;
+ case '<': printf("<"); break;
+ case '>': printf(">"); break;
+ case '"': printf("""); break;
+ case '\'': printf("'"); break;
+ default: putc(*bp, stdout); break;
+ }
+ }
+ } else if (j->tag == JSON_BOOL) {
+ printf("%s", (j->bool_) ? "true" : "false");
+ } else if (j->tag == JSON_NULL) {
+ printf("null");
+ }
+ printf("%s>\n", j->key);
+}
/*
* Output location data as CSV. If `fields' is not NULL, it's a JSON
* array of JSON elment names which should be printed instead of the
@@ -116,6 +154,47 @@ void csv_output(JsonNode *json, output_type otype, JsonNode *fields)
json_delete(inttypes);
}
+
+void xml_output(JsonNode *json, output_type otype, JsonNode *fields)
+{
+ JsonNode *node, *inttypes;
+ JsonNode *arr, *one, *j;
+
+ /* Prime the inttypes object with types we consider "integer" */
+ inttypes = json_mkobject();
+ json_append_member(inttypes, "batt", json_mkbool(1));
+ json_append_member(inttypes, "vel", json_mkbool(1));
+ json_append_member(inttypes, "cog", json_mkbool(1));
+ json_append_member(inttypes, "tst", json_mkbool(1));
+ json_append_member(inttypes, "alt", json_mkbool(1));
+ json_append_member(inttypes, "dist", json_mkbool(1));
+ json_append_member(inttypes, "trip", json_mkbool(1));
+
+ printf("\n\
+ \n");
+ printf("\n");
+
+ arr = json_find_member(json, "locations");
+ json_foreach(one, arr) {
+ printf(" \n");
+ if (fields) {
+ json_foreach(node, fields) {
+ if ((j = json_find_member(one, node->string_)) != NULL) {
+ emit_one(j, inttypes);
+ }
+ }
+ } else {
+ json_foreach(j, one) {
+ emit_one(j, inttypes);
+ }
+ }
+ printf(" \n\n");
+ }
+ printf("\n");
+
+ json_delete(inttypes);
+}
+
void usage(char *prog)
{
printf("Usage: %s [options..] [file ...]\n", prog);
@@ -136,6 +215,7 @@ void usage(char *prog)
printf(" geojson Geo-JSON points\n");
printf(" linestring Geo-JSON LineString\n");
printf(" gpx\n");
+ printf(" xml\n");
printf(" raw\n");
printf(" payload Like RAW but JSON payload only\n");
printf(" --fields tst,lat,lon,... Choose fields for CSV. (dflt: ALL)\n");
@@ -303,6 +383,8 @@ int main(int argc, char **argv)
otype = RAW;
else if (!strcmp(optarg, "payload"))
otype = RAWPAYLOAD;
+ else if (!strcmp(optarg, "xml"))
+ otype = XML;
else if (!strcmp(optarg, "gpx"))
otype = GPX;
else if (!strcmp(optarg, "csv"))
@@ -392,6 +474,13 @@ int main(int argc, char **argv)
csv_output(o, CSV, fields);
json_delete(o);
+ } else if (otype == XML) {
+ JsonNode *o = json_mkobject();
+
+ json_append_member(o, "locations", user_array);
+ xml_output(o, CSV, fields);
+ json_delete(o);
+
} else {
fprintf(stderr, "%s: unsupported output type for LAST\n", progname);
}
@@ -503,6 +592,8 @@ int main(int argc, char **argv)
} else if (otype == CSV) {
csv_output(obj, CSV, fields);
+ } else if (otype == XML) {
+ xml_output(obj, XML, fields);
} else if (otype == RAW || otype == RAWPAYLOAD) {
/* We've already done what we need to do in locations() */
} else if (otype == LINESTRING) {
diff --git a/storage.h b/storage.h
index 15326ad..35c3131 100644
--- a/storage.h
+++ b/storage.h
@@ -15,6 +15,7 @@ typedef enum {
GPX,
RAWPAYLOAD,
LINESTRING,
+ XML,
} output_type;
JsonNode *lister(char *username, char *device, time_t s_lo, time_t s_hi, int reverse);