b5ec95f501493459c3837761657bcb318f67538d
[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 #define LINE_LENGTH 256
16
17 static const char * regexCommentString = "^([[:space:]]*|[[:space:]#]+.*)$";
18 static const char * regexNameValueString =
19                 "^[[:space:]]*([^[:space:]]+)[[:space:]]*=[[:space:]]*([^[:space:]]+)[[:space:]]*$";
20 static const size_t regexNameValuematchCount = 3;
21
22 static regex_t regexComment;
23 static regex_t regexNameValue;
24 static bool started = false;
25
26 typedef struct _CachedStat {
27         struct timespec st_mtim; /* Time of last modification.  */
28 } CachedStat;
29
30 static CachedStat cachedStat;
31
32
33 bool startPositionFile(void) {
34         if (started) {
35                 return true;
36         }
37
38         if (regcomp(&regexComment, regexCommentString, REG_EXTENDED)) {
39                 pudError(false, "Could not compile regex \"%s\"", regexCommentString);
40                 return false;
41         }
42
43         if (regcomp(&regexNameValue, regexNameValueString, REG_EXTENDED)) {
44                 pudError(false, "Could not compile regex \"%s\"", regexNameValueString);
45                 return false;
46         }
47
48         cachedStat.st_mtim.tv_sec = -1;
49         cachedStat.st_mtim.tv_nsec = -1;
50
51         started = true;
52         return true;
53 }
54
55 void stopPositionFile(void) {
56         if (started) {
57                 regfree(&regexNameValue);
58                 regfree(&regexComment);
59                 started = false;
60         }
61 }
62
63 static bool regexMatch(regex_t * regex, char * line, size_t nmatch, regmatch_t pmatch[]) {
64         int result = regexec(regex, line, nmatch, pmatch, 0);
65         if (!result) {
66                 return true;
67         }
68
69         if (result == REG_NOMATCH) {
70                 return false;
71         }
72
73         {
74                 char msgbuf[256];
75                 regerror(result, regex, msgbuf, sizeof(msgbuf));
76                 pudError(false, "Regex match failed: %s", msgbuf);
77         }
78
79         return false;
80 }
81
82 static char line[LINE_LENGTH];
83
84 bool readPositionFile(char * fileName, nmeaINFO * nmeaInfo) {
85         bool retval = false;
86         struct stat statBuf;
87         nmeaINFO result;
88         FILE * fd = NULL;
89         unsigned int lineNumber = 0;
90         char * name = NULL;
91         char * value = NULL;
92
93         if (stat(fileName, &statBuf)) {
94                 /* could not access the file */
95                 goto out;
96         }
97
98         if (!memcmp(&cachedStat.st_mtim, &statBuf.st_mtim, sizeof(cachedStat.st_mtim))) {
99                 /* file did not change since last read */
100                 goto out;
101         }
102
103         fd = fopen(fileName, "r");
104         if (!fd) {
105                 goto out;
106         }
107
108         nmea_zero_INFO(&result);
109         result.sig = POSFILE_DEFAULT_SIG;
110         result.fix = POSFILE_DEFAULT_FIX;
111         result.HDOP = POSFILE_DEFAULT_HDOP;
112         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
113         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
114         result.lat = POSFILE_DEFAULT_LAT;
115         result.lon = POSFILE_DEFAULT_LON;
116         result.elv = POSFILE_DEFAULT_ELV;
117         result.speed = POSFILE_DEFAULT_SPEED;
118         result.direction = POSFILE_DEFAULT_DIRECTION;
119
120         memcpy(&cachedStat.st_mtim, &statBuf.st_mtim, sizeof(cachedStat.st_mtim));
121
122         while (fgets(line, LINE_LENGTH, fd)) {
123                 regmatch_t pmatch[regexNameValuematchCount];
124
125                 lineNumber++;
126
127                 if (regexMatch(&regexComment, line, 0, NULL)) {
128                         continue;
129                 }
130
131                 if (!regexMatch(&regexNameValue, line, regexNameValuematchCount, pmatch)) {
132                         pudError(false, "Position file \"%s\", line %d uses invalid syntax: %s", fileName, lineNumber, line);
133                         goto out;
134                 }
135
136                 /* copy name/value */
137                 name = &line[pmatch[1].rm_so];
138                 line[pmatch[1].rm_eo] = '\0';
139                 value = &line[pmatch[2].rm_so];
140                 line[pmatch[2].rm_eo] = '\0';
141
142                 if (!strncasecmp(POSFILE_NAME_SIG, name, sizeof(line))) {
143                         if (!strncasecmp(POSFILE_VALUE_SIG_BAD, value, sizeof(line))) {
144                                 result.sig = NMEA_SIG_BAD;
145                         } else if (!strncasecmp(POSFILE_VALUE_SIG_LOW, value, sizeof(line))) {
146                                 result.sig = NMEA_SIG_LOW;
147                         } else if (!strncasecmp(POSFILE_VALUE_SIG_MID, value, sizeof(line))) {
148                                 result.sig = NMEA_SIG_MID;
149                         } else if (!strncasecmp(POSFILE_VALUE_SIG_HIGH, value, sizeof(line))) {
150                                 result.sig = NMEA_SIG_HIGH;
151                         } else {
152                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
153                                                 " valid values are [%s|%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_SIG,
154                                                 POSFILE_VALUE_SIG_BAD, POSFILE_VALUE_SIG_LOW, POSFILE_VALUE_SIG_MID, POSFILE_VALUE_SIG_HIGH);
155                                 goto out;
156                         }
157                 } else if (!strncasecmp(POSFILE_NAME_FIX, name, sizeof(line))) {
158                         if (!strncasecmp(POSFILE_VALUE_FIX_BAD, value, sizeof(line))) {
159                                 result.fix = NMEA_FIX_BAD;
160                         } else if (!strncasecmp(POSFILE_VALUE_FIX_2D, value, sizeof(line))) {
161                                 result.fix = NMEA_FIX_2D;
162                         } else if (!strncasecmp(POSFILE_VALUE_FIX_3D, value, sizeof(line))) {
163                                 result.fix = NMEA_FIX_3D;
164                         } else {
165                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
166                                                 " valid values are [%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_FIX, POSFILE_VALUE_FIX_BAD,
167                                                 POSFILE_VALUE_FIX_2D, POSFILE_VALUE_FIX_3D);
168                                 goto out;
169                         }
170                 } else if (!strncasecmp(POSFILE_NAME_HDOP, name, sizeof(line))) {
171                         double val;
172                         if (!readDouble(POSFILE_NAME_HDOP, value, &val)) {
173                                 goto out;
174                         }
175
176                         result.HDOP = val;
177                         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
178                         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
179                 } else if (!strncasecmp(POSFILE_NAME_LAT, name, sizeof(line))) {
180                         double val;
181                         if (!readDouble(POSFILE_NAME_LAT, value, &val)) {
182                                 goto out;
183                         }
184
185                         result.lat = val;
186                 } else if (!strncasecmp(POSFILE_NAME_LON, name, sizeof(line))) {
187                         double val;
188                         if (!readDouble(POSFILE_NAME_LON, value, &val)) {
189                                 goto out;
190                         }
191
192                         result.lon = val;
193                 } else if (!strncasecmp(POSFILE_NAME_ELV, name, sizeof(line))) {
194                         double val;
195                         if (!readDouble(POSFILE_NAME_ELV, value, &val)) {
196                                 goto out;
197                         }
198
199                         result.elv = val;
200                 } else if (!strncasecmp(POSFILE_NAME_SPEED, name, sizeof(line))) {
201                         double val;
202                         if (!readDouble(POSFILE_NAME_SPEED, value, &val)) {
203                                 goto out;
204                         }
205
206                         result.speed = val;
207                 } else if (!strncasecmp(POSFILE_NAME_DIRECTION, name, sizeof(line))) {
208                         double val;
209                         if (!readDouble(POSFILE_NAME_DIRECTION, value, &val)) {
210                                 goto out;
211                         }
212
213                         result.direction = val;
214                 } else {
215                         pudError(false, "Position file \"%s\", line %d uses an invalid option \"%s\","
216                                         " valid options are [%s|%s|%s|%s|%s|%s|%s|%s]", fileName, lineNumber, name, POSFILE_NAME_SIG,
217                                         POSFILE_NAME_FIX, POSFILE_NAME_HDOP, POSFILE_NAME_LAT, POSFILE_NAME_LON, POSFILE_NAME_ELV,
218                                         POSFILE_NAME_SPEED, POSFILE_NAME_DIRECTION);
219                         goto out;
220                 }
221         }
222
223         fclose(fd);
224
225         result.smask = POSFILE_SANITISE_SMASK;
226         nmea_INFO_sanitise(&result);
227         nmea_INFO_unit_conversion(&result);
228         if (result.fix == NMEA_FIX_BAD) {
229                 result.smask = 0;
230         } else {
231                 result.smask = POSFILE_DEFAULT_SMASK;
232         }
233
234         memcpy(nmeaInfo, &result, sizeof(result));
235         retval = true;
236
237         out: return retval;
238 }