"Initial commit to Gerrit"
[profile/ivi/gpsd.git] / gpsmon.c
1 /*
2  * The generic GPS packet monitor.
3  *
4  * This file is Copyright (c) 2010 by the GPSD project
5  * BSD terms apply: see the file COPYING in the distribution root for details.
6  */
7 #include <sys/types.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <math.h>
12 #include <ctype.h>
13 #ifndef S_SPLINT_S
14 #include <unistd.h>
15 #endif /* S_SPLINT_S */
16 #include <assert.h>
17 #include <signal.h>
18 #include <setjmp.h>
19 /* Cygwin has only _timezone and not timezone unless the following is set */
20 #if defined(__CYGWIN__)
21 #define timezonevar
22 #endif /* defined(__CYGWIN__) */
23 #include <time.h>
24 #include <termios.h>
25 #include <fcntl.h>              /* for O_RDWR */
26 #include <stdarg.h>
27 #include <stdbool.h>
28 #include <errno.h>
29 #include <sys/ioctl.h>          /* for O_RDWR */
30 #include <setjmp.h>
31
32 #include "gpsd_config.h"
33
34 #ifdef HAVE_BLUEZ
35 #include <bluetooth/bluetooth.h>
36 #endif
37
38 #ifdef HAVE_NCURSES_H
39 #include <ncurses.h>
40 #else
41 #include <curses.h>
42 #endif /* HAVE_NCURSES_H */
43 #include "gpsd.h"
44
45 #include "bits.h"
46
47 #if defined(HAVE_SYS_TIME_H)
48 #include <sys/time.h>
49 #endif
50 #if defined (HAVE_SYS_SELECT_H)
51 #include <sys/select.h>
52 #endif
53
54 #include "gpsdclient.h"
55 #include "gpsmon.h"
56 #include "revision.h"
57
58 #ifdef S_SPLINT_S
59 extern struct tm *localtime_r(const time_t *, /*@out@*/ struct tm *tp);
60 #endif /* S_SPLINT_S */
61
62 #define BUFLEN          2048
63
64 /* external capability tables */
65 extern struct monitor_object_t nmea_mmt, sirf_mmt, garmin_mmt, ashtech_mmt;
66 extern struct monitor_object_t italk_mmt, ubx_mmt, superstar2_mmt;
67 extern struct monitor_object_t fv18_mmt, gpsclock_mmt, mtk3301_mmt;
68 extern struct monitor_object_t oncore_mmt, tnt_mmt;
69
70 /* These are public */
71 struct gps_device_t session;
72 WINDOW *devicewin;
73 int gmt_offset;
74
75 /* These are private */
76 static struct gps_context_t context;
77 static int controlfd = -1;
78 static bool serial, curses_active;
79 static int debuglevel = 0;
80 static WINDOW *statwin, *cmdwin;
81 /*@null@*/ static WINDOW *packetwin;
82 static FILE *logfile;
83 static char *type_name;
84 /*@ -nullassign @*/
85 static const struct monitor_object_t *monitor_objects[] = {
86 #ifdef NMEA_ENABLE
87     &nmea_mmt,
88 #if defined(GARMIN_ENABLE) && defined(NMEA_ENABLE)
89     &garmin_mmt,
90 #endif /* GARMIN_ENABLE && NMEA_ENABLE */
91 #ifdef ASHTECH_ENABLE
92     &ashtech_mmt,
93 #endif /* ASHTECH_ENABLE */
94 #ifdef FV18_ENABLE
95     &fv18_mmt,
96 #endif /* FV18_ENABLE */
97 #ifdef GPSCLOCK_ENABLE
98     &gpsclock_mmt,
99 #endif /* GPSCLOCK_ENABLE */
100 #ifdef MTK3301_ENABLE
101     &mtk3301_mmt,
102 #endif /* MTK3301_ENABLE */
103 #endif /* NMEA_ENABLE */
104 #if defined(SIRF_ENABLE) && defined(BINARY_ENABLE)
105     &sirf_mmt,
106 #endif /* defined(SIRF_ENABLE) && defined(BINARY_ENABLE) */
107 #if defined(UBX_ENABLE) && defined(BINARY_ENABLE)
108     &ubx_mmt,
109 #endif /* defined(UBX_ENABLE) && defined(BINARY_ENABLE) */
110 #if defined(ITRAX_ENABLE) && defined(BINARY_ENABLE)
111     &italk_mmt,
112 #endif /* defined(ITALK_ENABLE) && defined(BINARY_ENABLE) */
113 #if defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE)
114     &superstar2_mmt,
115 #endif /* defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE) */
116 #if defined(ONCORE_ENABLE) && defined(BINARY_ENABLE)
117     &oncore_mmt,
118 #endif /* defined(ONCORE_ENABLE) && defined(BINARY_ENABLE) */
119 #ifdef TNT_ENABLE
120     &tnt_mmt,
121 #endif /* TNT_ENABLE */
122     NULL,
123 };
124
125 static const struct monitor_object_t **active;
126 /*@ +nullassign @*/
127
128 static jmp_buf terminate;
129
130 #define display (void)mvwprintw
131
132 /* ternination codes */
133 #define TERM_SELECT_FAILED      1
134 #define TERM_DRIVER_SWITCH      2
135 #define TERM_EMPTY_READ         3
136 #define TERM_READ_ERROR         4
137
138 void monitor_fixframe(WINDOW * win)
139 {
140     int ymax, xmax, ycur, xcur;
141
142     assert(win != NULL);
143     getyx(win, ycur, xcur);
144     getmaxyx(win, ymax, xmax);
145     (void)mvwaddch(win, ycur, xmax - 1, ACS_VLINE);
146 }
147
148 /******************************************************************************
149  *
150  * Device-independent I/O routines
151  *
152  ******************************************************************************/
153
154 void gpsd_report(int errlevel UNUSED, const char *fmt, ...)
155 /* our version of the logger */
156 {
157     if (errlevel <= debuglevel && packetwin != NULL) {
158         va_list ap;
159         va_start(ap, fmt);
160         if (!curses_active)
161             (void)vprintf(fmt, ap);
162         else
163             (void)wprintw(packetwin, (char *)fmt, ap);
164         va_end(ap);
165     }
166 }
167
168 /*@ -globstate @*/
169 static ssize_t readpkt(void)
170 {
171     /*@ -type -shiftnegative -compdef -nullpass @*/
172     struct timeval timeval;
173     fd_set select_set;
174     gps_mask_t changed;
175
176     FD_ZERO(&select_set);
177     FD_SET(session.gpsdata.gps_fd, &select_set);
178     if (controlfd > -1)
179         FD_SET(controlfd, &select_set);
180     timeval.tv_sec = 3;
181     timeval.tv_usec = 0;
182     if (select(session.gpsdata.gps_fd + 1, &select_set, NULL, NULL, &timeval)
183         == -1)
184         longjmp(terminate, TERM_SELECT_FAILED);
185
186     if (!FD_ISSET(session.gpsdata.gps_fd, &select_set))
187         longjmp(terminate, TERM_SELECT_FAILED);
188
189     changed = gpsd_poll(&session);
190     if (changed == 0)
191         longjmp(terminate, TERM_EMPTY_READ);
192
193     if ((changed & ERROR_IS) != 0)
194         longjmp(terminate, TERM_READ_ERROR);
195
196     if (logfile != NULL) {
197         /*@ -shiftimplementation -sefparams +charint @*/
198         assert(fwrite
199                (session.packet.outbuffer, sizeof(char),
200                 session.packet.outbuflen, logfile) >= 1);
201         /*@ +shiftimplementation +sefparams -charint @*/
202     }
203     return session.packet.outbuflen;
204     /*@ +type +shiftnegative +compdef +nullpass @*/
205 }
206
207 /*@ +globstate @*/
208
209 static void packet_dump(char *buf, size_t buflen)
210 {
211     if (packetwin != NULL) {
212         size_t i;
213         bool printable = true;
214         for (i = 0; i < buflen; i++)
215             if (!isprint(buf[i]) && !isspace(buf[i]))
216                 printable = false;
217         if (printable) {
218             for (i = 0; i < buflen; i++)
219                 if (isprint(buf[i]))
220                     (void)waddch(packetwin, (chtype) buf[i]);
221                 else
222                     (void)wprintw(packetwin, "\\x%02x",
223                                   (unsigned char)buf[i]);
224         } else {
225             for (i = 0; i < buflen; i++)
226                 (void)wprintw(packetwin, "%02x", (unsigned char)buf[i]);
227         }
228         (void)wprintw(packetwin, "\n");
229     }
230 }
231
232 #if defined(ALLOW_CONTROLSEND) || defined(ALLOW_RECONFIGURE)
233 static void monitor_dump_send(void)
234 {
235     if (packetwin != NULL) {
236         (void)wattrset(packetwin, A_BOLD);
237         (void)wprintw(packetwin, ">>>");
238         packet_dump(session.msgbuf, session.msgbuflen);
239         (void)wattrset(packetwin, A_NORMAL);
240     }
241 }
242 #endif /* defined(ALLOW_CONTROLSEND) || defined(ALLOW_RECONFIGURE) */
243
244 #ifdef ALLOW_CONTROLSEND
245 bool monitor_control_send( /*@in@*/ unsigned char *buf, size_t len)
246 {
247     if (controlfd == -1)
248         return false;
249     else {
250         int savefd;
251         ssize_t st;
252
253         if (!serial) {
254             /*@ -sefparams @*/
255             assert(write(controlfd, "!", 1) != -1);
256             assert(write
257                    (controlfd, session.gpsdata.dev.path,
258                     strlen(session.gpsdata.dev.path)) != -1);
259             assert(write(controlfd, "=", 1) != -1);
260             /*@ +sefparams @*/
261             /*
262              * Ugh...temporarily con the libgpsd layer into using the
263              * socket descriptor.
264              */
265             savefd = session.gpsdata.gps_fd;
266             session.gpsdata.gps_fd = controlfd;
267         }
268
269         st = (*active)->driver->control_send(&session, (char *)buf, len);
270
271         if (!serial) {
272             /* stop pretending now */
273             session.gpsdata.gps_fd = controlfd;
274             /* enough room for "ERROR\r\n\0" */
275             /*@ -sefparams @*/
276             assert(read(controlfd, buf, 8) != -1);
277             /*@ +sefparams @*/
278         }
279         monitor_dump_send();
280         return (st != -1);
281     }
282 }
283
284 static bool monitor_raw_send( /*@in@*/ unsigned char *buf, size_t len)
285 {
286     if (controlfd == -1)
287         return false;
288     else {
289         ssize_t st;
290
291         if (!serial) {
292             /*@ -sefparams @*/
293             assert(write(controlfd, "!", 1) != -1);
294             assert(write(controlfd, session.gpsdata.dev.path,
295                          strlen(session.gpsdata.dev.path)) != -1);
296             assert(write(controlfd, "=", 1) != -1);
297             /*@ +sefparams @*/
298         }
299
300         st = write(controlfd, (char *)buf, len);
301
302         if (!serial) {
303             /* enough room for "ERROR\r\n\0" */
304             /*@ -sefparams @*/
305             assert(read(controlfd, buf, 8) != -1);
306             /*@ +sefparams @*/
307         }
308         (void)memcpy(session.msgbuf, buf, len);
309         session.msgbuflen = len;
310         monitor_dump_send();
311         return (st > 0 && (size_t) st == len);
312     }
313 }
314 #endif /* ALLOW_CONTROLSEND */
315
316 /*****************************************************************************
317  *
318  * Main sequence and display machinery
319  *
320  *****************************************************************************/
321
322 static long tzoffset(void)
323 {
324     time_t now = time(NULL);
325     struct tm tm;
326     long res = 0;
327
328     tzset();
329 #ifdef HAVE_TIMEZONE
330     res = timezone;
331 #else
332     res = localtime_r(&now, &tm)->tm_gmtoff;
333 #endif
334 #ifdef HAVE_DAYLIGHT
335     if (daylight != 0 && localtime_r(&now, &tm)->tm_isdst != 0)
336         res -= 3600;
337 #else
338     if (localtime_r(&now, &tm)->tm_isdst)
339         res -= 3600;
340 #endif
341     return res;
342 }
343
344 void monitor_complain(const char *fmt, ...)
345 {
346     va_list ap;
347     (void)wmove(cmdwin, 0, (int)strlen(type_name) + 2);
348     (void)wclrtoeol(cmdwin);
349     (void)wattrset(cmdwin, A_BOLD | A_BLINK);
350     va_start(ap, fmt);
351     (void)vwprintw(cmdwin, (char *)fmt, ap);
352     va_end(ap);
353     (void)wattrset(cmdwin, A_NORMAL);
354     (void)wrefresh(cmdwin);
355     (void)wgetch(cmdwin);
356 }
357
358
359 void monitor_log(const char *fmt, ...)
360 {
361     if (packetwin != NULL) {
362         va_list ap;
363         va_start(ap, fmt);
364         (void)vwprintw(packetwin, (char *)fmt, ap);
365         va_end(ap);
366     }
367 }
368
369 static bool switch_type(const struct gps_type_t *devtype)
370 {
371     const struct monitor_object_t **trial, **newobject;
372     newobject = NULL;
373     for (trial = monitor_objects; *trial; trial++)
374         if ((*trial)->driver == devtype)
375             newobject = trial;
376     if (newobject) {
377         int leftover;
378         if (LINES < (*newobject)->min_y + 1 || COLS < (*newobject)->min_x) {
379             monitor_complain("New type requires %dx%d screen",
380                              (*newobject)->min_x, (*newobject)->min_y + 1);
381         } else {
382             if (active != NULL) {
383                 (*active)->wrap();
384                 (void)delwin(devicewin);
385             }
386             active = newobject;
387             devicewin = newwin((*active)->min_y, (*active)->min_x, 1, 0);
388             if ((devicewin == NULL) || !(*active)->initialize()) {
389                 monitor_complain("Internal initialization failure - screen "
390                                  "must be at least 80x24. aborting.");
391                 return false;
392             }
393
394             /*@ -onlytrans @*/
395             leftover = LINES - 1 - (*active)->min_y;
396             if (leftover <= 0) {
397                 if (packetwin != NULL)
398                     (void)delwin(packetwin);
399                 packetwin = NULL;
400             } else if (packetwin == NULL) {
401                 packetwin = newwin(leftover, COLS, (*active)->min_y + 1, 0);
402                 (void)scrollok(packetwin, true);
403                 (void)wsetscrreg(packetwin, 0, leftover - 1);
404             } else {
405                 (void)wresize(packetwin, leftover, COLS);
406                 (void)mvwin(packetwin, (*active)->min_y + 1, 0);
407                 (void)wsetscrreg(packetwin, 0, leftover - 1);
408             }
409             /*@ +onlytrans @*/
410         }
411         return true;
412     }
413
414     monitor_complain("No matching monitor type.");
415     return false;
416 }
417
418 static jmp_buf assertbuf;
419
420 static void onsig(int sig UNUSED)
421 {
422     longjmp(assertbuf, 1);
423 }
424
425 int main(int argc, char **argv)
426 {
427 #if defined(ALLOW_CONTROLSEND) || defined(ALLOW_RECONFIGURE)
428     unsigned int v;
429 #endif /* defined(ALLOW_CONTROLSEND) || defined(ALLOW_RECONFIGURE) */
430     int option, status, last_type = BAD_PACKET;
431     ssize_t len;
432     struct fixsource_t source;
433     char *p, *controlsock = "/var/run/gpsd.sock";
434     fd_set select_set;
435     unsigned char buf[BUFLEN];
436     char line[80], *explanation;
437     int bailout = 0;
438
439     gmt_offset = (int)tzoffset();
440     /*@ -observertrans @*/
441     (void)putenv("TZ=GMT");     // for ctime()
442     /*@ +observertrans @*/
443     /*@ -branchstate @*/
444     while ((option = getopt(argc, argv, "D:F:LVhl:")) != -1) {
445         switch (option) {
446         case 'D':
447             debuglevel = atoi(optarg);
448             break;
449         case 'F':
450             controlsock = optarg;
451             break;
452         case 'V':
453             (void)printf("gpsmon: %s (revision %s)\n", VERSION, REVISION);
454             exit(0);
455         case 'L':               /* list known device types */
456             (void)
457                 fputs
458                 ("General commands available per type. '+' means there are private commands.\n",
459                  stdout);
460             for (active = monitor_objects; *active; active++) {
461                 (void)fputs("i l q ^S ^Q", stdout);
462                 (void)fputc(' ', stdout);
463 #ifdef ALLOW_RECONFIGURE
464                 if ((*active)->driver->mode_switcher != NULL)
465                     (void)fputc('n', stdout);
466                 else
467                     (void)fputc(' ', stdout);
468                 (void)fputc(' ', stdout);
469                 if ((*active)->driver->speed_switcher != NULL)
470                     (void)fputc('s', stdout);
471                 else
472                     (void)fputc(' ', stdout);
473                 (void)fputc(' ', stdout);
474                 if ((*active)->driver->rate_switcher != NULL)
475                     (void)fputc('x', stdout);
476                 else
477                     (void)fputc(' ', stdout);
478                 (void)fputc(' ', stdout);
479 #endif /* ALLOW_RECONFIGURE */
480 #ifdef ALLOW_CONTROLSEND
481                 if ((*active)->driver->control_send != NULL)
482                     (void)fputc('x', stdout);
483                 else
484                     (void)fputc(' ', stdout);
485 #endif /* ALLOW_CONTROLSEND */
486                 (void)fputc(' ', stdout);
487                 if ((*active)->command != NULL)
488                     (void)fputc('+', stdout);
489                 else
490                     (void)fputc(' ', stdout);
491                 (void)fputs("\t", stdout);
492                 (void)fputs((*active)->driver->type_name, stdout);
493                 (void)fputc('\n', stdout);
494             }
495             exit(0);
496         case 'l':               /* enable logging at startup */
497             logfile = fopen(optarg, "w");
498             if (logfile == NULL) {
499                 (void)fprintf(stderr, "Couldn't open logfile for writing.\n");
500                 exit(1);
501             }
502             break;
503         case 'h':
504         case '?':
505         default:
506             (void)
507                 fputs
508                 ("usage:  gpsmon [-?hVl] [-D debuglevel] [-F controlsock] [server[:port:[device]]]\n",
509                  stderr);
510             exit(1);
511         }
512     }
513     /*@ +branchstate @*/
514
515     if (optind < argc) {
516         gpsd_source_spec(argv[optind], &source);
517     } else
518         gpsd_source_spec(NULL, &source);
519
520     gpsd_init(&session, &context, NULL);
521
522     /*@ -boolops */
523     if ((optind >= argc || source.device == NULL
524         || strchr(argv[optind], ':') != NULL)
525 #ifdef HAVE_BLUEZ
526         && bachk(argv[optind])) {
527 #else
528         ) {
529 #endif
530         (void)gps_open_r(source.server, source.port, &session.gpsdata);
531         if (session.gpsdata.gps_fd < 0) {
532             (void)fprintf(stderr,
533                           "%s: connection failure on %s:%s, error %d = %s.\n",
534                           argv[0], source.server, source.port,
535                           session.gpsdata.gps_fd,
536                           netlib_errstr(session.gpsdata.gps_fd));
537             exit(1);
538         }
539         controlfd = open(controlsock, O_RDWR);
540         if (source.device != NULL)
541             (void)gps_send(&session.gpsdata,
542                            "?WATCH={\"raw\":2,\"device\":\"%s\"}\r\n",
543                            source.device);
544         else
545             (void)gps_send(&session.gpsdata, "?WATCH={\"raw\":2}\r\n");
546         serial = false;
547     } else {
548         (void)strlcpy(session.gpsdata.dev.path, argv[optind],
549                       sizeof(session.gpsdata.dev.path));
550         if (gpsd_activate(&session) == -1) {
551             gpsd_report(LOG_ERROR,
552                         "activation of device %s failed, errno=%d\n",
553                         session.gpsdata.dev.path, errno);
554             exit(2);
555         }
556
557         controlfd = session.gpsdata.gps_fd;
558         serial = true;
559     }
560     /*@ +boolops */
561     /*@ +nullpass +branchstate @*/
562
563     /*
564      * This is a monitoring utility. Disable autoprobing, because
565      * in some cases (e.g. SiRFs) there is no way to probe a chip
566      * type without flipping it to native mode.
567      */
568     context.readonly = true;
569
570     /* quit cleanly if an assertion fails */
571     (void)signal(SIGABRT, onsig);
572     if (setjmp(assertbuf) > 0) {
573         if (logfile)
574             (void)fclose(logfile);
575         (void)endwin();
576         (void)fputs("gpsmon: assertion failure, probable I/O error\n",
577                     stderr);
578         exit(1);
579     }
580
581     (void)initscr();
582     (void)cbreak();
583     (void)noecho();
584     (void)intrflush(stdscr, FALSE);
585     (void)keypad(stdscr, true);
586     curses_active = true;
587
588 #define CMDWINHEIGHT    1
589
590     /*@ -onlytrans @*/
591     statwin = newwin(CMDWINHEIGHT, 30, 0, 0);
592     cmdwin = newwin(CMDWINHEIGHT, 0, 0, 30);
593     packetwin = newwin(0, 0, CMDWINHEIGHT, 0);
594     if (statwin == NULL || cmdwin == NULL || packetwin == NULL)
595         goto quit;
596     (void)scrollok(packetwin, true);
597     (void)wsetscrreg(packetwin, 0, LINES - CMDWINHEIGHT);
598     /*@ +onlytrans @*/
599
600     (void)wmove(packetwin, 0, 0);
601
602     FD_ZERO(&select_set);
603
604
605     if ((bailout = setjmp(terminate)) == 0) {
606         /*@ -observertrans @*/
607         for (;;) {
608             /* *INDENT-OFF* */
609             type_name =
610                 session.device_type ? session.device_type->type_name : "Unknown device";
611             /* *INDENT-ON* */
612             (void)wattrset(statwin, A_BOLD);
613             if (serial)
614                 display(statwin, 0, 0, "%s %4d %c %d",
615                         session.gpsdata.dev.path,
616                         gpsd_get_speed(&session.ttyset),
617                         session.gpsdata.dev.parity,
618                         session.gpsdata.dev.stopbits);
619             else
620                 /*@ -nullpass @*/
621                 display(statwin, 0, 0, "%s:%s:%s",
622                         source.server, source.port, session.gpsdata.dev.path);
623             /*@ +nullpass @*/
624             (void)wattrset(statwin, A_NORMAL);
625             (void)wmove(cmdwin, 0, 0);
626
627             /* get a packet -- calls gpsd_poll() */
628             if ((len = readpkt()) > 0 && session.packet.outbuflen > 0) {
629                 /* switch types on packet receipt */
630                 /*@ -nullpass */
631                 if (session.packet.type != last_type) {
632                     last_type = session.packet.type;
633                     if (!switch_type(session.device_type))
634                         longjmp(terminate, TERM_DRIVER_SWITCH);
635                 }
636                 /*@ +nullpass */
637
638                 /* refresh all windows */
639                 (void)wprintw(cmdwin, type_name);
640                 (void)wprintw(cmdwin, "> ");
641                 (void)wclrtoeol(cmdwin);
642                 if (active != NULL && len > 0 && session.packet.outbuflen > 0)
643                     (*active)->update();
644                 (void)wprintw(packetwin, "(%d) ", session.packet.outbuflen);
645                 packet_dump((char *)session.packet.outbuffer,
646                             session.packet.outbuflen);
647                 (void)wnoutrefresh(statwin);
648                 (void)wnoutrefresh(cmdwin);
649                 if (devicewin != NULL)
650                     (void)wnoutrefresh(devicewin);
651                 if (packetwin != NULL)
652                     (void)wnoutrefresh(packetwin);
653                 (void)doupdate();
654             }
655
656             /* rest of this invoked only if user has pressed a key */
657             FD_SET(0, &select_set);
658             FD_SET(session.gpsdata.gps_fd, &select_set);
659
660             if (select(FD_SETSIZE, &select_set, NULL, NULL, NULL) == -1)
661                 break;
662
663             if (FD_ISSET(0, &select_set)) {
664                 char *arg;
665                 (void)wmove(cmdwin, 0, (int)strlen(type_name) + 2);
666                 (void)wrefresh(cmdwin);
667                 (void)echo();
668                 /*@ -usedef -compdef @*/
669                 (void)wgetnstr(cmdwin, line, 80);
670                 (void)noecho();
671                 if (packetwin != NULL)
672                     (void)wrefresh(packetwin);
673                 (void)wrefresh(cmdwin);
674
675                 if ((p = strchr(line, '\r')) != NULL)
676                     *p = '\0';
677
678                 if (line[0] == '\0')
679                     continue;
680                 /*@ +usedef +compdef @*/
681
682                 arg = line;
683                 if (isspace(line[1])) {
684                     for (arg = line + 2; *arg != '\0' && isspace(*arg); arg++)
685                         arg++;
686                     arg++;
687                 } else
688                     arg = line + 1;
689
690                 if (active != NULL && (*active)->command != NULL) {
691                     status = (*active)->command(line);
692                     if (status == COMMAND_TERMINATE)
693                         goto quit;
694                     else if (status == COMMAND_MATCH)
695                         continue;
696                     assert(status == COMMAND_UNKNOWN);
697                 }
698                 switch (line[0]) {
699 #ifdef ALLOW_RECONFIGURE
700                 case 'c':       /* change cycle time */
701                     if (active == NULL)
702                         monitor_complain("No device defined yet");
703                     else if (serial) {
704                         double rate = strtod(arg, NULL);
705                         /* Ugh...should have a controlfd slot
706                          * in the session structure, really
707                          */
708                         if ((*active)->driver->rate_switcher) {
709                             int dfd = session.gpsdata.gps_fd;
710                             session.gpsdata.gps_fd = controlfd;
711                             /* *INDENT-OFF* */
712                             if ((*active)->driver->rate_switcher(&session, rate)) {
713                                 monitor_dump_send();
714                             } else
715                                 monitor_complain("Rate not supported.");
716                             /* *INDENT-ON* */
717                             session.gpsdata.gps_fd = dfd;
718                         } else
719                             monitor_complain
720                                 ("Device type has no rate switcher");
721                     } else {
722                         line[0] = 'c';
723                         /*@ -sefparams @*/
724                         assert(write
725                                (session.gpsdata.gps_fd, line,
726                                 strlen(line)) != -1);
727                         /* discard response */
728                         assert(read(session.gpsdata.gps_fd, buf, sizeof(buf))
729                                != -1);
730                         /*@ +sefparams @*/
731                     }
732                     break;
733 #endif /* ALLOW_RECONFIGURE */
734
735                 case 'i':       /* start probing for subtype */
736                     if (active == NULL)
737                         monitor_complain("No GPS type detected.");
738                     else {
739                         if (strcspn(line, "01") == strlen(line))
740                             context.readonly = !context.readonly;
741                         else
742                             context.readonly = (atoi(line + 1) == 0);
743                         /* *INDENT-OFF* */
744                         (void)gpsd_switch_driver(&session,
745                                  (*active)->driver->type_name);
746                         /* *INDENT-ON* */
747                     }
748                     break;
749
750                 case 'l':       /* open logfile */
751                     if (logfile != NULL) {
752                         if (packetwin != NULL)
753                             (void)wprintw(packetwin,
754                                           ">>> Logging to %s off", logfile);
755                         (void)fclose(logfile);
756                     }
757
758                     if ((logfile = fopen(line + 1, "a")) != NULL)
759                         if (packetwin != NULL)
760                             (void)wprintw(packetwin,
761                                           ">>> Logging to %s on", logfile);
762                     break;
763
764 #ifdef ALLOW_RECONFIGURE
765                 case 'n':       /* change mode */
766                     /* if argument not specified, toggle */
767                     if (strcspn(line, "01") == strlen(line)) {
768                         /* *INDENT-OFF* */
769                         v = (unsigned int)TEXTUAL_PACKET_TYPE(
770                             session.packet.type);
771                         /* *INDENT-ON* */
772                     } else
773                         v = (unsigned)atoi(line + 1);
774                     if (active == NULL)
775                         monitor_complain("No device defined yet");
776                     else if (serial) {
777                         /* Ugh...should have a controlfd slot
778                          * in the session structure, really
779                          */
780                         if ((*active)->driver->mode_switcher) {
781                             int dfd = session.gpsdata.gps_fd;
782                             session.gpsdata.gps_fd = controlfd;
783                             (*active)->driver->mode_switcher(&session,
784                                                              (int)v);
785                             monitor_dump_send();
786                             (void)tcdrain(session.gpsdata.gps_fd);
787                             (void)usleep(50000);
788                             session.gpsdata.gps_fd = dfd;
789                         } else
790                             monitor_complain
791                                 ("Device type has no mode switcher");
792                     } else {
793                         line[0] = 'n';
794                         line[1] = ' ';
795                         line[2] = '0' + v;
796                         /*@ -sefparams @*/
797                         assert(write
798                                (session.gpsdata.gps_fd, line,
799                                 strlen(line)) != -1);
800                         /* discard response */
801                         assert(read(session.gpsdata.gps_fd, buf, sizeof(buf))
802                                != -1);
803                         /*@ +sefparams @*/
804                     }
805                     break;
806 #endif /* ALLOW_RECONFIGURE */
807
808                 case 'q':       /* quit */
809                     goto quit;
810
811 #ifdef ALLOW_RECONFIGURE
812                 case 's':       /* change speed */
813                     if (active == NULL)
814                         monitor_complain("No device defined yet");
815                     else if (serial) {
816                         speed_t speed;
817                         char parity = session.gpsdata.dev.parity;
818                         unsigned int stopbits =
819                             (unsigned int)session.gpsdata.dev.stopbits;
820                         char *modespec;
821
822                         modespec = strchr(arg, ':');
823                         /*@ +charint @*/
824                         if (modespec != NULL) {
825                             if (strchr("78", *++modespec) == NULL) {
826                                 monitor_complain
827                                     ("No support for that word length.");
828                                 break;
829                             }
830                             parity = *++modespec;
831                             if (strchr("NOE", parity) == NULL) {
832                                 monitor_complain("What parity is '%c'?.",
833                                                  parity);
834                                 break;
835                             }
836                             stopbits = (unsigned int)*++modespec;
837                             if (strchr("12", (char)stopbits) == NULL) {
838                                 monitor_complain("Stop bits must be 1 or 2.");
839                                 break;
840                             }
841                             stopbits = (unsigned int)(stopbits - '0');
842                         }
843                         /*@ -charint @*/
844                         speed = (unsigned)atoi(arg);
845                         /* Ugh...should have a controlfd slot
846                          * in the session structure, really
847                          */
848                         /* *INDENT-OFF* */
849                         if ((*active)->driver->speed_switcher) {
850                             int dfd = session.gpsdata.gps_fd;
851                             session.gpsdata.gps_fd = controlfd;
852                             if ((*active)->
853                                 driver->speed_switcher(&session, speed,
854                                                        parity, (int)
855                                                        stopbits)) {
856                                 monitor_dump_send();
857                                 /*
858                                  * See the comment attached to the 'B'
859                                  * command in gpsd.  Allow the control
860                                  * string time to register at the GPS
861                                  * before we do the baud rate switch,
862                                  * which effectively trashes the UART's
863                                  * buffer.
864                                  */
865                                 (void)tcdrain(session.gpsdata.gps_fd);
866                                 (void)usleep(50000);
867                                 (void)gpsd_set_speed(&session, speed,
868                                                      parity, stopbits);
869                             } else
870                                 monitor_complain
871                                     ("Speed/mode combination not supported.");
872                             session.gpsdata.gps_fd = dfd;
873                         } else
874                             monitor_complain
875                                 ("Device type has no speed switcher");
876                         /* *INDENT-ON* */
877                     } else {
878                         line[0] = 'b';
879                         /*@ -sefparams @*/
880                         assert(write
881                                (session.gpsdata.gps_fd, line,
882                                 strlen(line)) != -1);
883                         /* discard response */
884                         assert(read(session.gpsdata.gps_fd, buf, sizeof(buf))
885                                != -1);
886                         /*@ +sefparams @*/
887                     }
888                     break;
889 #endif /* ALLOW_RECONFIGURE */
890
891                 case 't':       /* force device type */
892                     if (strlen(arg) > 0) {
893                         int matchcount = 0;
894                         const struct gps_type_t **dp, *forcetype = NULL;
895                         for (dp = gpsd_drivers; *dp; dp++) {
896                             if (strstr((*dp)->type_name, arg) != NULL) {
897                                 forcetype = *dp;
898                                 matchcount++;
899                             }
900                         }
901                         if (matchcount == 0) {
902                             monitor_complain
903                                 ("No driver type matches '%s'.", arg);
904                         } else if (matchcount == 1) {
905                             assert(forcetype != NULL);
906                             /* *INDENT-OFF* */
907                             if (switch_type(forcetype))
908                                 (void)gpsd_switch_driver(&session,
909                                                          forcetype->type_name);
910                             /* *INDENT-ON* */
911                         } else {
912                             monitor_complain
913                                 ("Multiple driver type names match '%s'.",
914                                  arg);
915                         }
916                     }
917                     break;
918
919 #ifdef ALLOW_CONTROLSEND
920                 case 'x':       /* send control packet */
921                     if (active == NULL)
922                         monitor_complain("No device defined yet");
923                     else {
924                         /*@ -compdef @*/
925                         int st = gpsd_hexpack(arg, (char *)buf, strlen(arg));
926                         if (st < 0)
927                             monitor_complain
928                                 ("Invalid hex string (error %d)", st);
929                         else if ((*active)->driver->control_send == NULL)
930                             monitor_complain
931                                 ("Device type has no control-send method.");
932                         else if (!monitor_control_send(buf, (size_t) st))
933                             monitor_complain("Control send failed.");
934                     }
935                     /*@ +compdef @*/
936                     break;
937
938                 case 'X':       /* send raw packet */
939                     /*@ -compdef @*/
940                     len =
941                         (ssize_t) gpsd_hexpack(arg, (char *)buf, strlen(arg));
942                     if (len < 0)
943                         monitor_complain("Invalid hex string (error %d)",
944                                          len);
945                     else if (!monitor_raw_send(buf, (size_t) len))
946                         monitor_complain("Raw send failed.");
947                     /*@ +compdef @*/
948                     break;
949 #endif /* ALLOW_CONTROLSEND */
950
951                 default:
952                     monitor_complain("Unknown command");
953                     break;
954                 }
955             }
956         }
957         /*@ +nullpass @*/
958         /*@ +observertrans @*/
959     }
960
961   quit:
962     /* we'll fall through to here on longjmp() */
963     gpsd_close(&session);
964     if (logfile)
965         (void)fclose(logfile);
966     (void)endwin();
967
968     explanation = NULL;
969     switch (bailout) {
970     case TERM_SELECT_FAILED:
971         explanation = "select(2) failed\n";
972         break;
973     case TERM_DRIVER_SWITCH:
974         explanation = "Driver type switch failed\n";
975         break;
976     case TERM_EMPTY_READ:
977         explanation = "Device went offline\n";
978         break;
979     case TERM_READ_ERROR:
980         explanation = "Read error from device\n";
981         break;
982     }
983
984     if (explanation != NULL)
985         (void)fputs(explanation, stderr);
986     exit(0);
987 }
988
989 /* gpsmon.c ends here */