4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "InputParser.hpp"
25 #include "InputConfig.hpp"
26 #include "InputKeys.hpp"
27 #include "InputLookup.hpp"
28 #include "IO/LineReader.hpp"
29 #include "Util/StringUtil.hpp"
30 #include "Util/StaticString.hpp"
31 #include "Util/EscapeBackslash.hpp"
32 #include "Util/NumberParser.hpp"
33 #include "LogFile.hpp"
40 parse_assignment(TCHAR
*buffer
, const TCHAR
*&key
, const TCHAR
*&value
)
42 TCHAR
*separator
= _tcschr(buffer
, '=');
43 if (separator
== NULL
|| separator
== buffer
)
46 *separator
= _T('\0');
49 value
= separator
+ 1;
55 unsigned event_id
, location
;
56 StaticString
<1024> mode
;
57 StaticString
<256> type
, data
, label
;
72 void commit(InputConfig
&config
, unsigned line
) {
79 token
= mode
.first_token(_T(" "));
81 // General errors - these should be true
82 assert(location
< 1024);
84 const TCHAR
*new_label
= NULL
;
85 while (token
!= NULL
) {
87 // All modes are valid at this point
88 int mode_id
= config
.MakeMode(token
);
92 // TODO code: Consider Reuse existing entries...
94 // Only copy this once per object - save string space
96 new_label
= UnescapeBackslash(label
);
99 config
.AppendMenu(mode_id
, new_label
, location
, event_id
);
102 // Make key (Keyboard input)
103 // key - Hardware key or keyboard
104 if (type
.equals(_T("key"))) {
105 // Get the int key (eg: APP1 vs 'a')
106 unsigned key
= ParseKeyCode(data
);
108 config
.Key2Event
[mode_id
][key
] = event_id
;
110 LogFormat(_T("Invalid key data: %s at %u"), data
.c_str(), line
);
112 // Make gce (Glide Computer Event)
113 // GCE - Glide Computer Event
114 } else if (type
.equals(_T("gce"))) {
115 // Get the int key (eg: APP1 vs 'a')
116 int key
= InputEvents::findGCE(data
);
118 config
.GC2Event
[key
] = event_id
;
120 LogFormat(_T("Invalid GCE data: %s at %u"), data
.c_str(), line
);
122 // Make gesture (Gesture Event)
124 } else if (type
.equals(_T("gesture"))) {
125 // Check data for invalid characters:
127 for (const TCHAR
* c
= data
; *c
; c
++)
135 // One entry per key: delete old, create new
136 config
.Gesture2Event
.Remove(data
.c_str());
137 config
.Gesture2Event
.Add(data
.c_str(), event_id
);
139 LogFormat(_T("Invalid gesture data: %s at %u"), data
.c_str(), line
);
141 // Make ne (NMEA Event)
143 } else if (type
.equals(_T("ne"))) {
144 // Get the int key (eg: APP1 vs 'a')
145 int key
= InputEvents::findNE(data
);
147 config
.N2Event
[key
] = event_id
;
149 LogFormat(_T("Invalid GCE data: %s at %u"), data
.c_str(), line
);
151 // label only - no key associated (label can still be touch screen)
152 } else if (type
.equals(_T("label"))) {
153 // Nothing to do here...
156 LogFormat(_T("Invalid type: %s at %u"), type
.c_str(), line
);
159 token
= mode
.next_token(_T(" "));
165 ParseInputFile(InputConfig
&config
, TLineReader
&reader
)
167 // TODO code - Safer sizes, strings etc - use C++ (can scanf restrict length?)
169 // Multiple modes (so large string)
170 EventBuilder current
;
175 // Read from the file
177 while ((buffer
= reader
.ReadLine()) != NULL
) {
181 const TCHAR
*key
, *value
;
183 // experimental: if the first line is "#CLEAR" then the whole default config is cleared
184 // and can be overwritten by file
185 if (line
== 1 && StringIsEqual(buffer
, _T("#CLEAR"))) {
186 config
.SetDefaults();
187 } else if (buffer
[0] == _T('\0')) {
188 // Check valid line? If not valid, assume next record (primative, but works ok!)
189 // General checks before continue...
190 current
.commit(config
, line
);
195 } else if (StringIsEmpty(buffer
) || buffer
[0] == _T('#')) {
196 // Do nothing - we probably just have a comment line
197 // NOTE: Do NOT display buffer to user as it may contain an invalid stirng !
199 } else if (parse_assignment(buffer
, key
, value
)) {
200 if (StringIsEqual(key
, _T("mode"))) {
201 current
.mode
= value
;
202 } else if (StringIsEqual(key
, _T("type"))) {
203 current
.type
= value
;
204 } else if (StringIsEqual(key
, _T("data"))) {
205 current
.data
= value
;
206 } else if (StringIsEqual(key
, _T("event"))) {
207 if (_tcslen(value
) < 256) {
208 TCHAR d_event
[256] = _T("");
209 TCHAR d_misc
[256] = _T("");
212 #if defined(__BORLANDC__)
213 memset(d_event
, 0, sizeof(d_event
));
214 memset(d_misc
, 0, sizeof(d_event
));
215 if (_tcschr(value
, ' ') == NULL
) {
216 _tcscpy(d_event
, value
);
220 ef
= _stscanf(value
, _T("%[^ ] %[A-Za-z0-9 \\/().,]"), d_event
,
223 #if defined(__BORLANDC__)
227 if ((ef
== 1) || (ef
== 2)) {
229 // TODO code: Consider reusing existing identical events
231 pt2Event event
= InputEvents::findEvent(d_event
);
233 TCHAR
*allocated
= UnescapeBackslash(d_misc
);
234 current
.event_id
= config
.AppendEvent(event
, allocated
,
237 /* not freeing the string, because
238 InputConfig::AppendEvent() stores the string point
239 without duplicating it; strictly speaking, this is a
240 memory leak, but the input file is only loaded once
241 at startup, so this is acceptable; in return, we
242 don't have to duplicate the hard-coded defaults,
243 which saves some memory */
247 LogFormat(_T("Invalid event type: %s at %i"), d_event
, line
);
250 LogFormat("Invalid event type at %i", line
);
253 } else if (StringIsEqual(key
, _T("label"))) {
254 current
.label
= value
;
255 } else if (StringIsEqual(key
, _T("location"))) {
256 current
.location
= ParseUnsigned(value
);
259 LogFormat(_T("Invalid key/value pair %s=%s at %i"), key
, value
, line
);
262 LogFormat("Invalid line at %i", line
);
267 current
.commit(config
, line
);