"Initial commit to Gerrit"
[profile/ivi/gpsd.git] / libgps_core.c
1 /* libgps.c -- client interface library for the gpsd daemon
2  *
3  * This file is Copyright (c) 2010 by the GPSD project
4  * BSD terms apply: see the file COPYING in the distribution root for details.
5  */
6 #include <sys/time.h>
7 #include <stdio.h>
8 #ifndef S_SPLINT_S
9 #include <unistd.h>
10 #endif /* S_SPLINT_S */
11 #include <sys/types.h>
12 #include <stdlib.h>
13 #include <fcntl.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <stdarg.h>
17 #include <math.h>
18 #include <locale.h>
19 #include <assert.h>
20
21 #include "gpsd.h"
22 #include "gps_json.h"
23
24 #ifndef USE_QT
25 #ifndef S_SPLINT_S
26 #ifdef HAVE_SYS_SOCKET_H
27 #include <sys/socket.h>
28 #else
29 #define AF_UNSPEC 0
30 #endif
31 #endif
32 #if defined (HAVE_SYS_SELECT_H)
33 #include <sys/select.h>
34 #endif
35 #else
36 #include <QTcpSocket>
37 #endif /* USE_QT */
38
39 #ifdef S_SPLINT_S
40 extern char *strtok_r(char *, const char *, char **);
41 #endif /* S_SPLINT_S */
42
43 #if defined(TESTMAIN) || defined(CLIENTDEBUG_ENABLE)
44 #define LIBGPS_DEBUG
45 #endif /* defined(TESTMAIN) || defined(CLIENTDEBUG_ENABLE) */
46
47 struct privdata_t
48 {
49     bool newstyle;
50     ssize_t waiting;
51     char buffer[GPS_JSON_RESPONSE_MAX * 2];
52 };
53 #define PRIVATE(gpsdata) ((struct privdata_t *)gpsdata->privdata)
54
55 #ifdef LIBGPS_DEBUG
56 static int debuglevel = 0;
57 static int waitcount = 0;
58 static FILE *debugfp;
59
60 void gps_enable_debug(int level, FILE * fp)
61 /* control the level and destination of debug trace messages */
62 {
63     debuglevel = level;
64     debugfp = fp;
65 #ifdef CLIENTDEBUG_ENABLE
66     json_enable_debug(level - 2, fp);
67 #endif
68 }
69
70 static void gps_trace(int errlevel, const char *fmt, ...)
71 /* assemble command in printf(3) style */
72 {
73     if (errlevel <= debuglevel) {
74         char buf[BUFSIZ];
75         va_list ap;
76
77         (void)strlcpy(buf, "libgps: ", BUFSIZ);
78         va_start(ap, fmt);
79         (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt,
80                         ap);
81         va_end(ap);
82
83         (void)fputs(buf, debugfp);
84     }
85 }
86
87 # define libgps_debug_trace(args) (void) gps_trace args
88 #else
89 # define libgps_debug_trace(args) /*@i1@*/do { } while (0)
90 #endif /* LIBGPS_DEBUG */
91
92 /*@-nullderef@*/
93 int gps_open_r(/*@null@*/const char *host, /*@null@*/const char *port,
94                /*@out@*/ struct gps_data_t *gpsdata)
95 {
96     /*@ -branchstate @*/
97     if (!gpsdata)
98         return -1;
99     if (!host)
100         host = "localhost";
101     if (!port)
102         port = DEFAULT_GPSD_PORT;
103
104     libgps_debug_trace((1, "gps_open_r(%s, %s)\n", host, port));
105
106 #ifndef USE_QT
107     if ((gpsdata->gps_fd =
108          netlib_connectsock(AF_UNSPEC, host, port, "tcp")) < 0) {
109         errno = gpsdata->gps_fd;
110         return -1;
111     }
112 #else
113     QTcpSocket *sock = new QTcpSocket();
114     gpsdata->gps_fd = sock;
115     sock->connectToHost(host, QString(port).toInt());
116     if (!sock->waitForConnected())
117         qDebug() << "libgps::connect error: " << sock->errorString();
118     else
119         qDebug() << "libgps::connected!";
120 #endif
121
122     gpsdata->set = 0;
123     gpsdata->status = STATUS_NO_FIX;
124     gps_clear_fix(&gpsdata->fix);
125
126     /* set up for line-buffered I/O over the daemon socket */
127     gpsdata->privdata = (void *)malloc(sizeof(struct privdata_t));
128     if (gpsdata->privdata == NULL)
129         return -1;
130     PRIVATE(gpsdata)->newstyle = false;
131     PRIVATE(gpsdata)->waiting = 0;
132     /*@i2@*/ PRIVATE(gpsdata)->buffer[0] = '\0';
133
134     return 0;
135     /*@ +branchstate @*/
136 }
137
138 /*@-compmempass -immediatetrans@*/
139 struct gps_data_t *gps_open(const char *host, const char *port)
140 /* open a connection to a gpsd daemon */
141 {
142     static struct gps_data_t gpsdata;
143     if (gps_open_r(host, port, &gpsdata) == -1)
144         return NULL;
145     else
146         return &gpsdata;
147 }
148
149 /*@+compmempass +immediatetrans@*/
150
151 /*@-compdef -usereleased@*/
152 int gps_close(struct gps_data_t *gpsdata)
153 /* close a gpsd connection */
154 {
155     libgps_debug_trace((1, "gps_close()\n"));
156 #ifndef USE_QT
157     free(PRIVATE(gpsdata));
158     (void)close(gpsdata->gps_fd);
159     gpsdata->gps_fd = -1;
160 #else
161     QTcpSocket *sock = (QTcpSocket *) gpsdata->gps_fd;
162     sock->disconnectFromHost();
163     delete sock;
164     gpsdata->gps_fd = NULL;
165 #endif
166
167     return 0;
168 }
169
170 /*@+compdef +usereleased@*/
171
172 void gps_set_raw_hook(struct gps_data_t *gpsdata,
173                       void (*hook) (struct gps_data_t *, char *, size_t len))
174 {
175     gpsdata->raw_hook = hook;
176 }
177
178 #ifdef LIBGPS_DEBUG
179 static void libgps_dump_state(struct gps_data_t *collect, time_t now)
180 {
181     char *status_values[] = { "NO_FIX", "FIX", "DGPS_FIX" };
182     char *mode_values[] = { "", "NO_FIX", "MODE_2D", "MODE_3D" };
183
184     /* no need to dump the entire state, this is a sanity check */
185 #ifndef USE_QT
186     (void)fprintf(debugfp, "flags: (0x%04x) %s\n",
187                   collect->set, gps_maskdump(collect->set));
188 #endif
189     if (collect->set & ONLINE_SET)
190         (void)fprintf(debugfp, "ONLINE: %lf\n", collect->online);
191     if (collect->set & TIME_SET)
192         (void)fprintf(debugfp, "TIME: %lf\n", collect->fix.time);
193     if (collect->set & LATLON_SET)
194         (void)fprintf(debugfp, "LATLON: lat/lon: %lf %lf\n",
195                       collect->fix.latitude, collect->fix.longitude);
196     if (collect->set & ALTITUDE_SET)
197         (void)fprintf(debugfp, "ALTITUDE: altitude: %lf  U: climb: %lf\n",
198                       collect->fix.altitude, collect->fix.climb);
199     if (collect->set & SPEED_SET)
200         (void)fprintf(debugfp, "SPEED: %lf\n", collect->fix.speed);
201     if (collect->set & TRACK_SET)
202         (void)fprintf(debugfp, "TRACK: track: %lf\n", collect->fix.track);
203     if (collect->set & CLIMB_SET)
204         (void)fprintf(debugfp, "CLIMB: climb: %lf\n", collect->fix.climb);
205     if (collect->set & STATUS_SET)
206         (void)fprintf(debugfp, "STATUS: status: %d (%s)\n",
207                       collect->status, status_values[collect->status]);
208     if (collect->set & MODE_SET)
209         (void)fprintf(debugfp, "MODE: mode: %d (%s)\n",
210                       collect->fix.mode, mode_values[collect->fix.mode]);
211     if (collect->set & DOP_SET)
212         (void)fprintf(debugfp,
213                       "DOP: satellites %d, pdop=%lf, hdop=%lf, vdop=%lf\n",
214                       collect->satellites_used, collect->dop.pdop,
215                       collect->dop.hdop, collect->dop.vdop);
216     if (collect->set & VERSION_SET)
217         (void)fprintf(debugfp, "VERSION: release=%s rev=%s proto=%d.%d\n",
218                       collect->version.release,
219                       collect->version.rev,
220                       collect->version.proto_major,
221                       collect->version.proto_minor);
222     if (collect->set & POLICY_SET)
223         (void)fprintf(debugfp,
224                       "POLICY: watcher=%s nmea=%s raw=%d scaled=%s timing=%s, devpath=%s\n",
225                       collect->policy.watcher ? "true" : "false",
226                       collect->policy.nmea ? "true" : "false",
227                       collect->policy.raw,
228                       collect->policy.scaled ? "true" : "false",
229                       collect->policy.timing ? "true" : "false",
230                       collect->policy.devpath);
231     if (collect->set & SATELLITE_SET) {
232         int i;
233
234         (void)fprintf(debugfp, "SKY: satellites in view: %d\n",
235                       collect->satellites_visible);
236         for (i = 0; i < collect->satellites_visible; i++) {
237             (void)fprintf(debugfp, "    %2.2d: %2.2d %3.3d %3.0f %c\n",
238                           collect->PRN[i], collect->elevation[i],
239                           collect->azimuth[i], collect->ss[i],
240                           collect->used[i] ? 'Y' : 'N');
241         }
242     }
243     if (collect->set & DEVICE_SET)
244         (void)fprintf(debugfp, "DEVICE: Device is '%s', driver is '%s'\n",
245                       collect->dev.path, collect->dev.driver);
246 #ifdef OLDSTYLE_ENABLE
247     if (collect->set & DEVICEID_SET)
248         (void)fprintf(debugfp, "GPSD ID is %s\n", collect->dev.subtype);
249 #endif /* OLDSTYLE_ENABLE */
250     if (collect->set & DEVICELIST_SET) {
251         int i;
252         (void)fprintf(debugfp, "DEVICELIST:%d devices:\n",
253                       collect->devices.ndevices);
254         for (i = 0; i < collect->devices.ndevices; i++) {
255             (void)fprintf(debugfp, "%d: path='%s' driver='%s'\n",
256                           collect->devices.ndevices,
257                           collect->devices.list[i].path,
258                           collect->devices.list[i].driver);
259         }
260     }
261
262 }
263 #endif /* LIBGPS_DEBUG */
264
265
266 /*@ -branchstate -usereleased -mustfreefresh -nullstate -usedef @*/
267 int gps_unpack(char *buf, struct gps_data_t *gpsdata)
268 /* unpack a gpsd response into a status structure, buf must be writeable */
269 {
270     libgps_debug_trace((1, "gps_unpack(%s)\n", buf));
271
272     /* detect and process a JSON response */
273     if (buf[0] == '{') {
274         const char *jp = buf, **next = &jp;
275         while (next != NULL && *next != NULL && next[0][0] != '\0') {
276             libgps_debug_trace((1,
277                                 "gps_unpack() segment parse '%s'\n", *next));
278             if (libgps_json_unpack(*next, gpsdata, next) == -1)
279                 break;
280 #ifdef LIBGPS_DEBUG
281             if (debuglevel >= 1)
282                 libgps_dump_state(gpsdata, time(NULL));
283 #endif /* LIBGPS_DEBUG */
284
285         }
286 #ifdef OLDSTYLE_ENABLE
287         if (PRIVATE(gpsdata) != NULL)
288             PRIVATE(gpsdata)->newstyle = true;
289 #endif /* OLDSTYLE_ENABLE */
290     }
291 #ifdef OLDSTYLE_ENABLE
292     else {
293         /*
294          * Get the decimal separator for the current application locale.
295          * This looks thread-unsafe, but it's not.  The key is that
296          * character assignment is atomic.
297          */
298         char *ns, *sp, *tp;
299
300         static char decimal_point = '\0';
301         if (decimal_point == '\0') {
302             struct lconv *locale_data = localeconv();
303             if (locale_data != NULL && locale_data->decimal_point[0] != '.')
304                 decimal_point = locale_data->decimal_point[0];
305         }
306
307         for (ns = buf; ns; ns = strstr(ns + 1, "GPSD")) {
308             if ( /*@i1@*/ strncmp(ns, "GPSD", 4) == 0) {
309                 bool eol = false;
310                 /* the following should execute each time we have a good next sp */
311                 for (sp = ns + 5; *sp != '\0'; sp = tp + 1) {
312                     tp = sp + strcspn(sp, ",\r\n");
313                     eol = *tp == '\r' || *tp == '\n';
314                     if (*tp == '\0')
315                         tp--;
316                     else
317                         *tp = '\0';
318
319                     /*
320                      * The daemon always emits the Anglo-American and SI
321                      * decimal point.  Hack these into whatever the
322                      * application locale requires if it's not the same.
323                      * This has to happen *after* we grab the next
324                      * comma-delimited response, or we'll lose horribly
325                      * in locales where the decimal separator is comma.
326                      */
327                     if (decimal_point != '\0') {
328                         char *cp;
329                         for (cp = sp; cp < tp; cp++)
330                             if (*cp == '.')
331                                 *cp = decimal_point;
332                     }
333
334                     /* note, there's a bit of skip logic after the switch */
335
336                     switch (*sp) {
337                     case 'F':   /*@ -mustfreeonly */
338                         if (sp[2] == '?')
339                             gpsdata->dev.path[0] = '\0';
340                         else {
341                             /*@ -mayaliasunique @*/
342                             strncpy(gpsdata->dev.path, sp + 2,
343                                     sizeof(gpsdata->dev.path));
344                             /*@ +mayaliasunique @*/
345                             gpsdata->set |= DEVICE_SET;
346                         }
347                         /*@ +mustfreeonly */
348                         break;
349                     case 'I':
350                         /*@ -mustfreeonly */
351                         if (sp[2] == '?')
352                             gpsdata->dev.subtype[0] = '\0';
353                         else {
354                             (void)strlcpy(gpsdata->dev.subtype, sp + 2,
355                                           sizeof(gpsdata->dev.subtype));
356                             gpsdata->set |= DEVICEID_SET;
357                         }
358                         /*@ +mustfreeonly */
359                         break;
360                     case 'O':
361                         if (sp[2] == '?') {
362                             gpsdata->set = MODE_SET | STATUS_SET;
363                             gpsdata->status = STATUS_NO_FIX;
364                             gps_clear_fix(&gpsdata->fix);
365                         } else {
366                             struct gps_fix_t nf;
367                             char tag[MAXTAGLEN + 1], alt[20];
368                             char eph[20], epv[20], track[20], speed[20],
369                                 climb[20];
370                             char epd[20], eps[20], epc[20], mode[2];
371                             char timestr[20], ept[20], lat[20], lon[20];
372                             int st = sscanf(sp + 2,
373                                             "%8s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %19s %1s",
374                                             tag, timestr, ept, lat, lon,
375                                             alt, eph, epv, track, speed,
376                                             climb,
377                                             epd, eps, epc, mode);
378                             if (st >= 14) {
379 #define DEFAULT(val) (val[0] == '?') ? NAN : atof(val)
380                                 /*@ +floatdouble @*/
381                                 nf.time = DEFAULT(timestr);
382                                 nf.latitude = DEFAULT(lat);
383                                 nf.longitude = DEFAULT(lon);
384                                 nf.ept = DEFAULT(ept);
385                                 nf.altitude = DEFAULT(alt);
386                                 /* designed before we split eph into epx+epy */
387                                 nf.epx = nf.epy = DEFAULT(eph) / sqrt(2);
388                                 nf.epv = DEFAULT(epv);
389                                 nf.track = DEFAULT(track);
390                                 nf.speed = DEFAULT(speed);
391                                 nf.climb = DEFAULT(climb);
392                                 nf.epd = DEFAULT(epd);
393                                 nf.eps = DEFAULT(eps);
394                                 nf.epc = DEFAULT(epc);
395                                 /*@ -floatdouble @*/
396 #undef DEFAULT
397                                 if (st >= 15)
398                                     nf.mode =
399                                         (mode[0] ==
400                                          '?') ? MODE_NOT_SEEN : atoi(mode);
401                                 else
402                                     nf.mode =
403                                         (alt[0] == '?') ? MODE_2D : MODE_3D;
404                                 if (alt[0] != '?')
405                                     gpsdata->set |= ALTITUDE_SET | CLIMB_SET;
406                                 if (isnan(nf.epx) == 0 && isnan(nf.epy) == 0)
407                                     gpsdata->set |= HERR_SET;
408                                 if (isnan(nf.epv) == 0)
409                                     gpsdata->set |= VERR_SET;
410                                 if (isnan(nf.track) == 0)
411                                     gpsdata->set |= TRACK_SET | SPEED_SET;
412                                 if (isnan(nf.eps) == 0)
413                                     gpsdata->set |= SPEEDERR_SET;
414                                 if (isnan(nf.epc) == 0)
415                                     gpsdata->set |= CLIMBERR_SET;
416                                 gpsdata->fix = nf;
417                                 (void)strlcpy(gpsdata->tag, tag,
418                                               MAXTAGLEN + 1);
419                                 gpsdata->set |=
420                                     TIME_SET | TIMERR_SET | LATLON_SET |
421                                     MODE_SET;
422                                 gpsdata->status = STATUS_FIX;
423                                 gpsdata->set |= STATUS_SET;
424                             }
425                         }
426                         break;
427                     case 'X':
428                         if (sp[2] == '?')
429                             gpsdata->online = -1;
430                         else {
431                             (void)sscanf(sp, "X=%lf", &gpsdata->online);
432                             gpsdata->set |= ONLINE_SET;
433                         }
434                         break;
435                     case 'Y':
436                         if (sp[2] == '?') {
437                             gpsdata->satellites_visible = 0;
438                         } else {
439                             int j, i1, i2, i3, i5;
440                             int PRN[MAXCHANNELS];
441                             int elevation[MAXCHANNELS], azimuth[MAXCHANNELS];
442                             int used[MAXCHANNELS];
443                             double ss[MAXCHANNELS], f4;
444                             char tag[MAXTAGLEN + 1], timestamp[21];
445
446                             (void)sscanf(sp, "Y=%8s %20s %d ",
447                                          tag, timestamp,
448                                          &gpsdata->satellites_visible);
449                             (void)strncpy(gpsdata->tag, tag, MAXTAGLEN);
450                             if (timestamp[0] != '?') {
451                                 gpsdata->set |= TIME_SET;
452                             }
453                             for (j = 0; j < gpsdata->satellites_visible; j++) {
454                                 PRN[j] = elevation[j] = azimuth[j] = used[j] =
455                                     0;
456                                 ss[j] = 0.0;
457                             }
458                             for (j = 0, gpsdata->satellites_used = 0;
459                                  j < gpsdata->satellites_visible; j++) {
460                                 if ((sp != NULL)
461                                     && ((sp = strchr(sp, ':')) != NULL)) {
462                                     sp++;
463                                     (void)sscanf(sp, "%d %d %d %lf %d", &i1,
464                                                  &i2, &i3, &f4, &i5);
465                                     PRN[j] = i1;
466                                     elevation[j] = i2;
467                                     azimuth[j] = i3;
468                                     ss[j] = f4;
469                                     used[j] = i5;
470                                     if (i5 == 1)
471                                         gpsdata->satellites_used++;
472                                 }
473                             }
474                             /*@ -compdef @*/
475                             memcpy(gpsdata->PRN, PRN, sizeof(PRN));
476                             memcpy(gpsdata->elevation, elevation,
477                                    sizeof(elevation));
478                             memcpy(gpsdata->azimuth, azimuth,
479                                    sizeof(azimuth));
480                             memcpy(gpsdata->ss, ss, sizeof(ss));
481                             memcpy(gpsdata->used, used, sizeof(used));
482                             /*@ +compdef @*/
483                         }
484                         gpsdata->set |= SATELLITE_SET;
485                         break;
486                     }
487
488 #ifdef LIBGPS_DEBUG
489                     if (debuglevel >= 1)
490                         libgps_dump_state(gpsdata, time(NULL));
491 #endif /* LIBGPS_DEBUG */
492
493                     /*
494                      * Skip to next GPSD when we see \r or \n;
495                      * we don't want to try interpreting stuff
496                      * in between that might be raw mode data.
497                      */
498                     if (eol)
499                         break;
500                 }
501             }
502         }
503     }
504 #endif /* OLDSTYLE_ENABLE */
505
506 /*@ -compdef @*/
507     if (gpsdata->raw_hook) {
508         //libgps_debug_trace((stderr, "libgps: raw hook called on '%s'\n", buf));
509         gpsdata->raw_hook(gpsdata, buf, strlen(buf));
510     }
511 #ifndef USE_QT
512     libgps_debug_trace((1, "final flags: (0x%04x) %s\n", gpsdata->set,
513                         gps_maskdump(gpsdata->set)));
514 #endif
515     return 0;
516 }
517
518 /*@ +compdef @*/
519 /*@ -branchstate +usereleased +mustfreefresh +nullstate +usedef @*/
520
521 bool gps_waiting(struct gps_data_t * gpsdata)
522 /* is there input waiting from the GPS? */
523 {
524 #ifndef USE_QT
525     fd_set rfds;
526     struct timeval tv;
527
528     libgps_debug_trace((1, "gps_waiting(): %d\n", waitcount++));
529     if (PRIVATE(gpsdata)->waiting > 0)
530         return true;
531
532     FD_ZERO(&rfds);
533     FD_SET(gpsdata->gps_fd, &rfds);
534     tv.tv_sec = 0;
535     tv.tv_usec = 1;
536     /* all error conditions return "not waiting" -- crude but effective */
537     return (select(gpsdata->gps_fd + 1, &rfds, NULL, NULL, &tv) == 1);
538 #else
539     return ((QTcpSocket *) (gpsdata->gps_fd))->waitForReadyRead(250);
540 #endif
541 }
542
543 /*@-compdef -usedef -uniondef@*/
544 int gps_read(/*@out@*/struct gps_data_t *gpsdata)
545 /* wait for and read data being streamed from the daemon */
546 {
547     char *eol;
548     double received = 0;
549     ssize_t response_length;
550     int status = -1;
551     struct privdata_t *priv = PRIVATE(gpsdata);
552
553     gpsdata->set &= ~PACKET_SET;
554     for (eol = priv->buffer;
555          *eol != '\n' && eol < priv->buffer + priv->waiting; eol++)
556         continue;
557     if (*eol != '\n')
558         eol = NULL;
559
560     if (eol == NULL) {
561 #ifndef USE_QT
562         /* read data: return -1 if no data waiting or buffered, 0 otherwise */
563         status = (int)recv(gpsdata->gps_fd,
564                            priv->buffer + priv->waiting,
565                            sizeof(priv->buffer) - priv->waiting, 0);
566 #else
567         status =
568             ((QTcpSocket *) (gpsdata->gps_fd))->read(priv->buffer +
569                                                      priv->waiting,
570                                                      sizeof(priv->buffer) -
571                                                      priv->waiting);
572 #endif
573
574         /* if we just received data from the socket, it's in the buffer */
575         if (status > -1)
576             priv->waiting += status;
577         /* buffer is empty - implies no data was read */
578         if (priv->waiting == 0) {
579             /* 
580              * If we received 0 bytes, other side of socket is closing.
581              * Return -1 as end-of-data indication.
582              */
583             if (status == 0)
584                 return -1;
585 #ifndef USE_QT
586             /* count transient errors as success, we'll retry later */
587             else if (errno == EINTR || errno == EAGAIN
588                      || errno == EWOULDBLOCK)
589                 return 0;
590 #endif
591             /* hard error return of -1, pass it along */
592             else
593                 return -1;
594         }
595         /* there's buffered data waiting to be returned */
596         for (eol = priv->buffer;
597              *eol != '\n' && eol < priv->buffer + priv->waiting; eol++)
598             continue;
599         if (*eol != '\n')
600             eol = NULL;
601         if (eol == NULL)
602             return 0;
603     }
604
605     assert(eol != NULL);
606     *eol = '\0';
607     response_length = eol - priv->buffer + 1;
608     received = gpsdata->online = timestamp();
609     status = gps_unpack(priv->buffer, gpsdata);
610     /*@+matchanyintegral@*/
611     memmove(priv->buffer,
612             priv->buffer + response_length, priv->waiting - response_length);
613     /*@-matchanyintegral@*/
614     priv->waiting -= response_length;
615     gpsdata->set |= PACKET_SET;
616
617     return status;
618 }
619 /*@+compdef -usedef +uniondef@*/
620
621 int gps_poll(/*@out@*/struct gps_data_t *gpsdata)
622 /* for backwards compatibility */
623 {
624     int status = gps_read(gpsdata);
625
626     if (status > 0)
627         status = 0;
628
629     return status;
630 }
631
632 int gps_send(struct gps_data_t *gpsdata, const char *fmt, ...)
633 /* send a command to the gpsd instance */
634 {
635     char buf[BUFSIZ];
636     va_list ap;
637
638     va_start(ap, fmt);
639     (void)vsnprintf(buf, sizeof(buf) - 2, fmt, ap);
640     va_end(ap);
641     if (buf[strlen(buf) - 1] != '\n')
642         (void)strlcat(buf, "\n", BUFSIZ);
643 #ifndef USE_QT
644     if (write(gpsdata->gps_fd, buf, strlen(buf)) == (ssize_t) strlen(buf))
645         return 0;
646     else
647         return -1;
648 #else
649     QTcpSocket *sock = (QTcpSocket *) gpsdata->gps_fd;
650     sock->write(buf, strlen(buf));
651     if (sock->waitForBytesWritten())
652         return 0;
653     else {
654         qDebug() << "libgps::send error: " << sock->errorString();
655         return -1;
656     }
657 #endif
658 }
659
660 int gps_stream(struct gps_data_t *gpsdata, unsigned int flags,
661                /*@null@*/ void *d)
662 /* ask gpsd to stream reports at you, hiding the command details */
663 {
664     char buf[GPS_JSON_COMMAND_MAX];
665
666     if ((flags & (WATCH_JSON | WATCH_OLDSTYLE | WATCH_NMEA | WATCH_RAW)) == 0) {
667         flags |= WATCH_JSON;
668     }
669 #ifndef USE_QT
670     if (flags & POLL_NONBLOCK)
671         (void)fcntl(gpsdata->gps_fd, F_SETFL, O_NONBLOCK);
672 #endif
673     if ((flags & WATCH_DISABLE) != 0) {
674         if ((flags & WATCH_OLDSTYLE) != 0) {
675             (void)strlcpy(buf, "w-", sizeof(buf));
676             if (gpsdata->raw_hook != NULL || (flags & WATCH_NMEA) != 0)
677                 (void)strlcat(buf, "r-", sizeof(buf));
678         } else {
679             (void)strlcpy(buf, "?WATCH={\"enable\":false,", sizeof(buf));
680             if (flags & WATCH_JSON)
681                 (void)strlcat(buf, "\"json\":false,", sizeof(buf));
682             if (flags & WATCH_NMEA)
683                 (void)strlcat(buf, "\"nmea\":false,", sizeof(buf));
684             if (flags & WATCH_RAW)
685                 (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
686             if (flags & WATCH_RARE)
687                 (void)strlcat(buf, "\"raw\":0,", sizeof(buf));
688             if (flags & WATCH_SCALED)
689                 (void)strlcat(buf, "\"scaled\":false,", sizeof(buf));
690             if (buf[strlen(buf) - 1] == ',')
691                 buf[strlen(buf) - 1] = '\0';
692             (void)strlcat(buf, "};", sizeof(buf));
693         }
694         libgps_debug_trace((1, "gps_stream() disable command: %s\n", buf));
695         return gps_send(gpsdata, buf);
696     } else {                    /* if ((flags & WATCH_ENABLE) != 0) */
697
698         if ((flags & WATCH_OLDSTYLE) != 0) {
699             (void)strlcpy(buf, "w+x", sizeof(buf));
700             if (gpsdata->raw_hook != NULL || (flags & WATCH_NMEA) != 0)
701                 (void)strlcat(buf, "r+", sizeof(buf));
702         } else {
703             (void)strlcpy(buf, "?WATCH={\"enable\":true,", sizeof(buf));
704             if (flags & WATCH_JSON)
705                 (void)strlcat(buf, "\"json\":true,", sizeof(buf));
706             if (flags & WATCH_NMEA)
707                 (void)strlcat(buf, "\"nmea\":true,", sizeof(buf));
708             if (flags & WATCH_RARE)
709                 (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
710             if (flags & WATCH_RAW)
711                 (void)strlcat(buf, "\"raw\":2,", sizeof(buf));
712             if (flags & WATCH_SCALED)
713                 (void)strlcat(buf, "\"scaled\":true,", sizeof(buf));
714             /*@-nullpass@*//* shouldn't be needed, splint has a bug */
715             if (flags & WATCH_DEVICE)
716                 (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
717                                "\"device\":%s,", (char *)d);
718             /*@+nullpass@*/
719             if (buf[strlen(buf) - 1] == ',')
720                 buf[strlen(buf) - 1] = '\0';
721             (void)strlcat(buf, "};", sizeof(buf));
722         }
723         libgps_debug_trace((1, "gps_stream() enable command: %s\n", buf));
724         return gps_send(gpsdata, buf);
725     }
726 }
727
728 extern char /*@observer@*/ *gps_errstr(const int err)
729 {
730     /* 
731      * We might add our own error codes in the future, e.g for
732      * protocol compatibility checks
733      */
734 #ifndef USE_QT
735     return netlib_errstr(err);
736 #else
737     return "";
738 #endif
739 }
740
741 #ifdef TESTMAIN
742 /*
743  * A simple command-line exerciser for the library.
744  * Not really useful for anything but debugging.
745  */
746
747 static void dumpline(struct gps_data_t *ud UNUSED,
748                      char *buf, size_t ulen UNUSED)
749 {
750     puts(buf);
751 }
752
753 #ifndef S_SPLINT_S
754 #include <unistd.h>
755 #endif /* S_SPLINT_S */
756 #include <getopt.h>
757 #include <signal.h>
758
759 static void onsig(int sig)
760 {
761     (void)fprintf(stderr, "libgps: died with signal %d\n", sig);
762     exit(1);
763 }
764
765 /* must start zeroed, otherwise the unit test will try to chase garbage pointer fields. */
766 static struct gps_data_t gpsdata;
767
768 int main(int argc, char *argv[])
769 {
770     struct gps_data_t *collect;
771     char buf[BUFSIZ];
772     int option;
773     bool batchmode = false;
774     int debug = 0;
775
776     (void)signal(SIGSEGV, onsig);
777     (void)signal(SIGBUS, onsig);
778
779     while ((option = getopt(argc, argv, "bhsD:?")) != -1) {
780         switch (option) {
781         case 'b':
782             batchmode = true;
783             break;
784         case 's':
785             (void)
786                 printf
787                 ("Sizes: gpsdata=%zd rtcm2=%zd rtcm3=%zd ais=%zd compass=%zd raw=%zd devices=%zd policy=%zd version=%zd\n",
788                  sizeof(struct gps_data_t), sizeof(struct rtcm2_t),
789                  sizeof(struct rtcm3_t), sizeof(struct ais_t),
790                  sizeof(struct attitude_t), sizeof(struct rawdata_t),
791                  sizeof(collect->devices), sizeof(struct policy_t),
792                  sizeof(struct version_t));
793             exit(0);
794         case 'D':
795             debug = atoi(optarg);
796             break;
797         case '?':
798         case 'h':
799         default:
800             (void)fputs("usage: libgps [-b] [-d lvl] [-s]\n", stderr);
801             exit(1);
802         }
803     }
804
805     gps_enable_debug(debug, stdout);
806     if (batchmode) {
807         while (fgets(buf, sizeof(buf), stdin) != NULL) {
808             if (buf[0] == '{' || isalpha(buf[0])) {
809                 gps_unpack(buf, &gpsdata);
810                 libgps_dump_state(&gpsdata, time(NULL));
811             }
812         }
813     } else if ((collect = gps_open(NULL, 0)) == NULL) {
814         (void)fputs("Daemon is not running.\n", stdout);
815         exit(1);
816     } else if (optind < argc) {
817         gps_set_raw_hook(collect, dumpline);
818         (void)strlcpy(buf, argv[optind], BUFSIZ);
819         (void)strlcat(buf, "\n", BUFSIZ);
820         (void)gps_send(collect, buf);
821         (void)gps_read(collect);
822         libgps_dump_state(collect, time(NULL));
823         (void)gps_close(collect);
824     } else {
825         int tty = isatty(0);
826
827         gps_set_raw_hook(collect, dumpline);
828         if (tty)
829             (void)fputs("This is the gpsd exerciser.\n", stdout);
830         for (;;) {
831             if (tty)
832                 (void)fputs("> ", stdout);
833             if (fgets(buf, sizeof(buf), stdin) == NULL) {
834                 if (tty)
835                     putchar('\n');
836                 break;
837             }
838             collect->set = 0;
839             (void)gps_send(collect, buf);
840             (void)gps_read(collect);
841             libgps_dump_state(collect, time(NULL));
842         }
843         (void)gps_close(collect);
844     }
845
846     return 0;
847 }
848
849 /*@-nullderef@*/
850
851 #endif /* TESTMAIN */