gpx2yaml

GPX to YAML converter
Log | Files | Refs | README | LICENSE

commit 69bdf1e0a07ed37961a6c75e6145dc8f98d68322
Author: Nikolay Korotkiy <sikmir@gmail.com>
Date:   Mon, 16 Aug 2021 22:03:06 +0300

initial repo

Diffstat:
A.gitignore | 2++
ALICENSE | 15+++++++++++++++
AMakefile | 16++++++++++++++++
AREADME.md | 14++++++++++++++
Agpx2yaml.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.c | 35+++++++++++++++++++++++++++++++++++
Autil.h | 2++
Axml.c | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Axml.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