commit 69bdf1e0a07ed37961a6c75e6145dc8f98d68322
Author: Nikolay Korotkiy <sikmir@gmail.com>
Date: Mon, 16 Aug 2021 22:03:06 +0300
initial repo
Diffstat:
A | .gitignore | | | 2 | ++ |
A | LICENSE | | | 15 | +++++++++++++++ |
A | Makefile | | | 16 | ++++++++++++++++ |
A | README.md | | | 14 | ++++++++++++++ |
A | gpx2yaml.c | | | 136 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.c | | | 35 | +++++++++++++++++++++++++++++++++++ |
A | util.h | | | 2 | ++ |
A | xml.c | | | 233 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | xml.h | | | 42 | ++++++++++++++++++++++++++++++++++++++++++ |
9 files changed, 495 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+gpx2yaml
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2021 Nikolay Korotkiy <sikmir@disroot.org>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,16 @@
+PREFIX = /usr/local
+
+BIN = gpx2yaml
+
+all: build
+
+build: clean
+ $(CC) -c xml.c util.c gpx2yaml.c ${CFLAGS}
+ $(CC) -o $(BIN) xml.o util.o gpx2yaml.o ${LDFLAGS}
+
+clean:
+ rm -f $(BIN) *.o
+
+install: all
+ mkdir -p "${DESTDIR}${PREFIX}/bin"
+ cp -f ${BIN} "${DESTDIR}${PREFIX}/bin"
diff --git a/README.md b/README.md
@@ -0,0 +1,14 @@
+gpx2yaml
+--------
+
+Convert GPX to YAML.
+
+Caveats
+-------
+
+It is not a full-featured GPX converter.
+
+License
+-------
+
+ISC, see LICENSE file.
diff --git a/gpx2yaml.c b/gpx2yaml.c
@@ -0,0 +1,136 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "util.h"
+#include "xml.h"
+
+struct pos {
+ char lat[16];
+ char lon[16];
+};
+
+static XMLParser parser; /* XML parser state */
+static char curelement[16];
+static char curgpxelement[16];
+static struct pos pos;
+static int linkindex;
+static int trksegindex;
+
+static int
+istag(const char *s1, const char *s2)
+{
+ return !strcasecmp(s1, s2);
+}
+
+static int
+isattr(const char *s1, const char *s2)
+{
+ return !strcasecmp(s1, s2);
+}
+
+void
+xml_handler_start_element(XMLParser *p, const char *tag, size_t taglen)
+{
+ if (istag(tag, "gpx")) {
+ printf("---\n");
+ printf("type: FeatureCollection\n");
+ printf("features:\n");
+ } else if (istag(tag, "wpt")) {
+ strlcpy(curgpxelement, tag, sizeof(curgpxelement));
+ memset(&pos, 0, sizeof(pos));
+ linkindex = 1;
+ printf("- type: Feature\n");
+ printf(" properties:\n");
+ } else if (istag(tag, "trk")) {
+ strlcpy(curgpxelement, tag, sizeof(curgpxelement));
+ linkindex = 1;
+ trksegindex = 1;
+ printf("- type: Feature\n");
+ printf(" properties:\n");
+ } else if (istag(tag, "name") || istag(tag, "desc") || istag(tag, "type") || istag(tag, "text")) {
+ strlcpy(curelement, tag, sizeof(curelement));
+ } else if (istag(tag, "trkseg")) {
+ if (trksegindex == 1) {
+ printf(" geometry:\n");
+ printf(" type: LineString\n");
+ printf(" coordinates:\n");
+ }
+ trksegindex++;
+ } else if (istag(tag, "trkpt")) {
+ memset(&pos, 0, sizeof(pos));
+ }
+}
+
+void
+xml_handler_end_element(XMLParser *p, const char *tag, size_t taglen, int isshort)
+{
+ if (istag(tag, "wpt")) {
+ curgpxelement[0] = '\0';
+ printf(" geometry:\n");
+ printf(" type: Point\n");
+ printf(" coordinates:\n");
+ printf(" - %s\n", pos.lon);
+ printf(" - %s\n", pos.lat);
+ } else if (istag(tag, "trk")) {
+ curgpxelement[0] = '\0';
+ } else if (istag(tag, "name") || istag(tag, "desc") || istag(tag, "type") || istag(tag, "text")) {
+ curelement[0] = '\0';
+ } else if (istag(tag, "link")) {
+ linkindex++;
+ } else if (istag(tag, "trkpt")) {
+ printf(" - - %s\n", pos.lon);
+ printf(" - %s\n", pos.lat);
+ }
+}
+
+void
+xml_handler_attr(XMLParser *p, const char *tag, size_t taglen, const char *name, size_t namelen,
+ const char *value, size_t valuelen)
+{
+ if (strcmp(curgpxelement, "wpt") != 0 && strcmp(curgpxelement, "trk") != 0)
+ return;
+
+ if (istag(tag, "wpt") || istag(tag, "trkpt")) {
+ if (isattr(name, "lat")) {
+ strlcpy(pos.lat, value, sizeof(pos.lat));
+ } else if (isattr(name, "lon")) {
+ strlcpy(pos.lon, value, sizeof(pos.lon));
+ }
+ } else if (istag(tag, "link")) {
+ if (isattr(name, "href")) {
+ printf(" link%d_href: \"%s\"\n", linkindex, value);
+ }
+ }
+}
+
+void
+xml_handler_data(XMLParser *p, const char *data, size_t datalen)
+{
+ if (strcmp(curgpxelement, "wpt") != 0 && strcmp(curgpxelement, "trk") != 0)
+ return;
+
+ if (strcmp(curelement, "name") == 0) {
+ printf(" name: \"%s\"\n", data);
+ } else if (strcmp(curelement, "desc") == 0) {
+ printf(" desc: \"%s\"\n", data);
+ } else if (strcmp(curelement, "type") == 0) {
+ printf(" type: \"%s\"\n", data);
+ } else if (strcmp(curelement, "text") == 0) {
+ printf(" link%d_text: \"%s\"\n", linkindex, data);
+ }
+}
+
+int
+main(void)
+{
+ parser.xmltagstart = xml_handler_start_element;
+ parser.xmltagend = xml_handler_end_element;
+ parser.xmlattr = xml_handler_attr;
+ parser.xmldata = xml_handler_data;
+
+ parser.getnext = getchar;
+
+ xml_parse(&parser);
+
+ return 0;
+}
diff --git a/util.c b/util.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "util.h"
+
+/*
+ * Taken from OpenBSD.
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz) {
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+ /* not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,2 @@
+#undef strlcpy
+size_t strlcpy(char *, const char *, size_t);
diff --git a/xml.c b/xml.c
@@ -0,0 +1,233 @@
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xml.h"
+
+static void
+xml_parseattrs(XMLParser *x)
+{
+ size_t namelen = 0, valuelen;
+ int c, endsep, endname = 0, valuestart = 0;
+
+ while ((c = GETNEXT()) != EOF) {
+ if (isspace(c)) {
+ if (namelen)
+ endname = 1;
+ continue;
+ } else if (c == '?')
+ ; /* ignore */
+ else if (c == '=') {
+ x->name[namelen] = '\0';
+ valuestart = 1;
+ endname = 1;
+ } else if (namelen && ((endname && !valuestart && isalpha(c)) || (c == '>' || c == '/'))) {
+ /* attribute without value */
+ x->name[namelen] = '\0';
+ if (x->xmlattrstart)
+ x->xmlattrstart(x, x->tag, x->taglen, x->name, namelen);
+ if (x->xmlattr)
+ x->xmlattr(x, x->tag, x->taglen, x->name, namelen, "", 0);
+ if (x->xmlattrend)
+ x->xmlattrend(x, x->tag, x->taglen, x->name, namelen);
+ endname = 0;
+ x->name[0] = c;
+ namelen = 1;
+ } else if (namelen && valuestart) {
+ /* attribute with value */
+ if (x->xmlattrstart)
+ x->xmlattrstart(x, x->tag, x->taglen, x->name, namelen);
+
+ valuelen = 0;
+ if (c == '\'' || c == '"') {
+ endsep = c;
+ } else {
+ endsep = ' '; /* isspace() */
+ goto startvalue;
+ }
+
+ while ((c = GETNEXT()) != EOF) {
+startvalue:
+ if (c == '&') { /* entities */
+ x->data[valuelen] = '\0';
+ /* call data function with data before entity if there is data */
+ if (valuelen && x->xmlattr)
+ x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen);
+ x->data[0] = c;
+ valuelen = 1;
+ while ((c = GETNEXT()) != EOF) {
+ if (c == endsep || (endsep == ' ' && (c == '>' || isspace(c))))
+ break;
+ if (valuelen < sizeof(x->data) - 1)
+ x->data[valuelen++] = c;
+ else {
+ /* entity too long for buffer, handle as normal data */
+ x->data[valuelen] = '\0';
+ if (x->xmlattr)
+ x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen);
+ x->data[0] = c;
+ valuelen = 1;
+ break;
+ }
+ if (c == ';') {
+ x->data[valuelen] = '\0';
+ if (x->xmlattrentity)
+ x->xmlattrentity(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen);
+ valuelen = 0;
+ break;
+ }
+ }
+ } else if (c != endsep && !(endsep == ' ' && (c == '>' || isspace(c)))) {
+ if (valuelen < sizeof(x->data) - 1) {
+ x->data[valuelen++] = c;
+ } else {
+ x->data[valuelen] = '\0';
+ if (x->xmlattr)
+ x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen);
+ x->data[0] = c;
+ valuelen = 1;
+ }
+ }
+ if (c == endsep || (endsep == ' ' && (c == '>' || isspace(c)))) {
+ x->data[valuelen] = '\0';
+ if (x->xmlattr)
+ x->xmlattr(x, x->tag, x->taglen, x->name, namelen, x->data, valuelen);
+ if (x->xmlattrend)
+ x->xmlattrend(x, x->tag, x->taglen, x->name, namelen);
+ break;
+ }
+ }
+ namelen = endname = valuestart = 0;
+ } else if (namelen < sizeof(x->name) - 1) {
+ x->name[namelen++] = c;
+ }
+ if (c == '>') {
+ break;
+ } else if (c == '/') {
+ x->isshorttag = 1;
+ x->name[0] = '\0';
+ namelen = 0;
+ }
+ }
+}
+
+void
+xml_parse(XMLParser *x)
+{
+ size_t datalen, tagdatalen;
+ int c, isend;
+
+ while ((c = GETNEXT()) != EOF && c != '<')
+ ; /* skip until < */
+
+ while (c != EOF) {
+ if (c == '<') { /* parse tag */
+ if ((c = GETNEXT()) == EOF)
+ return;
+
+ /* normal tag (open, short open, close), processing instruction. */
+ x->tag[0] = c;
+ x->taglen = 1;
+ x->isshorttag = isend = 0;
+
+ /* treat processing instruction as shorttag, don't strip "?" prefix. */
+ if (c == '?') {
+ x->isshorttag = 1;
+ } else if (c == '/') {
+ if ((c = GETNEXT()) == EOF)
+ return;
+ x->tag[0] = c;
+ isend = 1;
+ }
+
+ while ((c = GETNEXT()) != EOF) {
+ if (c == '/')
+ x->isshorttag = 1; /* short tag */
+ else if (c == '>' || isspace(c)) {
+ x->tag[x->taglen] = '\0';
+ if (isend) { /* end tag, starts with </ */
+ if (x->xmltagend)
+ x->xmltagend(x, x->tag, x->taglen, x->isshorttag);
+ x->tag[0] = '\0';
+ x->taglen = 0;
+ } else {
+ /* start tag */
+ if (x->xmltagstart)
+ x->xmltagstart(x, x->tag, x->taglen);
+ if (isspace(c))
+ xml_parseattrs(x);
+ if (x->xmltagstartparsed)
+ x->xmltagstartparsed(x, x->tag, x->taglen, x->isshorttag);
+ }
+ /* call tagend for shortform or processing instruction */
+ if (x->isshorttag) {
+ if (x->xmltagend)
+ x->xmltagend(x, x->tag, x->taglen, x->isshorttag);
+ x->tag[0] = '\0';
+ x->taglen = 0;
+ }
+ break;
+ } else if (x->taglen < sizeof(x->tag) - 1)
+ x->tag[x->taglen++] = c; /* NOTE: tag name truncation */
+ }
+ } else {
+ /* parse tag data */
+ datalen = 0;
+ if (x->xmldatastart)
+ x->xmldatastart(x);
+ while ((c = GETNEXT()) != EOF) {
+ if (c == '&') {
+ if (datalen) {
+ x->data[datalen] = '\0';
+ if (x->xmldata)
+ x->xmldata(x, x->data, datalen);
+ }
+ x->data[0] = c;
+ datalen = 1;
+ while ((c = GETNEXT()) != EOF) {
+ if (c == '<')
+ break;
+ if (datalen < sizeof(x->data) - 1)
+ x->data[datalen++] = c;
+ else {
+ /* entity too long for buffer, handle as normal data */
+ x->data[datalen] = '\0';
+ if (x->xmldata)
+ x->xmldata(x, x->data, datalen);
+ x->data[0] = c;
+ datalen = 1;
+ break;
+ }
+ if (c == ';') {
+ x->data[datalen] = '\0';
+ if (x->xmldataentity)
+ x->xmldataentity(x, x->data, datalen);
+ datalen = 0;
+ break;
+ }
+ }
+ } else if (c != '<') {
+ if (datalen < sizeof(x->data) - 1) {
+ x->data[datalen++] = c;
+ } else {
+ x->data[datalen] = '\0';
+ if (x->xmldata)
+ x->xmldata(x, x->data, datalen);
+ x->data[0] = c;
+ datalen = 1;
+ }
+ }
+ if (c == '<') {
+ x->data[datalen] = '\0';
+ if (x->xmldata && datalen)
+ x->xmldata(x, x->data, datalen);
+ if (x->xmldataend)
+ x->xmldataend(x);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/xml.h b/xml.h
@@ -0,0 +1,42 @@
+#ifndef _XML_H_
+#define _XML_H_
+
+#include <stdio.h>
+
+typedef struct xmlparser {
+ /* handlers */
+ void (*xmlattr)(struct xmlparser *, const char *, size_t,
+ const char *, size_t, const char *, size_t);
+ void (*xmlattrend)(struct xmlparser *, const char *, size_t,
+ const char *, size_t);
+ void (*xmlattrstart)(struct xmlparser *, const char *, size_t,
+ const char *, size_t);
+ void (*xmlattrentity)(struct xmlparser *, const char *, size_t,
+ const char *, size_t, const char *, size_t);
+ void (*xmldata)(struct xmlparser *, const char *, size_t);
+ void (*xmldataend)(struct xmlparser *);
+ void (*xmldataentity)(struct xmlparser *, const char *, size_t);
+ void (*xmldatastart)(struct xmlparser *);
+ void (*xmltagend)(struct xmlparser *, const char *, size_t, int);
+ void (*xmltagstart)(struct xmlparser *, const char *, size_t);
+ void (*xmltagstartparsed)(struct xmlparser *, const char *,
+ size_t, int);
+
+#ifndef GETNEXT
+ #define GETNEXT (x)->getnext
+ int (*getnext)(void);
+#endif
+
+ /* current tag */
+ char tag[1024];
+ size_t taglen;
+ /* current tag is in short form ? <tag /> */
+ int isshorttag;
+ /* current attribute name */
+ char name[1024];
+ /* data buffer used for tag data, cdata and attribute data */
+ char data[BUFSIZ];
+} XMLParser;
+
+void xml_parse(XMLParser *);
+#endif