6e422ac3fdaa3d6944e3a737e96bd90573c0e98b
[expresskeys.git] / src-expresskeys / config_read.c
1 /*
2  config_read.c -- Support ExpressKeys & Touch Strips on a Wacom Intuos3 tablet.
3
4  Copyright (C) 2005-2006 - Mats Johannesson
5
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21 #include "globals.h"
22
23 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
24  Function reads a configuration file containing program names and definitions
25  of which keys that should be mapped to pad buttons and touch strips. It takes
26  a file pointer and two pointers to global structures as input (data and
27  keywords). Returns nothing unless an error occured.
28  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
29
30 int read_file_config(int *ip, FILE *fp)
31 {
32
33         struct program *p;
34         struct configstrings *hp;
35
36         /* Convert to long for x86_64 systems */
37         p = (void *)(long)*ip;
38
39         if (is_graphire4) {
40                 hp = gr4_human_readable;
41         } else {
42                 hp = human_readable;
43         }
44
45         int i;
46         int j;
47         int klen;
48         int flen;
49         int slen;
50         int num_record = 0;
51         int num_field = 0;
52         int sane = 0;
53         int programname_alloced = 0;
54         int presscurve_alloced = 0;
55         int re_aligned = 0;
56
57         int *field_index = 0;
58         const char *string_index = 0;
59
60         int ifilter;
61         unsigned char cfilter;
62
63         char read_buffer [MAXBUFFER];
64         char write_buffer [MAXBUFFER];
65
66         const char *newline = 0;
67         const char *comment = 0;
68         const char *keyword = 0;
69         const char *quote = 0;
70         const char *record = 0;
71         const char *field = 0;
72
73 /* Previously allocated memory for all the config record names and stylus
74    PressCurve must be released on subsequent reads of the file. At program
75    start the flag is 0 */
76
77         if (reread_config) {
78                 for (i = 0; i < num_list; i++, p++) {
79                         free(p->stylus1_presscurve);
80                         free(p->class_name);
81                 }
82                 /* Convert to long for x86_64 systems */
83                 p = (void *)(long)*ip;
84         }
85         reread_config = 1;
86         num_list = 0;
87
88 /* Begin by making sure that the Config File version number is present before
89    a record begins. We exit if it's lacking or the number doesn't match up.
90    Also exit if the line was too long to handle */
91
92 /* Since parsing of a file is a complex action, this comment is almost line by line:
93  1) Read until end of file or until the first program record is encountered.
94  2) No newline character means that the line extended beyond MAXBUFFER bytes - exit.
95  3) The configstring must reside to the _left_ of an eventual comment.
96  4) A pointer to the string is incremented by string length - to step past it.
97  5) Next the pointer is stepped past any following whitespace, landing at the value.
98  6) Whitespace and newline is computed away, and the value written to a null-terminated buffer.
99  7) Compare... */
100
101 /* Version loop */
102         while (((fgets(read_buffer, MAXBUFFER, fp)) != NULL) && ((strstr(read_buffer, "%%")) == NULL)) {
103                 if ((newline = (strchr(read_buffer, '\n'))) != NULL) {
104                         if ((keyword = (strstr(read_buffer, configstring))) != NULL) {
105                                 if (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < keyword)) {
106                                         keyword = 0;
107                                 }
108                                 if (keyword) {
109                                         keyword = (keyword+(strlen(configstring)));
110                                         keyword = (keyword+(strspn(keyword, " \t")));
111
112                                         flen = strcspn(keyword, " \t\n#");
113                                         strncpy(write_buffer, keyword, flen);
114                                         strncpy(write_buffer + flen, "\0", 1);
115
116                                         if (be_verbose) {
117                                                 fprintf(stderr, "%s-user = %s\n", configstring, write_buffer);
118                                                 fprintf(stderr, "%s-ours = %s\n", configstring, configversion);
119                                         }
120                                         if (*write_buffer != *configversion) {
121                                                 if (atoi(write_buffer) < 3) {
122                                                         return 3;       /* Config file version is too old to handle */
123                                                 }
124                                         }
125                                         userconfigversion = atoi(write_buffer);
126                                         if (userconfigversion == 3) {
127                                                 if (stylus1_info) {
128                                                         configfields = 2; /* Users with only a 'stylus' device */
129                                                 }
130                                                 if (pad1_info) {
131                                                         configfields = config3fields; /* Full 'pad' and 'stylus' */
132                                                 }
133                                                 if (is_graphire4) {
134                                                         configfields = config3gr4fields; /* Tiny 'pad' and 'stylus' */
135                                                 }
136                                                 configheaderfields = config3headerfields;
137                                         } else {
138                                                 return 3;               /* Add handling when next version exist! */
139                                         }
140                                         if (be_verbose) {
141                                                 fprintf(stderr, "ConfigHeaderFields = %d\n", configheaderfields);
142                                         }
143                                         break;                          /* Found a compatible config file */
144                                 }
145                         }
146                 } else {
147                         return 4;                                       /* Line was too long to parse */
148                 }
149         }
150 /* End version loop */
151         if (keyword == NULL) {
152                 return 3;                                               /* End of file or a record begins */
153         }
154
155 /* Read the config file in one go from top to bottom. Parse out the info
156    between one record separator "%%" and the next. Recognize field beginnings
157    by a keyword, for example HandleTouchStrips. Each full record is written
158    to a global memory structure, while partial records are discarded and
159    excessive fields are truncated. No sanity check on program names is
160    performed (except for the mandatory "default") and only a basic control
161    of the keycodes is in place - it filters out anything below 9 [Esc].
162    A global counter variable of full record instances is updated before the
163    function exits to reflect the new state. */
164
165 /* FIXME Doesn't discard duplicate keywords and is thus prone to become out of sync in such cases */
166
167 /* Fake that we've already read a line with a record separator, so that the while loop can begin */
168
169         sprintf(read_buffer, "%s", "%%\n");
170
171 /* Record loop */
172         while ((num_record < MAXRECORDS) && ((record = (strstr(read_buffer, "%%"))) != NULL)) {
173                 if ((newline = (strchr(read_buffer, '\n'))) != NULL) {
174                         if (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < record)) {
175                                 record = 0;
176                                 sprintf(read_buffer, "%s", "%%\n");     /* <-- Since no new reads from the file */
177                         }                                                                               /* is performed in this part of the loop, */
178                         if (record) {                                                   /* we break out by erasing an encountered */
179                                 num_field = 0;                      /* commented record separator. We must */
180                                 programname_alloced = 0;                                        /* write an actual separator here! */
181                                 presscurve_alloced = 0;
182
183 /* Field loop*/ while ((((fgets(read_buffer, MAXBUFFER, fp)) != NULL) && ((record = (strstr(read_buffer, "%%"))) == NULL))
184                                                 || (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < record))) {
185                                         if ((newline = (strchr(read_buffer, '\n'))) != NULL) {
186
187                                                 keyword = hp->h_class_name;
188                                                 re_aligned = 0;
189
190 /* Keyword loop */              for (i = 0; i < configfields; i++) {
191
192                                                         klen = strlen(keyword);
193                                                         if ((field = (strstr(read_buffer, keyword))) != NULL) {
194                                                                 flen = strcspn(field, " \t");
195                                                                 if (flen == klen) {
196                                                                         if (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < field)) {
197                                                                                 field = 0;
198                                                                         }
199                                                                         if ((field) && (num_field < configfields)) {
200
201                                                                                 field = (field+flen);
202                                                                                 field = (field+(strspn(field, " \t\"")));
203                                                                                 if ((quote = (strchr(field, '\"'))) != NULL) {
204                                                                                         flen = (quote - field);
205                                                                                 } else {
206                                                                                         flen = strcspn(field, " \t\n#");
207                                                                                 }
208                                                                                 strncpy(write_buffer, field, flen);
209                                                                                 strncpy(write_buffer + flen, "\0", 1);
210 /* Begin basic sanity checks */
211                                                                                 ifilter = atoi(write_buffer);
212                                                                                 cfilter = *write_buffer;
213
214 /* Users of boolean 0 or 1 */                   if (keyword == hp->h_handle_touch) {
215                                                                                         if ((flen == 1) && (isdigit(cfilter))) {
216                                                                                                 if ((ifilter != 0) && (ifilter != 1)) {
217                                                                                                         sprintf(write_buffer, "%s", "0");
218                                                                                                         if (be_verbose) {
219                                                                                                                 fprintf(stderr, "Only boolean 1 or 0 allowed in %s field <-- RESETTING T0 ZERO\n", keyword);
220                                                                                                         }
221                                                                                                 }
222                                                                                         } else {
223                                                                                                 sprintf(write_buffer, "%s", "0");
224                                                                                                 if (be_verbose) {
225                                                                                                         fprintf(stderr, "Only boolean 1 or 0 allowed in %s field <-- RESETTING T0 ZERO\n", keyword);
226                                                                                                 }
227                                                                                         }
228 /* FIXME Only a basic control of the keycodes is in place - it filters out anything below 9 [Esc] and above 0 */
229                                                                                 } else {
230 /* Users not in this if-condition */            if ((keyword != hp->h_class_name)
231                                                                                                 && (keyword != hp->h_stylus1_presscurve)) {
232                                                                                                 if ((ifilter != 0) && (ifilter < 9)) {
233                                                                                                         sprintf(write_buffer, "%s", "0");
234                                                                                                         if (be_verbose) {
235                                                                                                                 fprintf(stderr, "Only a 9 [Esc] or above allowed in %s field <-- RESETTING T0 ZERO\n", keyword);
236                                                                                                         }
237                                                                                                 }
238                                                                                         }
239                                                                                 }
240 /* End basic sanity checks */
241                                                                                 if (keyword == hp->h_class_name) {
242 /* Memory allocation. Freed on reread */        if ((p->class_name = (char *)malloc(flen+1)) == NULL) {
243                                                                                                 return 2;
244                                                                                         }
245 /* Write the field to memory structure */       sprintf(p->class_name, "%s", write_buffer);
246                                                                                         num_record++; /* Only count record if program name is present */
247                                                                                         num_field++;
248                                                                                         programname_alloced = 1;
249                                                                                 } else if (keyword == hp->h_stylus1_presscurve) {
250 /* Memory allocation. Freed on reread */                if ((p->stylus1_presscurve = (char *)malloc(flen+1)) == NULL) {
251                                                                                                         return 2;
252                                                                                                 }
253 /* Write the field to memory structure */               sprintf(p->stylus1_presscurve, "%s", write_buffer);
254                                                                                                 num_field++;
255                                                                                                 presscurve_alloced = 1;
256                                                                                 } else {
257 /* Walking through members of a structure only using pointers, when the member name
258  in effect is unknown, still eludes me. This hack must suffice until the fog clears.
259  What I _do_ know is that the data structure begins with two char members, so
260  subtract them from the configfields number loop. They are already handled further
261  up in the code. The rest of the data structure consists of int members, where
262  a simple increment will walk correctly (while keeping in sync with the string
263  structure through string length+1). */
264                                                                                         field_index = &p->handle_touch;
265                                                                                         string_index = hp->h_handle_touch;
266                                                                                         for (j = 0; j <= (configfields - 2); j++) {
267                                                                                                 slen = strlen(string_index);
268                                                                                                 if ((strcmp(string_index, keyword)) == 0) {
269 /* Time for another ugly hack...*/                                      if (is_graphire4) {
270                                                                                                                 if ((strcmp(keyword, "LeftButton")) == 0) {
271                                                                                                                         field_index = &p->key_9;
272                                                                                                                 }
273                                                                                                                 if ((strcmp(keyword, "LeftButtonPlus")) == 0) {
274                                                                                                                         field_index = &p->key_9_plus;
275                                                                                                                 }
276                                                                                                                 if ((strcmp(keyword, "RightButton")) == 0) {
277                                                                                                                         field_index = &p->key_13;
278                                                                                                                 }
279                                                                                                                 if ((strcmp(keyword, "RightButtonPlus")) == 0) {
280                                                                                                                         field_index = &p->key_13_plus;
281                                                                                                                 }
282                                                                                                         }
283 /* Just until I understand!*/
284 /* Write the field to memory structure */                       *field_index = atoi(write_buffer);
285                                                                                                         num_field++;
286                                                                                                         break;
287                                                                                                 }
288                                                                                                 field_index++;
289                                                                                                 string_index = string_index+slen+1;
290                                                                                         }
291                                                                                 }
292                                                                                 break;
293                                                                         }
294                                                                 }
295                                                         }
296                                                         keyword = keyword+klen+1; /* Steps to the next string member in the struct */
297 /* This is such a HACK*/        if (!re_aligned) {
298 /* Because I don't know*/               if ((programname_alloced) && (presscurve_alloced)) {
299 /* How to traverse structs*/            keyword = hp->h_handle_touch;
300 /* Refactor after graphire4!*/          re_aligned = 1;
301                                                                 }
302                                                         }
303                                                 }
304 /* End keyword loop */
305                                         } else {
306                                                 return 4;       /* Line was too long to parse */
307                                         }
308                                 }
309 /* End field loop */
310                         }
311                         if ((num_field < configfields) && (num_field > 0)) {
312                                 if (be_verbose) {
313                                         fprintf(stderr, "Check configfile! A record is incomplete or has a premature record separator\n");
314                                 }
315                                 if (presscurve_alloced) {
316                                         free(p->stylus1_presscurve);
317                                 }
318                                 if ((num_record) && (programname_alloced)) {
319                                         if (be_verbose) {
320                                                 fprintf(stderr, "PGR RECNAME = %s skipped! (too few fields or a misplaced record separator)\n", p->class_name);
321                                         }
322                                         num_record--;
323                                         free(p->class_name);
324                                 }
325                         } else {
326                                 if (num_field == configfields) {
327                                         if (be_verbose) {
328                                                 fprintf(stderr, "PGR RECNAME = %s\n", p->class_name);
329                                                 fprintf(stderr, "ST1 PRCURVE = \"%s\"\n", p->stylus1_presscurve);
330                                         }
331                                         p++;
332                                 }
333                         }
334                 } else {
335                         return 4;                                       /* Line was too long to parse */
336                 }
337         }
338 /* End record loop */
339         if (!num_record) {
340                 return 1;                                               /* Could not find a complete record */
341         }
342
343         sane = 0;
344         p = p - num_record;
345         for (i = 0; i < num_record; i++, p++) {
346                 if ((strcmp(p->class_name, "default")) == 0) {
347                         default_program = p;
348                         sane = 1;
349                         break;
350                 }
351         }
352         if (!sane) {
353                 return 5;                                               /* No "default" program record. Exit */
354         }
355
356         num_list = num_record;
357         if (be_verbose) {
358                 fprintf(stderr, "PGR RECORDS = %d\n", num_list);
359         }
360         return 0;                                                       /* Return without errors */
361 }
362
363 /* End Code */
364