summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--gps.h13
-rw-r--r--libgps_core.c559
-rw-r--r--libgps_shm.c2
-rw-r--r--libgps_sock.c564
5 files changed, 662 insertions, 477 deletions
diff --git a/Makefile.am b/Makefile.am
index 4658ad82..86c626e2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -157,6 +157,7 @@ libgps_c_sources = \
libgps_core.c \
libgps_json.c \
libgps_shm.c \
+ libgps_sock.c \
netlib.c \
rtcm2_json.c \
shared_json.c \
diff --git a/gps.h b/gps.h
index 012dd053..b988129f 100644
--- a/gps.h
+++ b/gps.h
@@ -1433,18 +1433,20 @@ extern int gps_open(/*@null@*/const char *, /*@null@*/const char *,
extern int gps_close(struct gps_data_t *);
extern int gps_send(struct gps_data_t *, const char *, ... );
extern int gps_read(/*@out@*/struct gps_data_t *);
+extern int gps_unpack(char *, struct gps_data_t *);
extern bool gps_waiting(struct gps_data_t *, int);
extern int gps_stream(struct gps_data_t *, unsigned int, /*@null@*/void *);
extern const char /*@observer@*/ *gps_data(struct gps_data_t *);
extern const char /*@observer@*/ *gps_errstr(const int);
+extern int gps_sock_open(/*@null@*/const char *, /*@null@*/const char *,
+ /*@out@*/struct gps_data_t *);
+extern int gps_sock_read(/*@out@*/struct gps_data_t *);
+extern int gps_sock_close(struct gps_data_t *);
extern int gps_shm_open(/*@out@*/struct gps_data_t *);
extern int gps_shm_read(struct gps_data_t *);
extern void gps_shm_close(struct gps_data_t *);
-/* this only needs to be visible for the unit tests */
-extern int gps_unpack(char *, struct gps_data_t *);
-
/* dependencies on struct gpsdata_t end hrere */
extern void gps_clear_fix(/*@ out @*/struct gps_fix_t *);
@@ -1492,10 +1494,15 @@ extern double wgs84_separation(double, double);
#define NL_NOSOCK -4 /* can't create socket */
#define NL_NOSOCKOPT -5 /* error SETSOCKOPT SO_REUSEADDR */
#define NL_NOCONNECT -6 /* can't connect to host/socket pair */
+#define SHM_NOSHARED -7 /* shared-memory segment not available */
+#define SHM_NOATTACH -8 /* shared-memory attach failed */
#define DEFAULT_GPSD_PORT "2947" /* IANA assignment */
#define DEFAULT_RTCM_PORT "2101" /* IANA assignment */
+/* special host values for non-socket exports */
+#define GPSD_SHARED_MEMORY "shared memory"
+
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
diff --git a/libgps_core.c b/libgps_core.c
index 9983f8a6..19af31d8 100644
--- a/libgps_core.c
+++ b/libgps_core.c
@@ -56,9 +56,6 @@ struct privdata_t
#define DEBUG_JSON 5 /* minimum level for verbose JSON debugging */
static int debuglevel = 0;
static FILE *debugfp;
-#ifndef USE_QT
-static int waitcount = 0;
-#endif /* USE_QT */
void gps_enable_debug(int level, FILE * fp)
/* control the level and destination of debug trace messages */
@@ -158,376 +155,8 @@ int gps_close(struct gps_data_t *gpsdata)
return 0;
}
-
/*@+compdef +usereleased@*/
-#ifdef LIBGPS_DEBUG
-static void libgps_dump_state(struct gps_data_t *collect)
-{
- const char *status_values[] = { "NO_FIX", "FIX", "DGPS_FIX" };
- const char *mode_values[] = { "", "NO_FIX", "MODE_2D", "MODE_3D" };
-
- /* no need to dump the entire state, this is a sanity check */
-#ifndef USE_QT
- /* will fail on a 32-bit macine */
- (void)fprintf(debugfp, "flags: (0x%04x) %s\n",
- (unsigned int)collect->set, gps_maskdump(collect->set));
-#endif
- if (collect->set & ONLINE_SET)
- (void)fprintf(debugfp, "ONLINE: %lf\n", collect->online);
- if (collect->set & TIME_SET)
- (void)fprintf(debugfp, "TIME: %lf\n", collect->fix.time);
- if (collect->set & LATLON_SET)
- (void)fprintf(debugfp, "LATLON: lat/lon: %lf %lf\n",
- collect->fix.latitude, collect->fix.longitude);
- if (collect->set & ALTITUDE_SET)
- (void)fprintf(debugfp, "ALTITUDE: altitude: %lf U: climb: %lf\n",
- collect->fix.altitude, collect->fix.climb);
- if (collect->set & SPEED_SET)
- (void)fprintf(debugfp, "SPEED: %lf\n", collect->fix.speed);
- if (collect->set & TRACK_SET)
- (void)fprintf(debugfp, "TRACK: track: %lf\n", collect->fix.track);
- if (collect->set & CLIMB_SET)
- (void)fprintf(debugfp, "CLIMB: climb: %lf\n", collect->fix.climb);
- if (collect->set & STATUS_SET)
- (void)fprintf(debugfp, "STATUS: status: %d (%s)\n",
- collect->status, status_values[collect->status]);
- if (collect->set & MODE_SET)
- (void)fprintf(debugfp, "MODE: mode: %d (%s)\n",
- collect->fix.mode, mode_values[collect->fix.mode]);
- if (collect->set & DOP_SET)
- (void)fprintf(debugfp,
- "DOP: satellites %d, pdop=%lf, hdop=%lf, vdop=%lf\n",
- collect->satellites_used, collect->dop.pdop,
- collect->dop.hdop, collect->dop.vdop);
- if (collect->set & VERSION_SET)
- (void)fprintf(debugfp, "VERSION: release=%s rev=%s proto=%d.%d\n",
- collect->version.release,
- collect->version.rev,
- collect->version.proto_major,
- collect->version.proto_minor);
- if (collect->set & POLICY_SET)
- (void)fprintf(debugfp,
- "POLICY: watcher=%s nmea=%s raw=%d scaled=%s timing=%s, devpath=%s\n",
- collect->policy.watcher ? "true" : "false",
- collect->policy.nmea ? "true" : "false",
- collect->policy.raw,
- collect->policy.scaled ? "true" : "false",
- collect->policy.timing ? "true" : "false",
- collect->policy.devpath);
- if (collect->set & SATELLITE_SET) {
- int i;
-
- (void)fprintf(debugfp, "SKY: satellites in view: %d\n",
- collect->satellites_visible);
- for (i = 0; i < collect->satellites_visible; i++) {
- (void)fprintf(debugfp, " %2.2d: %2.2d %3.3d %3.0f %c\n",
- collect->PRN[i], collect->elevation[i],
- collect->azimuth[i], collect->ss[i],
- collect->used[i] ? 'Y' : 'N');
- }
- }
- if (collect->set & DEVICE_SET)
- (void)fprintf(debugfp, "DEVICE: Device is '%s', driver is '%s'\n",
- collect->dev.path, collect->dev.driver);
-#ifdef OLDSTYLE_ENABLE
- if (collect->set & DEVICEID_SET)
- (void)fprintf(debugfp, "GPSD ID is %s\n", collect->dev.subtype);
-#endif /* OLDSTYLE_ENABLE */
- if (collect->set & DEVICELIST_SET) {
- int i;
- (void)fprintf(debugfp, "DEVICELIST:%d devices:\n",
- collect->devices.ndevices);
- for (i = 0; i < collect->devices.ndevices; i++) {
- (void)fprintf(debugfp, "%d: path='%s' driver='%s'\n",
- collect->devices.ndevices,
- collect->devices.list[i].path,
- collect->devices.list[i].driver);
- }
- }
-
-}
-#endif /* LIBGPS_DEBUG */
-
-
-/*@ -branchstate -usereleased -mustfreefresh -nullstate -usedef @*/
-int gps_unpack(char *buf, struct gps_data_t *gpsdata)
-/* unpack a gpsd response into a status structure, buf must be writeable.
- * gps_unpack() currently returns 0 in all cases, but should it ever need to
- * return an error status, it must be < 0.
- */
-{
- libgps_debug_trace((DEBUG_CALLS, "gps_unpack(%s)\n", buf));
-
- /* detect and process a JSON response */
- if (buf[0] == '{') {
- const char *jp = buf, **next = &jp;
- while (next != NULL && *next != NULL && next[0][0] != '\0') {
- libgps_debug_trace((DEBUG_CALLS,
- "gps_unpack() segment parse '%s'\n", *next));
- if (libgps_json_unpack(*next, gpsdata, next) == -1)
- break;
-#ifdef LIBGPS_DEBUG
- if (debuglevel >= 1)
- libgps_dump_state(gpsdata);
-#endif /* LIBGPS_DEBUG */
-
- }
-#ifdef OLDSTYLE_ENABLE
- if (PRIVATE(gpsdata) != NULL)
- PRIVATE(gpsdata)->newstyle = true;
-#endif /* OLDSTYLE_ENABLE */
- }
-#ifdef OLDSTYLE_ENABLE
- else {
- /*
- * Get the decimal separator for the current application locale.
- * This looks thread-unsafe, but it's not. The key is that
- * character assignment is atomic.
- */
- char *ns, *sp, *tp;
-
- static char decimal_point = '\0';
- if (decimal_point == '\0') {
- struct lconv *locale_data = localeconv();
- if (locale_data != NULL && locale_data->decimal_point[0] != '.')
- decimal_point = locale_data->decimal_point[0];
- }
-
- for (ns = buf; ns; ns = strstr(ns + 1, "GPSD")) {
- if ( /*@i1@*/ strncmp(ns, "GPSD", 4) == 0) {
- bool eol = false;
- /* the following should execute each time we have a good next sp */
- for (sp = ns + 5; *sp != '\0'; sp = tp + 1) {
- tp = sp + strcspn(sp, ",\r\n");
- eol = *tp == '\r' || *tp == '\n';
- if (*tp == '\0')
- tp--;
- else
- *tp = '\0';
-
- /*
- * The daemon always emits the Anglo-American and SI
- * decimal point. Hack these into whatever the
- * application locale requires if it's not the same.
- * This has to happen *after* we grab the next
- * comma-delimited response, or we'll lose horribly
- * in locales where the decimal separator is comma.
- */
- if (decimal_point != '\0') {
- char *cp;
- for (cp = sp; cp < tp; cp++)
- if (*cp == '.')
- *cp = decimal_point;
- }
-
- /* note, there's a bit of skip logic after the switch */
-
- switch (*sp) {
- case 'F': /*@ -mustfreeonly */
- if (sp[2] == '?')
- gpsdata->dev.path[0] = '\0';
- else {
- /*@ -mayaliasunique @*/
- strncpy(gpsdata->dev.path, sp + 2,
- sizeof(gpsdata->dev.path));
- /*@ +mayaliasunique @*/
- gpsdata->set |= DEVICE_SET;
- }
- /*@ +mustfreeonly */
- break;
- case 'I':
- /*@ -mustfreeonly */
- if (sp[2] == '?')
- gpsdata->dev.subtype[0] = '\0';
- else {
- (void)strlcpy(gpsdata->dev.subtype, sp + 2,
- sizeof(gpsdata->dev.subtype));
- gpsdata->set |= DEVICEID_SET;
- }
- /*@ +mustfreeonly */
- break;
- case 'O':
- if (sp[2] == '?') {
- gpsdata->set = MODE_SET | STATUS_SET;
- gpsdata->status = STATUS_NO_FIX;
- gps_clear_fix(&gpsdata->fix);
- } else {
- struct gps_fix_t nf;
- char tag[MAXTAGLEN + 1], alt[20];
- char eph[20], epv[20], track[20], speed[20],
- climb[20];
- char epd[20], eps[20], epc[20], mode[2];
- char timestr[20], ept[20], lat[20], lon[20];
- int st = sscanf(sp + 2,
- "%8s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %1s",
- tag, timestr, ept, lat, lon,
- alt, eph, epv, track, speed,
- climb,
- epd, eps, epc, mode);
- if (st >= 14) {
-#define DEFAULT(val) (val[0] == '?') ? NAN : atof(val)
- /*@ +floatdouble @*/
- nf.time = DEFAULT(timestr);
- nf.latitude = DEFAULT(lat);
- nf.longitude = DEFAULT(lon);
- nf.ept = DEFAULT(ept);
- nf.altitude = DEFAULT(alt);
- /* designed before we split eph into epx+epy */
- nf.epx = nf.epy = DEFAULT(eph) / sqrt(2);
- nf.epv = DEFAULT(epv);
- nf.track = DEFAULT(track);
- nf.speed = DEFAULT(speed);
- nf.climb = DEFAULT(climb);
- nf.epd = DEFAULT(epd);
- nf.eps = DEFAULT(eps);
- nf.epc = DEFAULT(epc);
- /*@ -floatdouble @*/
-#undef DEFAULT
- if (st >= 15)
- nf.mode =
- (mode[0] ==
- '?') ? MODE_NOT_SEEN : atoi(mode);
- else
- nf.mode =
- (alt[0] == '?') ? MODE_2D : MODE_3D;
- if (alt[0] != '?')
- gpsdata->set |= ALTITUDE_SET | CLIMB_SET;
- if (isnan(nf.epx) == 0 && isnan(nf.epy) == 0)
- gpsdata->set |= HERR_SET;
- if (isnan(nf.epv) == 0)
- gpsdata->set |= VERR_SET;
- if (isnan(nf.track) == 0)
- gpsdata->set |= TRACK_SET | SPEED_SET;
- if (isnan(nf.eps) == 0)
- gpsdata->set |= SPEEDERR_SET;
- if (isnan(nf.epc) == 0)
- gpsdata->set |= CLIMBERR_SET;
- gpsdata->fix = nf;
- (void)strlcpy(gpsdata->tag, tag,
- MAXTAGLEN + 1);
- gpsdata->set |=
- TIME_SET | TIMERR_SET | LATLON_SET |
- MODE_SET;
- gpsdata->status = STATUS_FIX;
- gpsdata->set |= STATUS_SET;
- }
- }
- break;
- case 'X':
- if (sp[2] == '?')
- gpsdata->online = (timestamp_t)-1;
- else {
- (void)sscanf(sp, "X=%lf", &gpsdata->online);
- gpsdata->set |= ONLINE_SET;
- }
- break;
- case 'Y':
- if (sp[2] == '?') {
- gpsdata->satellites_visible = 0;
- } else {
- int j, i1, i2, i3, i5;
- int PRN[MAXCHANNELS];
- int elevation[MAXCHANNELS], azimuth[MAXCHANNELS];
- int used[MAXCHANNELS];
- double ss[MAXCHANNELS], f4;
- char tag[MAXTAGLEN + 1], timestamp[21];
-
- (void)sscanf(sp, "Y=%8s %20s %d ",
- tag, timestamp,
- &gpsdata->satellites_visible);
- (void)strncpy(gpsdata->tag, tag, MAXTAGLEN);
- if (timestamp[0] != '?') {
- gpsdata->set |= TIME_SET;
- }
- for (j = 0; j < gpsdata->satellites_visible; j++) {
- PRN[j] = elevation[j] = azimuth[j] = used[j] =
- 0;
- ss[j] = 0.0;
- }
- for (j = 0, gpsdata->satellites_used = 0;
- j < gpsdata->satellites_visible; j++) {
- if ((sp != NULL)
- && ((sp = strchr(sp, ':')) != NULL)) {
- sp++;
- (void)sscanf(sp, "%d %d %d %lf %d", &i1,
- &i2, &i3, &f4, &i5);
- PRN[j] = i1;
- elevation[j] = i2;
- azimuth[j] = i3;
- ss[j] = f4;
- used[j] = i5;
- if (i5 == 1)
- gpsdata->satellites_used++;
- }
- }
- /*@ -compdef @*/
- memcpy(gpsdata->PRN, PRN, sizeof(PRN));
- memcpy(gpsdata->elevation, elevation,
- sizeof(elevation));
- memcpy(gpsdata->azimuth, azimuth,
- sizeof(azimuth));
- memcpy(gpsdata->ss, ss, sizeof(ss));
- memcpy(gpsdata->used, used, sizeof(used));
- /*@ +compdef @*/
- }
- gpsdata->set |= SATELLITE_SET;
- break;
- }
-
-#ifdef LIBGPS_DEBUG
- if (debuglevel >= 1)
- libgps_dump_state(gpsdata);
-#endif /* LIBGPS_DEBUG */
-
- /*
- * Skip to next GPSD when we see \r or \n;
- * we don't want to try interpreting stuff
- * in between that might be raw mode data.
- */
- if (eol)
- break;
- }
- }
- }
- }
-#endif /* OLDSTYLE_ENABLE */
-
-#ifndef USE_QT
- libgps_debug_trace((DEBUG_CALLS, "final flags: (0x%04x) %s\n", gpsdata->set,
- gps_maskdump(gpsdata->set)));
-#endif
- return 0;
-}
-
-/*@ +compdef @*/
-/*@ -branchstate +usereleased +mustfreefresh +nullstate +usedef @*/
-
-bool gps_waiting(struct gps_data_t * gpsdata, int timeout)
-/* is there input waiting from the GPS? */
-{
-#ifndef USE_QT
- fd_set rfds;
- struct timeval tv;
-
- libgps_debug_trace((DEBUG_CALLS, "gps_waiting(%d): %d\n", timeout, waitcount++));
- if (PRIVATE(gpsdata)->waiting > 0)
- return true;
-
- /* we might want to check for EINTR if this returns false */
- errno = 0;
-
- FD_ZERO(&rfds);
- FD_SET(gpsdata->gps_fd, &rfds);
- tv.tv_sec = timeout / 1000000;
- tv.tv_usec = timeout % 1000000;
- /* all error conditions return "not waiting" -- crude but effective */
- return (select(gpsdata->gps_fd + 1, &rfds, NULL, NULL, &tv) == 1);
-#else
- return ((QTcpSocket *) (gpsdata->gps_fd))->waitForReadyRead(timeout / 1000);
-#endif
-}
-
/*@-compdef -usedef -uniondef@*/
int gps_read(/*@out@*/struct gps_data_t *gpsdata)
/* wait for and read data being streamed from the daemon */
@@ -606,108 +235,6 @@ int gps_read(/*@out@*/struct gps_data_t *gpsdata)
}
/*@+compdef -usedef +uniondef@*/
-const char /*@observer@*/ *gps_data(struct gps_data_t *gpsdata)
-/* return the contents of the client data buffer */
-{
- return PRIVATE(gpsdata)->buffer;
-}
-
-int gps_send(struct gps_data_t *gpsdata, const char *fmt, ...)
-/* send a command to the gpsd instance */
-{
- char buf[BUFSIZ];
- va_list ap;
-
- va_start(ap, fmt);
- (void)vsnprintf(buf, sizeof(buf) - 2, fmt, ap);
- va_end(ap);
- if (buf[strlen(buf) - 1] != '\n')
- (void)strlcat(buf, "\n", BUFSIZ);
-#ifndef USE_QT
- if (write(gpsdata->gps_fd, buf, strlen(buf)) == (ssize_t) strlen(buf))
- return 0;
- else
- return -1;
-#else
- QTcpSocket *sock = (QTcpSocket *) gpsdata->gps_fd;
- sock->write(buf, strlen(buf));
- if (sock->waitForBytesWritten())
- return 0;
- else {
- qDebug() << "libgps::send error: " << sock->errorString();
- return -1;
- }
-#endif
-}
-
-int gps_stream(struct gps_data_t *gpsdata, unsigned int flags,
- /*@null@*/ void *d)
-/* ask gpsd to stream reports at you, hiding the command details */
-{
- char buf[GPS_JSON_COMMAND_MAX];
-
- if ((flags & (WATCH_JSON | WATCH_OLDSTYLE | WATCH_NMEA | WATCH_RAW)) == 0) {
- flags |= WATCH_JSON;
- }
- if ((flags & WATCH_DISABLE) != 0) {
- if ((flags & WATCH_OLDSTYLE) != 0) {
- (void)strlcpy(buf, "w-", sizeof(buf));
- if ((flags & WATCH_NMEA) != 0)
- (void)strlcat(buf, "r-", sizeof(buf));
- } else {
- (void)strlcpy(buf, "?WATCH={\"enable\":false,", sizeof(buf));
- if (flags & WATCH_JSON)
- (void)strlcat(buf, "\"json\":false,", sizeof(buf));
- if (flags & WATCH_NMEA)
- (void)strlcat(buf, "\"nmea\":false,", sizeof(buf));
- if (flags & WATCH_RAW)
- (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
- if (flags & WATCH_RARE)
- (void)strlcat(buf, "\"raw\":0,", sizeof(buf));
- if (flags & WATCH_SCALED)
- (void)strlcat(buf, "\"scaled\":false,", sizeof(buf));
- if (flags & WATCH_TIMING)
- (void)strlcat(buf, "\"timing\":false,", sizeof(buf));
- if (buf[strlen(buf) - 1] == ',')
- buf[strlen(buf) - 1] = '\0';
- (void)strlcat(buf, "};", sizeof(buf));
- }
- libgps_debug_trace((DEBUG_CALLS, "gps_stream() disable command: %s\n", buf));
- return gps_send(gpsdata, buf);
- } else { /* if ((flags & WATCH_ENABLE) != 0) */
-
- if ((flags & WATCH_OLDSTYLE) != 0) {
- (void)strlcpy(buf, "w+x", sizeof(buf));
- if ((flags & WATCH_NMEA) != 0)
- (void)strlcat(buf, "r+", sizeof(buf));
- } else {
- (void)strlcpy(buf, "?WATCH={\"enable\":true,", sizeof(buf));
- if (flags & WATCH_JSON)
- (void)strlcat(buf, "\"json\":true,", sizeof(buf));
- if (flags & WATCH_NMEA)
- (void)strlcat(buf, "\"nmea\":true,", sizeof(buf));
- if (flags & WATCH_RARE)
- (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
- if (flags & WATCH_RAW)
- (void)strlcat(buf, "\"raw\":2,", sizeof(buf));
- if (flags & WATCH_SCALED)
- (void)strlcat(buf, "\"scaled\":true,", sizeof(buf));
- if (flags & WATCH_TIMING)
- (void)strlcat(buf, "\"timing\":true,", sizeof(buf));
- /*@-nullpass@*//* shouldn't be needed, splint has a bug */
- if (flags & WATCH_DEVICE)
- (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "\"device\":\"%s\",", (char *)d);
- /*@+nullpass@*/
- if (buf[strlen(buf) - 1] == ',')
- buf[strlen(buf) - 1] = '\0';
- (void)strlcat(buf, "};", sizeof(buf));
- }
- libgps_debug_trace((DEBUG_CALLS, "gps_stream() enable command: %s\n", buf));
- return gps_send(gpsdata, buf);
- }
-}
-
extern const char /*@observer@*/ *gps_errstr(const int err)
{
/*
@@ -735,6 +262,92 @@ extern const char /*@observer@*/ *gps_errstr(const int err)
#include <getopt.h>
#include <signal.h>
+static void libgps_dump_state(struct gps_data_t *collect)
+{
+ const char *status_values[] = { "NO_FIX", "FIX", "DGPS_FIX" };
+ const char *mode_values[] = { "", "NO_FIX", "MODE_2D", "MODE_3D" };
+
+ /* no need to dump the entire state, this is a sanity check */
+#ifndef USE_QT
+ /* will fail on a 32-bit macine */
+ (void)fprintf(debugfp, "flags: (0x%04x) %s\n",
+ (unsigned int)collect->set, gps_maskdump(collect->set));
+#endif
+ if (collect->set & ONLINE_SET)
+ (void)fprintf(debugfp, "ONLINE: %lf\n", collect->online);
+ if (collect->set & TIME_SET)
+ (void)fprintf(debugfp, "TIME: %lf\n", collect->fix.time);
+ if (collect->set & LATLON_SET)
+ (void)fprintf(debugfp, "LATLON: lat/lon: %lf %lf\n",
+ collect->fix.latitude, collect->fix.longitude);
+ if (collect->set & ALTITUDE_SET)
+ (void)fprintf(debugfp, "ALTITUDE: altitude: %lf U: climb: %lf\n",
+ collect->fix.altitude, collect->fix.climb);
+ if (collect->set & SPEED_SET)
+ (void)fprintf(debugfp, "SPEED: %lf\n", collect->fix.speed);
+ if (collect->set & TRACK_SET)
+ (void)fprintf(debugfp, "TRACK: track: %lf\n", collect->fix.track);
+ if (collect->set & CLIMB_SET)
+ (void)fprintf(debugfp, "CLIMB: climb: %lf\n", collect->fix.climb);
+ if (collect->set & STATUS_SET)
+ (void)fprintf(debugfp, "STATUS: status: %d (%s)\n",
+ collect->status, status_values[collect->status]);
+ if (collect->set & MODE_SET)
+ (void)fprintf(debugfp, "MODE: mode: %d (%s)\n",
+ collect->fix.mode, mode_values[collect->fix.mode]);
+ if (collect->set & DOP_SET)
+ (void)fprintf(debugfp,
+ "DOP: satellites %d, pdop=%lf, hdop=%lf, vdop=%lf\n",
+ collect->satellites_used, collect->dop.pdop,
+ collect->dop.hdop, collect->dop.vdop);
+ if (collect->set & VERSION_SET)
+ (void)fprintf(debugfp, "VERSION: release=%s rev=%s proto=%d.%d\n",
+ collect->version.release,
+ collect->version.rev,
+ collect->version.proto_major,
+ collect->version.proto_minor);
+ if (collect->set & POLICY_SET)
+ (void)fprintf(debugfp,
+ "POLICY: watcher=%s nmea=%s raw=%d scaled=%s timing=%s, devpath=%s\n",
+ collect->policy.watcher ? "true" : "false",
+ collect->policy.nmea ? "true" : "false",
+ collect->policy.raw,
+ collect->policy.scaled ? "true" : "false",
+ collect->policy.timing ? "true" : "false",
+ collect->policy.devpath);
+ if (collect->set & SATELLITE_SET) {
+ int i;
+
+ (void)fprintf(debugfp, "SKY: satellites in view: %d\n",
+ collect->satellites_visible);
+ for (i = 0; i < collect->satellites_visible; i++) {
+ (void)fprintf(debugfp, " %2.2d: %2.2d %3.3d %3.0f %c\n",
+ collect->PRN[i], collect->elevation[i],
+ collect->azimuth[i], collect->ss[i],
+ collect->used[i] ? 'Y' : 'N');
+ }
+ }
+ if (collect->set & DEVICE_SET)
+ (void)fprintf(debugfp, "DEVICE: Device is '%s', driver is '%s'\n",
+ collect->dev.path, collect->dev.driver);
+#ifdef OLDSTYLE_ENABLE
+ if (collect->set & DEVICEID_SET)
+ (void)fprintf(debugfp, "GPSD ID is %s\n", collect->dev.subtype);
+#endif /* OLDSTYLE_ENABLE */
+ if (collect->set & DEVICELIST_SET) {
+ int i;
+ (void)fprintf(debugfp, "DEVICELIST:%d devices:\n",
+ collect->devices.ndevices);
+ for (i = 0; i < collect->devices.ndevices; i++) {
+ (void)fprintf(debugfp, "%d: path='%s' driver='%s'\n",
+ collect->devices.ndevices,
+ collect->devices.list[i].path,
+ collect->devices.list[i].driver);
+ }
+ }
+
+}
+
static void onsig(int sig)
{
(void)fprintf(stderr, "libgps: died with signal %d\n", sig);
diff --git a/libgps_shm.c b/libgps_shm.c
index 99457842..4dccf7cc 100644
--- a/libgps_shm.c
+++ b/libgps_shm.c
@@ -74,7 +74,7 @@ int gps_shm_read(struct gps_data_t *gpsdata)
after = shared->bookend2;
/*@i1@*/gpsdata->privdata = shared;
- return (before == after) ? sizeof(struct gps_data_t) : 0;
+ return (before == after) ? (int)sizeof(struct gps_data_t) : 0;
}
}
diff --git a/libgps_sock.c b/libgps_sock.c
new file mode 100644
index 00000000..5a858060
--- /dev/null
+++ b/libgps_sock.c
@@ -0,0 +1,564 @@
+/* libgps_sock.c -- client interface library for the gpsd daemon
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * BSD terms apply: see the file COPYING in the distribution root for details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <math.h>
+#include <locale.h>
+#include <assert.h>
+#include <sys/time.h> /* expected to have a select(2) prototype a la SuS */
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef S_SPLINT_S
+#include <sys/socket.h>
+#include <unistd.h>
+#endif /* S_SPLINT_S */
+
+#ifndef USE_QT
+#ifndef S_SPLINT_S
+#include <sys/socket.h>
+#endif /* S_SPLINT_S */
+#else
+#include <QTcpSocket>
+#endif /* USE_QT */
+
+#include "gpsd.h"
+#include "gps_json.h"
+
+#ifdef S_SPLINT_S
+extern char *strtok_r(char *, const char *, char **);
+#endif /* S_SPLINT_S */
+
+struct privdata_t
+{
+ bool newstyle;
+ /* data buffered from the last read */
+ ssize_t waiting;
+ char buffer[GPS_JSON_RESPONSE_MAX * 2];
+
+};
+#define PRIVATE(gpsdata) ((struct privdata_t *)gpsdata->privdata)
+
+/*@-branchstate@*/
+int gps_sock_open(/*@null@*/const char *host, /*@null@*/const char *port,
+ /*@out@*/ struct gps_data_t *gpsdata)
+{
+ if (!host)
+ host = "localhost";
+ if (!port)
+ port = DEFAULT_GPSD_PORT;
+
+#ifndef USE_QT
+ if ((gpsdata->gps_fd =
+ netlib_connectsock(AF_UNSPEC, host, port, "tcp")) < 0) {
+ errno = gpsdata->gps_fd;
+ //libgps_debug_trace((DEBUG_CALLS, "netlib_connectsock() returns error %d\n", errno));
+ return -1;
+ }
+ else
+ //libgps_debug_trace((DEBUG_CALLS, "netlib_connectsock() returns socket on fd %d\n", gpsdata->gps_fd));
+#else
+ QTcpSocket *sock = new QTcpSocket();
+ gpsdata->gps_fd = sock;
+ sock->connectToHost(host, QString(port).toInt());
+ if (!sock->waitForConnected())
+ qDebug() << "libgps::connect error: " << sock->errorString();
+ else
+ qDebug() << "libgps::connected!";
+#endif /* USE_QT */
+
+ /* set up for line-buffered I/O over the daemon socket */
+ gpsdata->privdata = (void *)malloc(sizeof(struct privdata_t));
+ if (gpsdata->privdata == NULL)
+ return -1;
+ PRIVATE(gpsdata)->newstyle = false;
+ PRIVATE(gpsdata)->waiting = 0;
+
+ return 0;
+}
+/*@+branchstate@*/
+
+bool gps_waiting(struct gps_data_t * gpsdata, int timeout)
+/* is there input waiting from the GPS? */
+{
+#ifndef USE_QT
+ fd_set rfds;
+ struct timeval tv;
+
+ //libgps_debug_trace((DEBUG_CALLS, "gps_waiting(%d): %d\n", timeout, waitcount++));
+ if (PRIVATE(gpsdata)->waiting > 0)
+ return true;
+
+ /* we might want to check for EINTR if this returns false */
+ errno = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(gpsdata->gps_fd, &rfds);
+ tv.tv_sec = timeout / 1000000;
+ tv.tv_usec = timeout % 1000000;
+ /* all error conditions return "not waiting" -- crude but effective */
+ return (select(gpsdata->gps_fd + 1, &rfds, NULL, NULL, &tv) == 1);
+#else
+ return ((QTcpSocket *) (gpsdata->gps_fd))->waitForReadyRead(timeout / 1000);
+#endif
+}
+
+/*@-usereleased -compdef@*/
+int gps_sock_close(struct gps_data_t *gpsdata)
+/* close a gpsd connection */
+{
+#ifndef USE_QT
+ int status;
+
+ free(PRIVATE(gpsdata));
+ status = close(gpsdata->gps_fd);
+ gpsdata->gps_fd = -1;
+ return status;
+#else
+ QTcpSocket *sock = (QTcpSocket *) gpsdata->gps_fd;
+ sock->disconnectFromHost();
+ delete sock;
+ gpsdata->gps_fd = NULL;
+ return 0;
+#endif
+}
+/*@+usereleased +compdef@*/
+
+/*@-compdef -usedef -uniondef@*/
+int gps_sock_read(/*@out@*/struct gps_data_t *gpsdata)
+/* wait for and read data being streamed from the daemon */
+{
+ char *eol;
+ ssize_t response_length;
+ int status = -1;
+
+ gpsdata->set &= ~PACKET_SET;
+ for (eol = PRIVATE(gpsdata)->buffer;
+ *eol != '\n' && eol < PRIVATE(gpsdata)->buffer + PRIVATE(gpsdata)->waiting; eol++)
+ continue;
+ if (*eol != '\n')
+ eol = NULL;
+
+ errno = 0;
+
+ if (eol == NULL) {
+#ifndef USE_QT
+ /* read data: return -1 if no data waiting or buffered, 0 otherwise */
+ status = (int)recv(gpsdata->gps_fd,
+ PRIVATE(gpsdata)->buffer + PRIVATE(gpsdata)->waiting,
+ sizeof(PRIVATE(gpsdata)->buffer) - PRIVATE(gpsdata)->waiting, 0);
+#else
+ status =
+ ((QTcpSocket *) (gpsdata->gps_fd))->read(PRIVATE(gpsdata)->buffer +
+ PRIVATE(gpsdata)->waiting,
+ sizeof(PRIVATE(gpsdata)->buffer) -
+ PRIVATE(gpsdata)->waiting);
+#endif
+
+ /* if we just received data from the socket, it's in the buffer */
+ if (status > -1)
+ PRIVATE(gpsdata)->waiting += status;
+ /* buffer is empty - implies no data was read */
+ if (PRIVATE(gpsdata)->waiting == 0) {
+ /*
+ * If we received 0 bytes, other side of socket is closing.
+ * Return -1 as end-of-data indication.
+ */
+ if (status == 0)
+ return -1;
+#ifndef USE_QT
+ /* count transient errors as success, we'll retry later */
+ else if (errno == EINTR || errno == EAGAIN
+ || errno == EWOULDBLOCK)
+ return 0;
+#endif
+ /* hard error return of -1, pass it along */
+ else
+ return -1;
+ }
+ /* there's buffered data waiting to be returned */
+ for (eol = PRIVATE(gpsdata)->buffer;
+ *eol != '\n' && eol < PRIVATE(gpsdata)->buffer + PRIVATE(gpsdata)->waiting; eol++)
+ continue;
+ if (*eol != '\n')
+ eol = NULL;
+ if (eol == NULL)
+ return 0;
+ }
+
+ assert(eol != NULL);
+ *eol = '\0';
+ response_length = eol - PRIVATE(gpsdata)->buffer + 1;
+ gpsdata->online = timestamp();
+ status = gps_unpack(PRIVATE(gpsdata)->buffer, gpsdata);
+ /*@+matchanyintegral@*/
+ memmove(PRIVATE(gpsdata)->buffer,
+ PRIVATE(gpsdata)->buffer + response_length, PRIVATE(gpsdata)->waiting - response_length);
+ /*@-matchanyintegral@*/
+ PRIVATE(gpsdata)->waiting -= response_length;
+ gpsdata->set |= PACKET_SET;
+
+ return (status == 0) ? (int)response_length : status;
+}
+/*@+compdef -usedef +uniondef@*/
+
+/*@ -branchstate -usereleased -mustfreefresh -nullstate -usedef @*/
+int gps_unpack(char *buf, struct gps_data_t *gpsdata)
+/* unpack a gpsd response into a status structure, buf must be writeable.
+ * gps_unpack() currently returns 0 in all cases, but should it ever need to
+ * return an error status, it must be < 0.
+ */
+{
+ //libgps_debug_trace((DEBUG_CALLS, "gps_unpack(%s)\n", buf));
+
+ /* detect and process a JSON response */
+ if (buf[0] == '{') {
+ const char *jp = buf, **next = &jp;
+ while (next != NULL && *next != NULL && next[0][0] != '\0') {
+ //libgps_debug_trace((DEBUG_CALLS,"gps_unpack() segment parse '%s'\n", *next));
+ if (libgps_json_unpack(*next, gpsdata, next) == -1)
+ break;
+#ifdef LIBGPS_DEBUG
+ if (debuglevel >= 1)
+ libgps_dump_state(gpsdata);
+#endif /* LIBGPS_DEBUG */
+
+ }
+#ifdef OLDSTYLE_ENABLE
+ if (PRIVATE(gpsdata) != NULL)
+ PRIVATE(gpsdata)->newstyle = true;
+#endif /* OLDSTYLE_ENABLE */
+ }
+#ifdef OLDSTYLE_ENABLE
+ else {
+ /*
+ * Get the decimal separator for the current application locale.
+ * This looks thread-unsafe, but it's not. The key is that
+ * character assignment is atomic.
+ */
+ char *ns, *sp, *tp;
+
+ static char decimal_point = '\0';
+ if (decimal_point == '\0') {
+ struct lconv *locale_data = localeconv();
+ if (locale_data != NULL && locale_data->decimal_point[0] != '.')
+ decimal_point = locale_data->decimal_point[0];
+ }
+
+ for (ns = buf; ns; ns = strstr(ns + 1, "GPSD")) {
+ if ( /*@i1@*/ strncmp(ns, "GPSD", 4) == 0) {
+ bool eol = false;
+ /* the following should execute each time we have a good next sp */
+ for (sp = ns + 5; *sp != '\0'; sp = tp + 1) {
+ tp = sp + strcspn(sp, ",\r\n");
+ eol = *tp == '\r' || *tp == '\n';
+ if (*tp == '\0')
+ tp--;
+ else
+ *tp = '\0';
+
+ /*
+ * The daemon always emits the Anglo-American and SI
+ * decimal point. Hack these into whatever the
+ * application locale requires if it's not the same.
+ * This has to happen *after* we grab the next
+ * comma-delimited response, or we'll lose horribly
+ * in locales where the decimal separator is comma.
+ */
+ if (decimal_point != '\0') {
+ char *cp;
+ for (cp = sp; cp < tp; cp++)
+ if (*cp == '.')
+ *cp = decimal_point;
+ }
+
+ /* note, there's a bit of skip logic after the switch */
+
+ switch (*sp) {
+ case 'F': /*@ -mustfreeonly */
+ if (sp[2] == '?')
+ gpsdata->dev.path[0] = '\0';
+ else {
+ /*@ -mayaliasunique @*/
+ strncpy(gpsdata->dev.path, sp + 2,
+ sizeof(gpsdata->dev.path));
+ /*@ +mayaliasunique @*/
+ gpsdata->set |= DEVICE_SET;
+ }
+ /*@ +mustfreeonly */
+ break;
+ case 'I':
+ /*@ -mustfreeonly */
+ if (sp[2] == '?')
+ gpsdata->dev.subtype[0] = '\0';
+ else {
+ (void)strlcpy(gpsdata->dev.subtype, sp + 2,
+ sizeof(gpsdata->dev.subtype));
+ gpsdata->set |= DEVICEID_SET;
+ }
+ /*@ +mustfreeonly */
+ break;
+ case 'O':
+ if (sp[2] == '?') {
+ gpsdata->set = MODE_SET | STATUS_SET;
+ gpsdata->status = STATUS_NO_FIX;
+ gps_clear_fix(&gpsdata->fix);
+ } else {
+ struct gps_fix_t nf;
+ char tag[MAXTAGLEN + 1], alt[20];
+ char eph[20], epv[20], track[20], speed[20],
+ climb[20];
+ char epd[20], eps[20], epc[20], mode[2];
+ char timestr[20], ept[20], lat[20], lon[20];
+ int st = sscanf(sp + 2,
+ "%8s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %1s",
+ tag, timestr, ept, lat, lon,
+ alt, eph, epv, track, speed,
+ climb,
+ epd, eps, epc, mode);
+ if (st >= 14) {
+#define DEFAULT(val) (val[0] == '?') ? NAN : atof(val)
+ /*@ +floatdouble @*/
+ nf.time = DEFAULT(timestr);
+ nf.latitude = DEFAULT(lat);
+ nf.longitude = DEFAULT(lon);
+ nf.ept = DEFAULT(ept);
+ nf.altitude = DEFAULT(alt);
+ /* designed before we split eph into epx+epy */
+ nf.epx = nf.epy = DEFAULT(eph) / sqrt(2);
+ nf.epv = DEFAULT(epv);
+ nf.track = DEFAULT(track);
+ nf.speed = DEFAULT(speed);
+ nf.climb = DEFAULT(climb);
+ nf.epd = DEFAULT(epd);
+ nf.eps = DEFAULT(eps);
+ nf.epc = DEFAULT(epc);
+ /*@ -floatdouble @*/
+#undef DEFAULT
+ if (st >= 15)
+ nf.mode =
+ (mode[0] ==
+ '?') ? MODE_NOT_SEEN : atoi(mode);
+ else
+ nf.mode =
+ (alt[0] == '?') ? MODE_2D : MODE_3D;
+ if (alt[0] != '?')
+ gpsdata->set |= ALTITUDE_SET | CLIMB_SET;
+ if (isnan(nf.epx) == 0 && isnan(nf.epy) == 0)
+ gpsdata->set |= HERR_SET;
+ if (isnan(nf.epv) == 0)
+ gpsdata->set |= VERR_SET;
+ if (isnan(nf.track) == 0)
+ gpsdata->set |= TRACK_SET | SPEED_SET;
+ if (isnan(nf.eps) == 0)
+ gpsdata->set |= SPEEDERR_SET;
+ if (isnan(nf.epc) == 0)
+ gpsdata->set |= CLIMBERR_SET;
+ gpsdata->fix = nf;
+ (void)strlcpy(gpsdata->tag, tag,
+ MAXTAGLEN + 1);
+ gpsdata->set |=
+ TIME_SET | TIMERR_SET | LATLON_SET |
+ MODE_SET;
+ gpsdata->status = STATUS_FIX;
+ gpsdata->set |= STATUS_SET;
+ }
+ }
+ break;
+ case 'X':
+ if (sp[2] == '?')
+ gpsdata->online = (timestamp_t)-1;
+ else {
+ (void)sscanf(sp, "X=%lf", &gpsdata->online);
+ gpsdata->set |= ONLINE_SET;
+ }
+ break;
+ case 'Y':
+ if (sp[2] == '?') {
+ gpsdata->satellites_visible = 0;
+ } else {
+ int j, i1, i2, i3, i5;
+ int PRN[MAXCHANNELS];
+ int elevation[MAXCHANNELS], azimuth[MAXCHANNELS];
+ int used[MAXCHANNELS];
+ double ss[MAXCHANNELS], f4;
+ char tag[MAXTAGLEN + 1], timestamp[21];
+
+ (void)sscanf(sp, "Y=%8s %20s %d ",
+ tag, timestamp,
+ &gpsdata->satellites_visible);
+ (void)strncpy(gpsdata->tag, tag, MAXTAGLEN);
+ if (timestamp[0] != '?') {
+ gpsdata->set |= TIME_SET;
+ }
+ for (j = 0; j < gpsdata->satellites_visible; j++) {
+ PRN[j] = elevation[j] = azimuth[j] = used[j] =
+ 0;
+ ss[j] = 0.0;
+ }
+ for (j = 0, gpsdata->satellites_used = 0;
+ j < gpsdata->satellites_visible; j++) {
+ if ((sp != NULL)
+ && ((sp = strchr(sp, ':')) != NULL)) {
+ sp++;
+ (void)sscanf(sp, "%d %d %d %lf %d", &i1,
+ &i2, &i3, &f4, &i5);
+ PRN[j] = i1;
+ elevation[j] = i2;
+ azimuth[j] = i3;
+ ss[j] = f4;
+ used[j] = i5;
+ if (i5 == 1)
+ gpsdata->satellites_used++;
+ }
+ }
+ /*@ -compdef @*/
+ memcpy(gpsdata->PRN, PRN, sizeof(PRN));
+ memcpy(gpsdata->elevation, elevation,
+ sizeof(elevation));
+ memcpy(gpsdata->azimuth, azimuth,
+ sizeof(azimuth));
+ memcpy(gpsdata->ss, ss, sizeof(ss));
+ memcpy(gpsdata->used, used, sizeof(used));
+ /*@ +compdef @*/
+ }
+ gpsdata->set |= SATELLITE_SET;
+ break;
+ }
+
+#ifdef LIBGPS_DEBUG
+ if (debuglevel >= 1)
+ libgps_dump_state(gpsdata);
+#endif /* LIBGPS_DEBUG */
+
+ /*
+ * Skip to next GPSD when we see \r or \n;
+ * we don't want to try interpreting stuff
+ * in between that might be raw mode data.
+ */
+ if (eol)
+ break;
+ }
+ }
+ }
+ }
+#endif /* OLDSTYLE_ENABLE */
+
+#ifndef USE_QT
+ //libgps_debug_trace((DEBUG_CALLS, "final flags: (0x%04x) %s\n", gpsdata->set,gps_maskdump(gpsdata->set)));
+#endif
+ return 0;
+}
+/*@ +compdef @*/
+
+const char /*@observer@*/ *gps_data(struct gps_data_t *gpsdata)
+/* return the contents of the client data buffer */
+{
+ return PRIVATE(gpsdata)->buffer;
+}
+
+int gps_send(struct gps_data_t *gpsdata, const char *fmt, ...)
+/* send a command to the gpsd instance */
+{
+ char buf[BUFSIZ];
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void)vsnprintf(buf, sizeof(buf) - 2, fmt, ap);
+ va_end(ap);
+ if (buf[strlen(buf) - 1] != '\n')
+ (void)strlcat(buf, "\n", BUFSIZ);
+#ifndef USE_QT
+ if (write(gpsdata->gps_fd, buf, strlen(buf)) == (ssize_t) strlen(buf))
+ return 0;
+ else
+ return -1;
+#else
+ QTcpSocket *sock = (QTcpSocket *) gpsdata->gps_fd;
+ sock->write(buf, strlen(buf));
+ if (sock->waitForBytesWritten())
+ return 0;
+ else {
+ qDebug() << "libgps::send error: " << sock->errorString();
+ return -1;
+ }
+#endif
+}
+
+int gps_stream(struct gps_data_t *gpsdata, unsigned int flags,
+ /*@null@*/ void *d)
+/* ask gpsd to stream reports at you, hiding the command details */
+{
+ char buf[GPS_JSON_COMMAND_MAX];
+
+ if ((flags & (WATCH_JSON | WATCH_OLDSTYLE | WATCH_NMEA | WATCH_RAW)) == 0) {
+ flags |= WATCH_JSON;
+ }
+ if ((flags & WATCH_DISABLE) != 0) {
+ if ((flags & WATCH_OLDSTYLE) != 0) {
+ (void)strlcpy(buf, "w-", sizeof(buf));
+ if ((flags & WATCH_NMEA) != 0)
+ (void)strlcat(buf, "r-", sizeof(buf));
+ } else {
+ (void)strlcpy(buf, "?WATCH={\"enable\":false,", sizeof(buf));
+ if (flags & WATCH_JSON)
+ (void)strlcat(buf, "\"json\":false,", sizeof(buf));
+ if (flags & WATCH_NMEA)
+ (void)strlcat(buf, "\"nmea\":false,", sizeof(buf));
+ if (flags & WATCH_RAW)
+ (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
+ if (flags & WATCH_RARE)
+ (void)strlcat(buf, "\"raw\":0,", sizeof(buf));
+ if (flags & WATCH_SCALED)
+ (void)strlcat(buf, "\"scaled\":false,", sizeof(buf));
+ if (flags & WATCH_TIMING)
+ (void)strlcat(buf, "\"timing\":false,", sizeof(buf));
+ if (buf[strlen(buf) - 1] == ',')
+ buf[strlen(buf) - 1] = '\0';
+ (void)strlcat(buf, "};", sizeof(buf));
+ }
+ //libgps_debug_trace((DEBUG_CALLS, "gps_stream() disable command: %s\n", buf));
+ return gps_send(gpsdata, buf);
+ } else { /* if ((flags & WATCH_ENABLE) != 0) */
+
+ if ((flags & WATCH_OLDSTYLE) != 0) {
+ (void)strlcpy(buf, "w+x", sizeof(buf));
+ if ((flags & WATCH_NMEA) != 0)
+ (void)strlcat(buf, "r+", sizeof(buf));
+ } else {
+ (void)strlcpy(buf, "?WATCH={\"enable\":true,", sizeof(buf));
+ if (flags & WATCH_JSON)
+ (void)strlcat(buf, "\"json\":true,", sizeof(buf));
+ if (flags & WATCH_NMEA)
+ (void)strlcat(buf, "\"nmea\":true,", sizeof(buf));
+ if (flags & WATCH_RARE)
+ (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
+ if (flags & WATCH_RAW)
+ (void)strlcat(buf, "\"raw\":2,", sizeof(buf));
+ if (flags & WATCH_SCALED)
+ (void)strlcat(buf, "\"scaled\":true,", sizeof(buf));
+ if (flags & WATCH_TIMING)
+ (void)strlcat(buf, "\"timing\":true,", sizeof(buf));
+ /*@-nullpass@*//* shouldn't be needed, splint has a bug */
+ if (flags & WATCH_DEVICE)
+ (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "\"device\":\"%s\",", (char *)d);
+ /*@+nullpass@*/
+ if (buf[strlen(buf) - 1] == ',')
+ buf[strlen(buf) - 1] = '\0';
+ (void)strlcat(buf, "};", sizeof(buf));
+ }
+ //libgps_debug_trace((DEBUG_CALLS, "gps_stream() enable command: %s\n", buf));
+ return gps_send(gpsdata, buf);
+ }
+}
+
+/* end */