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