pud: adjust to new nmealib
[olsrd.git] / lib / pud / src / posFile.c
1 #include "posFile.h"
2
3 /* Plugin includes */
4 #include "pud.h"
5 #include "configTools.h"
6
7 /* OLSR includes */
8
9 /* System includes */
10 #include <stdio.h>
11 #include <string.h>
12 #include <regex.h>
13 #include <sys/stat.h>
14
15 /** the maximal length of a line that is read from the file */
16 #define LINE_LENGTH 256
17
18 /** regular expression describing a comment */
19 static const char * regexCommentString = "^([[:space:]]*|[[:space:]#]+.*)$";
20
21 /** regular expression describing a key/value pair */
22 static const char * regexNameValueString =
23                 "^[[:space:]]*([^[:space:]]+)[[:space:]]*=[[:space:]]*([^[:space:]]+)[[:space:]]*$";
24
25 /** the number of matches in regexNameValueString */
26 static const size_t regexNameValuematchCount = 3;
27
28 /** the compiled regular expression describing a comment */
29 static regex_t regexComment;
30
31 /** the compiled regular expression describing a key/value pair */
32 static regex_t regexNameValue;
33
34 /** true when the plugin has been started */
35 static bool started = false;
36
37 /** type to hold the cached stat result */
38 typedef struct _CachedStat {
39         time_t timeStamp; /* Time of last modification (second resolution) */
40 } CachedStat;
41
42 /** the cached stat result */
43 static CachedStat cachedStat;
44
45 /**
46  * Initialises the positionFile reader.
47  * @return true upon success, false otherwise
48  */
49 bool startPositionFile(void) {
50         if (started) {
51                 return true;
52         }
53
54         if (regcomp(&regexComment, regexCommentString, REG_EXTENDED | REG_ICASE)) {
55                 pudError(false, "Could not compile regex \"%s\"", regexCommentString);
56                 return false;
57         }
58
59         if (regcomp(&regexNameValue, regexNameValueString, REG_EXTENDED | REG_ICASE)) {
60                 pudError(false, "Could not compile regex \"%s\"", regexNameValueString);
61                 regfree(&regexComment);
62                 return false;
63         }
64
65         cachedStat.timeStamp = -1;
66
67         started = true;
68         return true;
69 }
70
71 /**
72  * Cleans up the positionFile reader.
73  */
74 void stopPositionFile(void) {
75         if (started) {
76                 regfree(&regexNameValue);
77                 regfree(&regexComment);
78                 started = false;
79         }
80 }
81
82 /**
83  * Performs a regex match
84  * @param regex the compiled regex to match against
85  * @param line the line to match
86  * @param nmatch the number of matches to produce
87  * @param pmatch the array with match information
88  * @return true upon success, false otherwise
89  */
90 static bool regexMatch(regex_t * regex, char * line, size_t nmatch, regmatch_t pmatch[]) {
91         int result = regexec(regex, line, nmatch, pmatch, 0);
92         if (!result) {
93                 return true;
94         }
95
96         if (result == REG_NOMATCH) {
97                 return false;
98         }
99
100         {
101                 char msgbuf[256];
102                 regerror(result, regex, msgbuf, sizeof(msgbuf));
103                 pudError(false, "Regex match failed: %s", msgbuf);
104         }
105
106         return false;
107 }
108
109 /** the buffer in which to store a line read from the file */
110 static char line[LINE_LENGTH];
111
112 /**
113  * Read the position file
114  * @param fileName the filename
115  * @param nmeaInfo the NMEA data
116  */
117 bool readPositionFile(char * fileName, nmeaINFO * nmeaInfo) {
118         bool retval = false;
119         struct stat statBuf;
120         nmeaINFO result;
121         FILE * fd = NULL;
122         unsigned int lineNumber = 0;
123         char * name = NULL;
124         char * value = NULL;
125
126         if (stat(fileName, &statBuf)) {
127                 /* could not access the file */
128                 goto out;
129         }
130
131         if (!memcmp(&cachedStat.timeStamp, &statBuf.st_mtime, sizeof(cachedStat.timeStamp))) {
132                 /* file did not change since last read */
133                 goto out;
134         }
135
136         fd = fopen(fileName, "r");
137         if (!fd) {
138                 goto out;
139         }
140
141         nmea_zero_INFO(&result);
142         result.sig = POSFILE_DEFAULT_SIG;
143         result.fix = POSFILE_DEFAULT_FIX;
144         result.HDOP = POSFILE_DEFAULT_HDOP;
145         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
146         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
147         result.lat = POSFILE_DEFAULT_LAT;
148         result.lon = POSFILE_DEFAULT_LON;
149         result.elv = POSFILE_DEFAULT_ELV;
150         result.speed = POSFILE_DEFAULT_SPEED;
151         result.track = POSFILE_DEFAULT_TRACK;
152         result.mtrack = POSFILE_DEFAULT_MTRACK;
153         result.magvar = POSFILE_DEFAULT_MAGVAR;
154
155         memcpy(&cachedStat.timeStamp, &statBuf.st_mtime, sizeof(cachedStat.timeStamp));
156
157         while (fgets(line, LINE_LENGTH, fd)) {
158                 regmatch_t pmatch[regexNameValuematchCount];
159
160                 lineNumber++;
161
162                 if (regexMatch(&regexComment, line, 0, NULL)) {
163                         continue;
164                 }
165
166                 if (!regexMatch(&regexNameValue, line, regexNameValuematchCount, pmatch)) {
167                         pudError(false, "Position file \"%s\", line %d uses invalid syntax: %s", fileName, lineNumber, line);
168                         goto out;
169                 }
170
171                 /* determine name/value */
172                 name = &line[pmatch[1].rm_so];
173                 line[pmatch[1].rm_eo] = '\0';
174                 value = &line[pmatch[2].rm_so];
175                 line[pmatch[2].rm_eo] = '\0';
176
177                 if (!strncasecmp(POSFILE_NAME_SIG, name, sizeof(line))) {
178                         if (!strncasecmp(POSFILE_VALUE_SIG_BAD, value, sizeof(line))) {
179                                 result.sig = NMEA_SIG_BAD;
180                         } else if (!strncasecmp(POSFILE_VALUE_SIG_LOW, value, sizeof(line))) {
181                                 result.sig = NMEA_SIG_LOW;
182                         } else if (!strncasecmp(POSFILE_VALUE_SIG_MID, value, sizeof(line))) {
183                                 result.sig = NMEA_SIG_MID;
184                         } else if (!strncasecmp(POSFILE_VALUE_SIG_HIGH, value, sizeof(line))) {
185                                 result.sig = NMEA_SIG_HIGH;
186                         } else {
187                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
188                                                 " valid values are [%s|%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_SIG,
189                                                 POSFILE_VALUE_SIG_BAD, POSFILE_VALUE_SIG_LOW, POSFILE_VALUE_SIG_MID, POSFILE_VALUE_SIG_HIGH);
190                                 goto out;
191                         }
192                         nmea_INFO_set_present(&result.present, SIG);
193                 } else if (!strncasecmp(POSFILE_NAME_FIX, name, sizeof(line))) {
194                         if (!strncasecmp(POSFILE_VALUE_FIX_BAD, value, sizeof(line))) {
195                                 result.fix = NMEA_FIX_BAD;
196                         } else if (!strncasecmp(POSFILE_VALUE_FIX_2D, value, sizeof(line))) {
197                                 result.fix = NMEA_FIX_2D;
198                         } else if (!strncasecmp(POSFILE_VALUE_FIX_3D, value, sizeof(line))) {
199                                 result.fix = NMEA_FIX_3D;
200                         } else {
201                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
202                                                 " valid values are [%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_FIX, POSFILE_VALUE_FIX_BAD,
203                                                 POSFILE_VALUE_FIX_2D, POSFILE_VALUE_FIX_3D);
204                                 goto out;
205                         }
206                         nmea_INFO_set_present(&result.present, FIX);
207                 } else if (!strncasecmp(POSFILE_NAME_HDOP, name, sizeof(line))) {
208                         double val;
209                         if (!readDouble(POSFILE_NAME_HDOP, value, &val)) {
210                                 goto out;
211                         }
212
213                         result.HDOP = val;
214                         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
215                         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
216                         nmea_INFO_set_present(&result.present, HDOP);
217                         nmea_INFO_set_present(&result.present, VDOP);
218                         nmea_INFO_set_present(&result.present, PDOP);
219                 } else if (!strncasecmp(POSFILE_NAME_LAT, name, sizeof(line))) {
220                         double val;
221                         if (!readDouble(POSFILE_NAME_LAT, value, &val)) {
222                                 goto out;
223                         }
224
225                         result.lat = val;
226                         nmea_INFO_set_present(&result.present, LAT);
227                 } else if (!strncasecmp(POSFILE_NAME_LON, name, sizeof(line))) {
228                         double val;
229                         if (!readDouble(POSFILE_NAME_LON, value, &val)) {
230                                 goto out;
231                         }
232
233                         result.lon = val;
234                         nmea_INFO_set_present(&result.present, LON);
235                 } else if (!strncasecmp(POSFILE_NAME_ELV, name, sizeof(line))) {
236                         double val;
237                         if (!readDouble(POSFILE_NAME_ELV, value, &val)) {
238                                 goto out;
239                         }
240
241                         result.elv = val;
242                         nmea_INFO_set_present(&result.present, ELV);
243                 } else if (!strncasecmp(POSFILE_NAME_SPEED, name, sizeof(line))) {
244                         double val;
245                         if (!readDouble(POSFILE_NAME_SPEED, value, &val)) {
246                                 goto out;
247                         }
248
249                         result.speed = val;
250                         nmea_INFO_set_present(&result.present, SPEED);
251                 } else if (!strncasecmp(POSFILE_NAME_TRACK, name, sizeof(line))) {
252                         double val;
253                         if (!readDouble(POSFILE_NAME_TRACK, value, &val)) {
254                                 goto out;
255                         }
256
257                         result.track = val;
258                         nmea_INFO_set_present(&result.present, TRACK);
259                 } else if (!strncasecmp(POSFILE_NAME_MTRACK, name, sizeof(line))) {
260                         double val;
261                         if (!readDouble(POSFILE_NAME_MTRACK, value, &val)) {
262                                 goto out;
263                         }
264
265                         result.mtrack = val;
266                         nmea_INFO_set_present(&result.present, MTRACK);
267                 } else if (!strncasecmp(POSFILE_NAME_MAGVAR, name, sizeof(line))) {
268                         double val;
269                         if (!readDouble(POSFILE_NAME_MAGVAR, value, &val)) {
270                                 goto out;
271                         }
272
273                         result.magvar = val;
274                         nmea_INFO_set_present(&result.present, MAGVAR);
275                 } else {
276                         pudError(false, "Position file \"%s\", line %d uses an invalid option \"%s\","
277                                         " valid options are [%s|%s|%s|%s|%s|%s|%s|%s|%s|%s]", fileName, lineNumber, name, POSFILE_NAME_SIG,
278                                         POSFILE_NAME_FIX, POSFILE_NAME_HDOP, POSFILE_NAME_LAT, POSFILE_NAME_LON, POSFILE_NAME_ELV,
279                                         POSFILE_NAME_SPEED, POSFILE_NAME_TRACK, POSFILE_NAME_MTRACK, POSFILE_NAME_MAGVAR);
280                         goto out;
281                 }
282         }
283
284         fclose(fd);
285
286         result.smask = POSFILE_DEFAULT_SMASK;
287         nmea_INFO_set_present(&result.present, SMASK);
288
289         nmea_INFO_sanitise(&result);
290         nmea_INFO_unit_conversion(&result);
291
292         memcpy(nmeaInfo, &result, sizeof(result));
293         retval = true;
294
295         out: return retval;
296 }