1a952b8044121ce808850584294a5c05bb56fe2d
[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, int *ihp, 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         hp = (void *)(long)*ihp;
39
40         int i;
41         int j;
42         int klen;
43         int flen;
44         int slen;
45         int num_record = 0;
46         int num_field = 0;
47         int sane = 0;
48         int programname_alloced = 0;
49         int presscurve_alloced = 0;
50
51         int *field_index = 0;
52         const char *string_index = 0;
53
54         int ifilter;
55         unsigned char cfilter;
56
57         char read_buffer [MAXBUFFER];
58         char write_buffer [MAXBUFFER];
59
60         const char *newline = 0;
61         const char *comment = 0;
62         const char *keyword = 0;
63         const char *quote = 0;
64         const char *record = 0;
65         const char *field = 0;
66
67 /* Previously allocated memory for all the config record names and stylus
68    PressCurve must be released on subsequent reads of the file. At program
69    start the flag is 0 */
70
71         if (reread_config) {
72                 for (i = 0; i < num_list; i++, p++) {
73                         free(p->stylus1_presscurve);
74                         free(p->class_name);
75                 }
76                 /* Convert to long for x86_64 systems */
77                 p = (void *)(long)*ip;
78         }
79         reread_config = 1;
80         num_list = 0;
81
82 /* Begin by making sure that the Config File version number is present before
83    a record begins. We exit if it's lacking or the number doesn't match up.
84    Also exit if the line was too long to handle */
85
86 /* Since parsing of a file is a complex action, this comment is almost line by line:
87  1) Read until end of file or until the first program record is encountered.
88  2) No newline character means that the line extended beyond MAXBUFFER bytes - exit.
89  3) The configstring must reside to the _left_ of an eventual comment.
90  4) A pointer to the string is incremented by string length - to step past it.
91  5) Next the pointer is stepped past any following whitespace, landing at the value.
92  6) Whitespace and newline is computed away, and the value written to a null-terminated buffer.
93  7) Compare... */
94
95 /* Version loop */
96         while (((fgets(read_buffer, MAXBUFFER, fp)) != NULL) && ((strstr(read_buffer, "%%")) == NULL)) {
97                 if ((newline = (strchr(read_buffer, '\n'))) != NULL) {
98                         if ((keyword = (strstr(read_buffer, configstring))) != NULL) {
99                                 if (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < keyword)) {
100                                         keyword = 0;
101                                 }
102                                 if (keyword) {
103                                         keyword = (keyword+(strlen(configstring)));
104                                         keyword = (keyword+(strspn(keyword, " \t")));
105
106                                         flen = strcspn(keyword, " \t\n#");
107                                         strncpy(write_buffer, keyword, flen);
108                                         strncpy(write_buffer + flen, "\0", 1);
109
110                                         if (be_verbose) {
111                                                 fprintf(stderr, "%s-user = %s\n", configstring, write_buffer);
112                                                 fprintf(stderr, "%s-ours = %s\n", configstring, configversion);
113                                         }
114                                         if (*write_buffer != *configversion) {
115                                                 if (atoi(write_buffer) < 3) {
116                                                         return 3;       /* Config file version is too old to handle */
117                                                 }
118                                         }
119                                         userconfigversion = atoi(write_buffer);
120                                         if (userconfigversion == 3) {
121                                                 if (stylus1_info) {
122                                                         configfields = 2; /* Users with only a 'stylus' device */
123                                                 }
124                                                 if (pad1_info) {
125                                                         configfields = config3fields; /* Full 'pad' and 'stylus' */
126                                                 }
127                                                 configheaderfields = config3headerfields;
128                                         } else {
129                                                 return 3;               /* Add handling when next version exist! */
130                                         }
131                                         if (be_verbose) {
132                                                 fprintf(stderr, "ConfigHeaderFields = %d\n", configheaderfields);
133                                         }
134                                         break;                          /* Found a compatible config file */
135                                 }
136                         }
137                 } else {
138                         return 4;                                       /* Line was too long to parse */
139                 }
140         }
141 /* End version loop */
142         if (keyword == NULL) {
143                 return 3;                                               /* End of file or a record begins */
144         }
145
146 /* Read the config file in one go from top to bottom. Parse out the info
147    between one record separator "%%" and the next. Recognize field beginnings
148    by a keyword, for example HandleTouchStrips. Each full record is written
149    to a global memory structure, while partial records are discarded and
150    excessive fields are truncated. No sanity check on program names is
151    performed (except for the mandatory "default") and only a basic control
152    of the keycodes is in place - it filters out anything below 9 [Esc].
153    A global counter variable of full record instances is updated before the
154    function exits to reflect the new state. */
155
156 /* FIXME Doesn't discard duplicate keywords and is thus prone to become out of sync in such cases */
157
158 /* Fake that we've already read a line with a record separator, so that the while loop can begin */
159
160         sprintf(read_buffer, "%s", "%%\n");
161
162 /* Record loop */
163         while ((num_record < MAXRECORDS) && ((record = (strstr(read_buffer, "%%"))) != NULL)) {
164                 if ((newline = (strchr(read_buffer, '\n'))) != NULL) {
165                         if (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < record)) {
166                                 record = 0;
167                                 sprintf(read_buffer, "%s", "%%\n");     /* <-- Since no new reads from the file */
168                         }                                                                               /* is performed in this part of the loop, */
169                         if (record) {                                                   /* we break out by erasing an encountered */
170                                 num_field = 0;                      /* commented record separator. We must */
171                                 programname_alloced = 0;                                        /* write an actual separator here! */
172                                 presscurve_alloced = 0;
173
174 /* Field loop*/ while ((((fgets(read_buffer, MAXBUFFER, fp)) != NULL) && ((record = (strstr(read_buffer, "%%"))) == NULL))
175                                                 || (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < record))) {
176                                         if ((newline = (strchr(read_buffer, '\n'))) != NULL) {
177
178                                                 keyword = hp->h_class_name;
179
180 /* Keyword loop */              for (i = 0; i < configfields; i++) {
181
182                                                         klen = strlen(keyword);
183                                                         if ((field = (strstr(read_buffer, keyword))) != NULL) {
184                                                                 flen = strcspn(field, " \t");
185                                                                 if (flen == klen) {
186                                                                         if (((comment = (strchr(read_buffer, '#'))) != NULL) && (comment < field)) {
187                                                                                 field = 0;
188                                                                         }
189                                                                         if ((field) && (num_field < configfields)) {
190
191                                                                                 field = (field+flen);
192                                                                                 field = (field+(strspn(field, " \t\"")));
193                                                                                 if ((quote = (strchr(field, '\"'))) != NULL) {
194                                                                                         flen = (quote - field);
195                                                                                 } else {
196                                                                                         flen = strcspn(field, " \t\n#");
197                                                                                 }
198                                                                                 strncpy(write_buffer, field, flen);
199                                                                                 strncpy(write_buffer + flen, "\0", 1);
200 /* Begin basic sanity checks */
201                                                                                 ifilter = atoi(write_buffer);
202                                                                                 cfilter = *write_buffer;
203
204 /* Users of boolean 0 or 1 */                   if (keyword == hp->h_handle_touch) {
205                                                                                         if ((flen == 1) && (isdigit(cfilter))) {
206                                                                                                 if ((ifilter != 0) && (ifilter != 1)) {
207                                                                                                         sprintf(write_buffer, "%s", "0");
208                                                                                                         if (be_verbose) {
209                                                                                                                 fprintf(stderr, "Only boolean 1 or 0 allowed in %s field <-- RESETTING T0 ZERO\n", keyword);
210                                                                                                         }
211                                                                                                 }
212                                                                                         } else {
213                                                                                                 sprintf(write_buffer, "%s", "0");
214                                                                                                 if (be_verbose) {
215                                                                                                         fprintf(stderr, "Only boolean 1 or 0 allowed in %s field <-- RESETTING T0 ZERO\n", keyword);
216                                                                                                 }
217                                                                                         }
218 /* FIXME Only a basic control of the keycodes is in place - it filters out anything below 9 [Esc] and above 0 */
219                                                                                 } else {
220 /* Users not in this if-condition */            if ((keyword != hp->h_class_name)
221                                                                                                 && (keyword != hp->h_stylus1_presscurve)) {
222                                                                                                 if ((ifilter != 0) && (ifilter < 9)) {
223                                                                                                         sprintf(write_buffer, "%s", "0");
224                                                                                                         if (be_verbose) {
225                                                                                                                 fprintf(stderr, "Only a 9 [Esc] or above allowed in %s field <-- RESETTING T0 ZERO\n", keyword);
226                                                                                                         }
227                                                                                                 }
228                                                                                         }
229                                                                                 }
230 /* End basic sanity checks */
231                                                                                 if (keyword == hp->h_class_name) {
232 /* Memory allocation. Freed on reread */        if ((p->class_name = (char *)malloc(flen+1)) == NULL) {
233                                                                                                 return 2;
234                                                                                         }
235 /* Write the field to memory structure */       sprintf(p->class_name, "%s", write_buffer);
236                                                                                         num_record++; /* Only count record if program name is present */
237                                                                                         num_field++;
238                                                                                         programname_alloced = 1;
239                                                                                 } else if (keyword == hp->h_stylus1_presscurve) {
240 /* Memory allocation. Freed on reread */                if ((p->stylus1_presscurve = (char *)malloc(flen+1)) == NULL) {
241                                                                                                         return 2;
242                                                                                                 }
243 /* Write the field to memory structure */               sprintf(p->stylus1_presscurve, "%s", write_buffer);
244                                                                                                 num_field++;
245                                                                                                 presscurve_alloced = 1;
246                                                                                 } else {
247 /* Walking through members of a structure only using pointers, when the member name
248  in effect is unknown, still eludes me. This hack must suffice until the fog clears.
249  What I _do_ know is that the data structure begins with two char members, so
250  subtract them from the configfields number loop. They are already handled further
251  up in the code. The rest of the data structure consists of int members, where
252  a simple increment will walk correctly (while keeping in sync with the string
253  structure through string length+1). */
254                                                                                         field_index = &p->handle_touch;
255                                                                                         string_index = hp->h_handle_touch;
256                                                                                         for (j = 0; j < (configfields - 2); j++) {
257                                                                                                 slen = strlen(string_index);
258                                                                                                 if ((strcmp(string_index, keyword)) == 0) {
259 /* Write the field to memory structure */                       *field_index = atoi(write_buffer);
260                                                                                                         num_field++;
261                                                                                                         break;
262                                                                                                 }
263                                                                                                 field_index++;
264                                                                                                 string_index = string_index+slen+1;
265                                                                                         }
266                                                                                 }
267                                                                                 break;
268                                                                         }
269                                                                 }
270                                                         }
271                                                         keyword = keyword+klen+1; /* Steps to the next string member in the struct */
272                                                 }
273 /* End keyword loop */
274                                         } else {
275                                                 return 4;       /* Line was too long to parse */
276                                         }
277                                 }
278 /* End field loop */
279                         }
280                         if ((num_field < configfields) && (num_field > 0)) {
281                                 if (be_verbose) {
282                                         fprintf(stderr, "Check configfile! A record is incomplete or has a premature record separator\n");
283                                 }
284                                 if (presscurve_alloced) {
285                                         free(p->stylus1_presscurve);
286                                 }
287                                 if ((num_record) && (programname_alloced)) {
288                                         if (be_verbose) {
289                                                 fprintf(stderr, "PGR RECNAME = %s skipped! (too few fields or a misplaced record separator)\n", p->class_name);
290                                         }
291                                         num_record--;
292                                         free(p->class_name);
293                                 }
294                         } else {
295                                 if (num_field == configfields) {
296                                         if (be_verbose) {
297                                                 fprintf(stderr, "PGR RECNAME = %s\n", p->class_name);
298                                                 fprintf(stderr, "ST1 PRCURVE = \"%s\"\n", p->stylus1_presscurve);
299                                         }
300                                         p++;
301                                 }
302                         }
303                 } else {
304                         return 4;                                       /* Line was too long to parse */
305                 }
306         }
307 /* End record loop */
308         if (!num_record) {
309                 return 1;                                               /* Could not find a complete record */
310         }
311
312         sane = 0;
313         p = p - num_record;
314         for (i = 0; i < num_record; i++, p++) {
315                 if ((strcmp(p->class_name, "default")) == 0) {
316                         default_program = p;
317                         sane = 1;
318                         break;
319                 }
320         }
321         if (!sane) {
322                 return 5;                                               /* No "default" program record. Exit */
323         }
324
325         num_list = num_record;
326         if (be_verbose) {
327                 fprintf(stderr, "PGR RECORDS = %d\n", num_list);
328         }
329         return 0;                                                       /* Return without errors */
330 }
331
332 /* End Code */
333