pud: nmealib: fix buffer overrun
[olsrd.git] / lib / pud / nmealib / src / generate.c
1 /*
2  * This file is part of nmealib.
3  *
4  * Copyright (c) 2008 Timur Sinitsyn
5  * Copyright (c) 2011 Ferry Huberts
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <nmea/generate.h>
22
23 #include <nmea/tok.h>
24 #include <nmea/conversions.h>
25
26 #include <stdio.h>
27 #include <stdbool.h>
28
29 /**
30  * Generate a GPGGA sentence from an nmeaGPGGA structure
31  *
32  * @param s a pointer to the buffer to generate the string in
33  * @param len the size of the buffer
34  * @param pack the structure
35  * @return the length of the generated sentence
36  */
37 int nmea_gen_GPGGA(char *s, const int len, const nmeaGPGGA *pack) {
38         char sTime[16];
39         char sLat[16];
40         char sNs[2];
41         char sLon[16];
42         char sEw[2];
43         char sSig[4];
44         char sSatInUse[4];
45         char sHdop[16];
46         char sElv[16];
47         char sElvUnit[2];
48
49         sTime[0] = 0;
50         sLat[0] = 0;
51         sNs[0] = sNs[1] = 0;
52         sLon[0] = 0;
53         sEw[0] = sEw[1] = 0;
54         sSig[0] = 0;
55         sSatInUse[0] = 0;
56         sHdop[0] = 0;
57         sElv[0] = 0;
58         sElvUnit[0] = sElvUnit[1] = 0;
59
60         if (nmea_INFO_is_present(pack->present, UTCTIME)) {
61                 snprintf(&sTime[0], sizeof(sTime), "%02d%02d%02d.%02d", pack->utc.hour, pack->utc.min, pack->utc.sec,
62                                 pack->utc.hsec);
63         }
64         if (nmea_INFO_is_present(pack->present, LAT)) {
65                 snprintf(&sLat[0], sizeof(sLat), "%09.4f", pack->lat);
66                 sNs[0] = pack->ns;
67         }
68         if (nmea_INFO_is_present(pack->present, LON)) {
69                 snprintf(&sLon[0], sizeof(sLon), "%010.4f", pack->lon);
70                 sEw[0] = pack->ew;
71         }
72         if (nmea_INFO_is_present(pack->present, SIG)) {
73                 snprintf(&sSig[0], sizeof(sSig), "%1d", pack->sig);
74         }
75         if (nmea_INFO_is_present(pack->present, SATINUSECOUNT)) {
76                 snprintf(&sSatInUse[0], sizeof(sSatInUse), "%02d", pack->satinuse);
77         }
78         if (nmea_INFO_is_present(pack->present, HDOP)) {
79                 snprintf(&sHdop[0], sizeof(sHdop), "%03.1f", pack->HDOP);
80         }
81         if (nmea_INFO_is_present(pack->present, ELV)) {
82                 snprintf(&sElv[0], sizeof(sElv), "%03.1f", pack->elv);
83                 sElvUnit[0] = pack->elv_units;
84         }
85
86         return nmea_printf(s, len, "$GPGGA,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,,,,", &sTime[0], &sLat[0], &sNs[0],
87                         &sLon[0], &sEw[0], &sSig[0], &sSatInUse[0], &sHdop[0], &sElv[0], &sElvUnit[0]);
88 }
89
90 /**
91  * Generate a GPGSA sentence from an nmeaGPGSA structure
92  *
93  * @param s a pointer to the buffer to generate the string in
94  * @param len the size of the buffer
95  * @param pack the structure
96  * @return the length of the generated sentence
97  */
98 int nmea_gen_GPGSA(char *s, const int len, const nmeaGPGSA *pack) {
99         int i;
100         char sFixMode[2];
101         char sFixType[2];
102         char sSatPrn[(NMEA_MAXSAT * 4) + 1];
103         char sPdop[16];
104         char sHdop[16];
105         char sVdop[16];
106
107         char * psSatPrn = &sSatPrn[0];
108         int ssSatPrn = sizeof(sSatPrn) - 1;
109
110         bool satinuse = nmea_INFO_is_present(pack->present, SATINUSE);
111
112         sFixMode[0] = sFixMode[1] = 0;
113         sFixType[0] = sFixType[1] = 0;
114         sSatPrn[0] = 0;
115         sPdop[0] = 0;
116         sHdop[0] = 0;
117         sVdop[0] = 0;
118
119         if (nmea_INFO_is_present(pack->present, FIX)) {
120                 sFixMode[0] = pack->fix_mode;
121                 snprintf(&sFixType[0], sizeof(sFixType), "%1d", pack->fix_type);
122         }
123
124         for (i = 0; i < NMEA_MAXSAT; i++) {
125                 if (satinuse && pack->sat_prn[i]) {
126                         int cnt = snprintf(psSatPrn, ssSatPrn, "%d", pack->sat_prn[i]);
127                         if (cnt >= ssSatPrn) {
128                                 ssSatPrn = 0;
129                                 psSatPrn = &sSatPrn[sizeof(sSatPrn) - 1];
130                                 *psSatPrn = '\0';
131                                 break;
132                         } else {
133                                 ssSatPrn -= cnt;
134                                 psSatPrn += cnt;
135                         }
136                 }
137                 if (i < (NMEA_MAXSAT - 1)) {
138                         *psSatPrn = ',';
139                         psSatPrn++;
140                         ssSatPrn--;
141                         *psSatPrn = '\0';
142                 }
143         }
144
145         if (nmea_INFO_is_present(pack->present, PDOP)) {
146                 snprintf(&sPdop[0], sizeof(sPdop), "%03.1f", pack->PDOP);
147         }
148         if (nmea_INFO_is_present(pack->present, HDOP)) {
149                 snprintf(&sHdop[0], sizeof(sHdop), "%03.1f", pack->HDOP);
150         }
151         if (nmea_INFO_is_present(pack->present, VDOP)) {
152                 snprintf(&sVdop[0], sizeof(sVdop), "%03.1f", pack->VDOP);
153         }
154
155         return nmea_printf(s, len, "$GPGSA,%s,%s,%s,%s,%s,%s", &sFixMode[0], &sFixType[0], &sSatPrn[0], &sPdop[0],
156                         &sHdop[0], &sVdop[0]);
157 }
158
159 /**
160  * Generate a GPGSV sentence from an nmeaGPGSV structure
161  *
162  * @param s a pointer to the buffer to generate the string in
163  * @param len the size of the buffer
164  * @param pack the structure
165  * @return the length of the generated sentence
166  */
167 int nmea_gen_GPGSV(char *s, const int len, const nmeaGPGSV *pack) {
168         char sCount[2];
169         char sIndex[2];
170         char sSatCount[4];
171         char sSatInfo[(NMEA_SATINPACK * 4) + 1];
172         char * psSatInfo = &sSatInfo[0];
173         int ssSatInfo = sizeof(sSatInfo) - 1;
174         bool satinview = nmea_INFO_is_present(pack->present, SATINVIEW);
175         int i;
176
177         sCount[0] = 0;
178         sIndex[0] = 0;
179         sSatCount[0] = 0;
180         sSatInfo[0] = 0;
181
182         if (satinview) {
183                 snprintf(&sCount[0], sizeof(sCount), "%1d", pack->pack_count);
184                 snprintf(&sIndex[0], sizeof(sIndex), "%1d", pack->pack_index);
185                 snprintf(&sSatCount[0], sizeof(sSatCount), "%02d", pack->sat_count);
186         }
187         for (i = 0; i < NMEA_SATINPACK; i++) {
188                 int cnt = 0;
189                 if (satinview && pack->sat_data[i].id) {
190                         cnt = snprintf(psSatInfo, ssSatInfo, "%02d,%02d,%03d,%02d", pack->sat_data[i].id, pack->sat_data[i].elv,
191                                         pack->sat_data[i].azimuth, pack->sat_data[i].sig);
192                 } else {
193                         cnt = snprintf(psSatInfo, ssSatInfo, ",,,");
194                 }
195                 if (cnt >= ssSatInfo) {
196                         ssSatInfo = 0;
197                         psSatInfo = &sSatInfo[sizeof(sSatInfo) - 1];
198                         *psSatInfo = '\0';
199                         break;
200                 } else {
201                         ssSatInfo -= cnt;
202                         psSatInfo += cnt;
203                 }
204                 if (i < (NMEA_SATINPACK - 1)) {
205                         *psSatInfo = ',';
206                         psSatInfo++;
207                         ssSatInfo--;
208                         *psSatInfo = '\0';
209                 }
210         }
211
212         return nmea_printf(s, len, "$GPGSV,%s,%s,%s,%s", &sCount[0], &sIndex[0], &sSatCount[0], &sSatInfo[0]);
213 }
214
215 /**
216  * Generate a GPRMC sentence from an nmeaGPRMC structure
217  *
218  * @param s a pointer to the buffer to generate the string in
219  * @param len the size of the buffer
220  * @param pack the structure
221  * @return the length of the generated sentence
222  */
223 int nmea_gen_GPRMC(char *s, const int len, const nmeaGPRMC *pack) {
224         char sTime[16];
225         char sDate[16];
226         char sLat[16];
227         char sNs[2];
228         char sLon[16];
229         char sEw[2];
230         char sSpeed[16];
231         char sTrack[16];
232         char sMagvar[16];
233         char sMagvar_ew[2];
234
235         sTime[0] = 0;
236         sDate[0] = 0;
237         sLat[0] = 0;
238         sNs[0] = sNs[1] = 0;
239         sLon[0] = 0;
240         sEw[0] = sEw[1] = 0;
241         sSpeed[0] = 0;
242         sTrack[0] = 0;
243         sMagvar[0] = 0;
244         sMagvar_ew[0] = sMagvar_ew[1] = 0;
245
246         if (nmea_INFO_is_present(pack->present, UTCDATE)) {
247                 snprintf(&sDate[0], sizeof(sDate), "%02d%02d%02d", pack->utc.day, pack->utc.mon + 1, pack->utc.year - 100);
248         }
249         if (nmea_INFO_is_present(pack->present, UTCTIME)) {
250                 snprintf(&sTime[0], sizeof(sTime), "%02d%02d%02d.%02d", pack->utc.hour, pack->utc.min, pack->utc.sec,
251                                 pack->utc.hsec);
252         }
253         if (nmea_INFO_is_present(pack->present, LAT)) {
254                 snprintf(&sLat[0], sizeof(sLat), "%09.4f", pack->lat);
255                 sNs[0] = pack->ns;
256         }
257         if (nmea_INFO_is_present(pack->present, LON)) {
258                 snprintf(&sLon[0], sizeof(sLon), "%010.4f", pack->lon);
259                 sEw[0] = pack->ew;
260         }
261         if (nmea_INFO_is_present(pack->present, SPEED)) {
262                 snprintf(&sSpeed[0], sizeof(sSpeed), "%03.1f", pack->speed);
263         }
264         if (nmea_INFO_is_present(pack->present, TRACK)) {
265                 snprintf(&sTrack[0], sizeof(sTrack), "%03.1f", pack->track);
266         }
267         if (nmea_INFO_is_present(pack->present, MAGVAR)) {
268                 snprintf(&sMagvar[0], sizeof(sMagvar), "%03.1f", pack->magvar);
269                 sMagvar_ew[0] = pack->magvar_ew;
270         }
271
272         return nmea_printf(s, len, "$GPRMC,%s,%C,%s,%s,%s,%s,%s,%s,%s,%s,%s,%C", &sTime[0], pack->status, &sLat[0], &sNs[0],
273                         &sLon[0], &sEw[0], &sSpeed[0], &sTrack[0], &sDate[0], &sMagvar[0], &sMagvar_ew[0], pack->mode);
274 }
275
276 /**
277  * Generate a GPVTG sentence from an nmeaGPVTG structure
278  *
279  * @param s a pointer to the buffer to generate the string in
280  * @param len the size of the buffer
281  * @param pack the structure
282  * @return the length of the generated sentence
283  */
284 int nmea_gen_GPVTG(char *s, const int len, const nmeaGPVTG *pack) {
285         char sTrackT[16];
286         char sTrackM[16];
287         char sSpeedN[16];
288         char sSpeedK[16];
289         char sUnitT[2];
290         char sUnitM[2];
291         char sUnitN[2];
292         char sUnitK[2];
293
294         sTrackT[0] = 0;
295         sTrackM[0] = 0;
296         sSpeedN[0] = 0;
297         sSpeedK[0] = 0;
298         sUnitT[0] = sUnitT[1] = 0;
299         sUnitM[0] = sUnitM[1] = 0;
300         sUnitN[0] = sUnitN[1] = 0;
301         sUnitK[0] = sUnitK[1] = 0;
302
303         if (nmea_INFO_is_present(pack->present, TRACK)) {
304                 snprintf(&sTrackT[0], sizeof(sTrackT), "%03.1f", pack->track);
305                 sUnitT[0] = 'T';
306         }
307         if (nmea_INFO_is_present(pack->present, MTRACK)) {
308                 snprintf(&sTrackM[0], sizeof(sTrackM), "%03.1f", pack->mtrack);
309                 sUnitM[0] = 'M';
310         }
311         if (nmea_INFO_is_present(pack->present, SPEED)) {
312                 snprintf(&sSpeedN[0], sizeof(sSpeedN), "%03.1f", pack->spn);
313                 sUnitN[0] = 'N';
314                 snprintf(&sSpeedK[0], sizeof(sSpeedK), "%03.1f", pack->spk);
315                 sUnitK[0] = 'K';
316         }
317
318         return nmea_printf(s, len, "$GPVTG,%s,%s,%s,%s,%s,%s,%s,%s", &sTrackT[0], &sUnitT[0], &sTrackM[0],
319                         &sUnitM[0], &sSpeedN[0], &sUnitN[0], &sSpeedK[0], &sUnitK[0]);
320 }
321
322 /**
323  * Generate a number of sentences from an nmeaINFO structure.
324  *
325  * @param s a pointer to the buffer in which to generate the sentences
326  * @param len the size of the buffer
327  * @param info the structure
328  * @param generate_mask the mask of which sentences to generate
329  * @return the total length of the generated sentences
330  */
331 int nmea_generate(char *s, const int len, const nmeaINFO *info, const int generate_mask) {
332         int gen_count = 0;
333         int pack_mask = generate_mask;
334
335         if (!s || !len || !info || !generate_mask)
336                 return 0;
337
338         while (pack_mask) {
339                 if (pack_mask & GPGGA) {
340                         nmeaGPGGA gga;
341
342                         nmea_info2GPGGA(info, &gga);
343                         gen_count += nmea_gen_GPGGA(s + gen_count, len - gen_count, &gga);
344                         pack_mask &= ~GPGGA;
345                 } else if (pack_mask & GPGSA) {
346                         nmeaGPGSA gsa;
347
348                         nmea_info2GPGSA(info, &gsa);
349                         gen_count += nmea_gen_GPGSA(s + gen_count, len - gen_count, &gsa);
350                         pack_mask &= ~GPGSA;
351                 } else if (pack_mask & GPGSV) {
352                         nmeaGPGSV gsv;
353                         int gsv_it;
354                         int gsv_count = nmea_gsv_npack(info->satinfo.inview);
355
356                         for (gsv_it = 0; gsv_it < gsv_count && len - gen_count > 0; gsv_it++) {
357                                 nmea_info2GPGSV(info, &gsv, gsv_it);
358                                 gen_count += nmea_gen_GPGSV(s + gen_count, len - gen_count, &gsv);
359                         }
360                         pack_mask &= ~GPGSV;
361                 } else if (pack_mask & GPRMC) {
362                         nmeaGPRMC rmc;
363
364                         nmea_info2GPRMC(info, &rmc);
365                         gen_count += nmea_gen_GPRMC(s + gen_count, len - gen_count, &rmc);
366                         pack_mask &= ~GPRMC;
367                 } else if (pack_mask & GPVTG) {
368                         nmeaGPVTG vtg;
369
370                         nmea_info2GPVTG(info, &vtg);
371                         gen_count += nmea_gen_GPVTG(s + gen_count, len - gen_count, &vtg);
372                         pack_mask &= ~GPVTG;
373                 } else {
374                         /* no more known sentences to process */
375                         break;
376                 }
377
378                 if (len - gen_count <= 0)
379                         break;
380         }
381
382         return gen_count;
383 }