pud: minor fix in the function reading the position file
[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
130         char * name = NULL;
131         char * value = NULL;
132
133         nmeaINFO result;
134         bool retval = false;
135
136         fd = open(fileName, O_RDONLY);
137         if (fd < 0) {
138                 /* could not open the file */
139                 memset(&cachedStat.timeStamp, 0, sizeof(cachedStat.timeStamp));
140                 goto out;
141         }
142
143         if (fstat(fd, &statBuf)) {
144                 /* could not stat the file */
145                 memset(&cachedStat.timeStamp, 0, sizeof(cachedStat.timeStamp));
146                 goto out;
147         }
148
149 #if defined(__linux__) && !defined(__ANDROID__)
150         mtim = &statBuf.st_mtim;
151 #else
152         mtim = &statBuf.st_mtime;
153 #endif
154
155         if (!memcmp(&cachedStat.timeStamp, mtim, sizeof(cachedStat.timeStamp))) {
156                 /* file did not change since last read */
157                 goto out;
158         }
159
160         fp = fdopen(fd, "r");
161         if (!fp) {
162                 /* could not open the file */
163                 goto out;
164         }
165
166         nmea_zero_INFO(&result);
167         result.sig = POSFILE_DEFAULT_SIG;
168         result.fix = POSFILE_DEFAULT_FIX;
169         result.HDOP = POSFILE_DEFAULT_HDOP;
170         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
171         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
172         result.lat = POSFILE_DEFAULT_LAT;
173         result.lon = POSFILE_DEFAULT_LON;
174         result.elv = POSFILE_DEFAULT_ELV;
175         result.speed = POSFILE_DEFAULT_SPEED;
176         result.track = POSFILE_DEFAULT_TRACK;
177         result.mtrack = POSFILE_DEFAULT_MTRACK;
178         result.magvar = POSFILE_DEFAULT_MAGVAR;
179
180         memcpy(&cachedStat.timeStamp, &statBuf.st_mtime, sizeof(cachedStat.timeStamp));
181
182         while (fgets(line, LINE_LENGTH, fp)) {
183                 regmatch_t pmatch[regexNameValuematchCount];
184
185                 lineNumber++;
186
187                 if (regexMatch(&regexComment, line, 0, NULL)) {
188                         continue;
189                 }
190
191                 if (!regexMatch(&regexNameValue, line, regexNameValuematchCount, pmatch)) {
192                         pudError(false, "Position file \"%s\", line %d uses invalid syntax: %s", fileName, lineNumber, line);
193                         goto out;
194                 }
195
196                 /* determine name/value */
197                 name = &line[pmatch[1].rm_so];
198                 line[pmatch[1].rm_eo] = '\0';
199                 value = &line[pmatch[2].rm_so];
200                 line[pmatch[2].rm_eo] = '\0';
201
202                 if (!strncasecmp(POSFILE_NAME_SIG, name, sizeof(line))) {
203                         if (!strncasecmp(POSFILE_VALUE_SIG_BAD, value, sizeof(line))) {
204                                 result.sig = NMEA_SIG_BAD;
205                         } else if (!strncasecmp(POSFILE_VALUE_SIG_LOW, value, sizeof(line))) {
206                                 result.sig = NMEA_SIG_LOW;
207                         } else if (!strncasecmp(POSFILE_VALUE_SIG_MID, value, sizeof(line))) {
208                                 result.sig = NMEA_SIG_MID;
209                         } else if (!strncasecmp(POSFILE_VALUE_SIG_HIGH, value, sizeof(line))) {
210                                 result.sig = NMEA_SIG_HIGH;
211                         } else {
212                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
213                                                 " valid values are [%s|%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_SIG,
214                                                 POSFILE_VALUE_SIG_BAD, POSFILE_VALUE_SIG_LOW, POSFILE_VALUE_SIG_MID, POSFILE_VALUE_SIG_HIGH);
215                                 goto out;
216                         }
217                         nmea_INFO_set_present(&result.present, SIG);
218                 } else if (!strncasecmp(POSFILE_NAME_FIX, name, sizeof(line))) {
219                         if (!strncasecmp(POSFILE_VALUE_FIX_BAD, value, sizeof(line))) {
220                                 result.fix = NMEA_FIX_BAD;
221                         } else if (!strncasecmp(POSFILE_VALUE_FIX_2D, value, sizeof(line))) {
222                                 result.fix = NMEA_FIX_2D;
223                         } else if (!strncasecmp(POSFILE_VALUE_FIX_3D, value, sizeof(line))) {
224                                 result.fix = NMEA_FIX_3D;
225                         } else {
226                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
227                                                 " valid values are [%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_FIX, POSFILE_VALUE_FIX_BAD,
228                                                 POSFILE_VALUE_FIX_2D, POSFILE_VALUE_FIX_3D);
229                                 goto out;
230                         }
231                         nmea_INFO_set_present(&result.present, FIX);
232                 } else if (!strncasecmp(POSFILE_NAME_HDOP, name, sizeof(line))) {
233                         double val;
234                         if (!readDouble(POSFILE_NAME_HDOP, value, &val)) {
235                                 goto out;
236                         }
237
238                         result.HDOP = val;
239                         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
240                         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
241                         nmea_INFO_set_present(&result.present, HDOP);
242                         nmea_INFO_set_present(&result.present, VDOP);
243                         nmea_INFO_set_present(&result.present, PDOP);
244                 } else if (!strncasecmp(POSFILE_NAME_LAT, name, sizeof(line))) {
245                         double val;
246                         if (!readDouble(POSFILE_NAME_LAT, value, &val)) {
247                                 goto out;
248                         }
249
250                         result.lat = val;
251                         nmea_INFO_set_present(&result.present, LAT);
252                 } else if (!strncasecmp(POSFILE_NAME_LON, name, sizeof(line))) {
253                         double val;
254                         if (!readDouble(POSFILE_NAME_LON, value, &val)) {
255                                 goto out;
256                         }
257
258                         result.lon = val;
259                         nmea_INFO_set_present(&result.present, LON);
260                 } else if (!strncasecmp(POSFILE_NAME_ELV, name, sizeof(line))) {
261                         double val;
262                         if (!readDouble(POSFILE_NAME_ELV, value, &val)) {
263                                 goto out;
264                         }
265
266                         result.elv = val;
267                         nmea_INFO_set_present(&result.present, ELV);
268                 } else if (!strncasecmp(POSFILE_NAME_SPEED, name, sizeof(line))) {
269                         double val;
270                         if (!readDouble(POSFILE_NAME_SPEED, value, &val)) {
271                                 goto out;
272                         }
273
274                         result.speed = val;
275                         nmea_INFO_set_present(&result.present, SPEED);
276                 } else if (!strncasecmp(POSFILE_NAME_TRACK, name, sizeof(line))) {
277                         double val;
278                         if (!readDouble(POSFILE_NAME_TRACK, value, &val)) {
279                                 goto out;
280                         }
281
282                         result.track = val;
283                         nmea_INFO_set_present(&result.present, TRACK);
284                 } else if (!strncasecmp(POSFILE_NAME_MTRACK, name, sizeof(line))) {
285                         double val;
286                         if (!readDouble(POSFILE_NAME_MTRACK, value, &val)) {
287                                 goto out;
288                         }
289
290                         result.mtrack = val;
291                         nmea_INFO_set_present(&result.present, MTRACK);
292                 } else if (!strncasecmp(POSFILE_NAME_MAGVAR, name, sizeof(line))) {
293                         double val;
294                         if (!readDouble(POSFILE_NAME_MAGVAR, value, &val)) {
295                                 goto out;
296                         }
297
298                         result.magvar = val;
299                         nmea_INFO_set_present(&result.present, MAGVAR);
300                 } else {
301                         pudError(false, "Position file \"%s\", line %d uses an invalid option \"%s\","
302                                         " valid options are [%s|%s|%s|%s|%s|%s|%s|%s|%s|%s]", fileName, lineNumber, name, POSFILE_NAME_SIG,
303                                         POSFILE_NAME_FIX, POSFILE_NAME_HDOP, POSFILE_NAME_LAT, POSFILE_NAME_LON, POSFILE_NAME_ELV,
304                                         POSFILE_NAME_SPEED, POSFILE_NAME_TRACK, POSFILE_NAME_MTRACK, POSFILE_NAME_MAGVAR);
305                         goto out;
306                 }
307         }
308
309         fclose(fp);
310         fp = NULL;
311
312         result.smask = POSFILE_DEFAULT_SMASK;
313         nmea_INFO_set_present(&result.present, SMASK);
314
315         nmea_INFO_sanitise(&result);
316         nmea_INFO_unit_conversion(&result);
317
318         memcpy(nmeaInfo, &result, sizeof(result));
319         retval = true;
320
321         out: if (fp) {
322                 fclose(fp);
323         }
324         if (fd >= 0) {
325                 close(fd);
326         }
327         return retval;
328 }