missing header
[wmaker-crm.git] / src / proplist.c
blob49d44165eee30b2003c789d073948e8f1743f2d0
1 /* proplist.c- Hand made proplist parser.
2 * The one in libPropList causes wmaker to crash if an error is found in
3 * the parsed file. This parser is also more rigid: it will not accept any
4 * property lists with errors, but will print more descriptive error messages
5 * and will hopefully not crash.
7 * Window Maker window manager
8 *
9 * Copyright (c) 1998 Alfredo K. Kojima
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
24 * USA.
27 #include "wconfig.h"
28 #include "WindowMaker.h"
30 #include <proplist.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <ctype.h>
37 #if 0
38 #define DPUT(s) puts(s)
39 #else
40 #define DPUT(s)
41 #endif
44 #define INITIAL_BUFFER_SIZE (16*1024)
46 #define BUFFER_SIZE_INCREMENT 1024
49 static int line_number = 1;
50 static int buffer_size = 0;
51 static char *buffer = NULL;
52 static char *file_name;
55 static proplist_t get_object(FILE *f);
56 static proplist_t get_array(FILE *f);
57 static proplist_t get_string(FILE *f);
58 static proplist_t get_qstring(FILE *f);
59 static proplist_t get_dictionary(FILE *f);
62 static INLINE int
63 get_char(FILE *f)
65 int c;
67 c = fgetc(f);
68 if (c=='\n')
69 line_number++;
70 return c;
75 static INLINE int
76 get_non_space_char(FILE *f)
78 int c;
80 while (1) {
81 c = fgetc(f);
82 if (c=='\n')
83 line_number++;
84 else if (!isspace(c))
85 break;
88 if (c!=EOF) {
89 return c;
90 } else {
91 return EOF;
96 static char *
97 unescapestr(char *src)
99 char *dest = wmalloc(strlen(src)+1);
100 char *src_ptr, *dest_ptr;
101 char ch;
104 for (src_ptr=src, dest_ptr=dest; *src_ptr; src_ptr++, dest_ptr++)
106 if(*src_ptr != '\\')
107 *dest_ptr = *src_ptr;
108 else
110 ch = *(++src_ptr);
111 if((ch>='0') && (ch<='3')) /* assume next 2 chars are octal too */
113 *dest_ptr = ((ch & 07) << 6);
114 *dest_ptr |= ((*(++src_ptr)&07)<<3);
115 *dest_ptr |= *(++src_ptr)&07;
117 else
119 switch(ch)
121 case 'a' : *dest_ptr = '\a'; break;
122 case 'b' : *dest_ptr = '\b'; break;
123 case 't' : *dest_ptr = '\t'; break;
124 case 'r' : *dest_ptr = '\r'; break;
125 case 'n' : *dest_ptr = '\n'; break;
126 case 'v' : *dest_ptr = '\v'; break;
127 case 'f' : *dest_ptr = '\f'; break;
128 default : *dest_ptr = *src_ptr;
133 *dest_ptr = 0;
135 return dest;
139 #define CHECK_BUFFER_SIZE(ptr) \
140 if ((ptr) >= buffer_size-1) {\
141 buffer_size += BUFFER_SIZE_INCREMENT;\
142 buffer = wrealloc(buffer, buffer_size);\
146 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
147 || (c)=='+')
151 #define COMPLAIN(msg) wwarning(_("syntax error in %s, line %i:%s"), \
152 file_name, line_number, msg)
155 static proplist_t
156 get_qstring(FILE *f)
158 int c;
159 int ptr = 0;
160 int escaping = 0;
161 int ok = 1;
163 while (1) {
164 c = get_char(f);
165 if (!escaping) {
166 if (c=='\\') {
167 escaping = 1;
168 continue;
170 if (c=='"')
171 break;
172 } else {
173 CHECK_BUFFER_SIZE(ptr);
174 buffer[ptr++] = '\\';
175 escaping = 0;
177 if (c==EOF) {
178 ptr--;
179 ok = 0;
180 COMPLAIN(_("unterminated string"));
181 break;
182 } else {
183 CHECK_BUFFER_SIZE(ptr);
184 buffer[ptr++] = c;
188 buffer[ptr] = 0;
190 if (!ok)
191 return NULL;
192 else {
193 char *tmp = unescapestr(buffer);
194 proplist_t pl = PLMakeString(tmp);
195 wfree(tmp);
196 return pl;
202 static proplist_t
203 get_string(FILE *f)
205 int c;
206 int ptr = 0;
208 while (1) {
209 c = get_char(f);
210 if (ISSTRINGABLE(c)) {
211 CHECK_BUFFER_SIZE(ptr);
212 buffer[ptr++] = c;
213 } else {
214 if (c!=EOF) {
215 ungetc(c, f);
217 break;
220 buffer[ptr] = 0;
222 if (ptr==0)
223 return NULL;
224 else {
225 char *tmp = unescapestr(buffer);
226 proplist_t pl = PLMakeString(tmp);
227 wfree(tmp);
228 return pl;
235 static proplist_t
236 get_array(FILE *f)
238 int c;
239 int ok=1, first=1;
240 proplist_t list, obj;
242 list = PLMakeArrayFromElements(NULL);
244 while (1) {
245 c = get_non_space_char(f);
246 if (c==EOF) {
247 COMPLAIN(_("unterminated array"));
248 ok = 0;
249 break;
250 } else if (c==')') {
251 break;
252 } else if (c==',') {
253 /* continue normally */
254 } else {
255 if (!first) {
256 COMPLAIN(_("missing , in array or unterminated array"));
257 ok = 0;
258 break;
259 } else {
260 ungetc(c, f);
263 first = 0;
264 /* get the data */
265 obj = get_object(f);
266 if (!obj) {
267 COMPLAIN(_("could not get array element"));
268 ok = 0;
269 break;
271 list = PLAppendArrayElement(list, obj);
272 PLRelease(obj);
275 if (ok)
276 return list;
277 else {
278 PLRelease(list);
279 return NULL;
284 static proplist_t
285 get_dictionary(FILE *f)
287 int c;
288 int ok = 1;
289 proplist_t dict, key, value;
291 dict = PLMakeDictionaryFromEntries(NULL, NULL);
293 while (1) {
294 c = get_non_space_char(f);
296 if (c==EOF) {
297 COMPLAIN(_("unterminated dictionary"));
298 ok = 0;
299 break;
300 } else if (c=='}') {
301 break;
304 /* get the entry */
306 /* get key */
307 DPUT("getting dict key");
308 if (c=='"')
309 key = get_qstring(f);
310 else if (ISSTRINGABLE(c)) {
311 ungetc(c, f);
312 key = get_string(f);
313 } else {
314 if (c=='=')
315 COMPLAIN(_("missing dictionary key"));
316 else
317 COMPLAIN(_("missing dictionary entry key or unterminated dictionary"));
318 ok = 0;
319 break;
322 if (!key) {
323 COMPLAIN(_("error parsing dictionary key"));
324 ok = 0;
325 break;
327 DPUT("getting =");
328 /* get = */
329 c = get_non_space_char(f);
330 if (c!='=') {
331 PLRelease(key);
332 COMPLAIN(_("missing = in dictionary entry"));
333 ok = 0;
334 break;
336 DPUT("getting dict entry data");
337 /* get data */
338 value = get_object(f);
339 if (!value) {
341 COMPLAIN(_("error parsing dictionary entry value"));
343 ok = 0;
344 PLRelease(key);
345 break;
347 DPUT("getting ;");
348 /* get ; */
349 c = get_non_space_char(f);
350 if (c!=';') {
351 COMPLAIN(_("missing ; in dictionary entry"));
352 ok = 0;
353 PLRelease(key);
354 PLRelease(value);
355 break;
357 dict = PLInsertDictionaryEntry(dict, key, value);
358 PLRelease(key);
359 PLRelease(value);
362 if (!ok) {
363 PLRelease(dict);
364 return NULL;
365 } else {
366 return dict;
371 static proplist_t
372 get_data(FILE *f)
376 COMPLAIN("the data datatype is not yet implemented");
378 return NULL;
385 static proplist_t
386 get_object(FILE *f)
388 int c;
389 proplist_t pl;
391 c = get_non_space_char(f);
393 switch (c) {
394 /* END OF FILE */
395 case EOF:
396 DPUT("EOF");
397 pl = NULL;
398 break;
400 /* dictionary */
401 case '{':
402 DPUT("getting dictionary");
403 pl = get_dictionary(f);
404 break;
406 /* array */
407 case '(':
408 DPUT("getting arrray");
409 pl = get_array(f);
410 break;
412 /* data */
413 case '<':
414 DPUT("getting data");
415 pl = get_data(f);
416 break;
418 /* quoted string */
419 case '"':
420 DPUT("getting qstring");
421 pl = get_qstring(f);
422 break;
424 /* string */
425 default:
426 if (ISSTRINGABLE(c)) {
427 DPUT("getting string");
428 /* put back */
429 ungetc(c, f);
430 pl = get_string(f);
431 } else {
432 COMPLAIN(_("was expecting a string, dictionary, data or array. If it's a string, try enclosing it with \"."));
433 if (c=='#' || c=='/') {
434 wwarning(_("Comments are not allowed inside WindowMaker owned domain files."));
436 pl = NULL;
438 break;
441 return pl;
445 proplist_t
446 ReadProplistFromFile(char *file)
448 FILE *f;
449 proplist_t pl = NULL;
451 f = fopen(file, "r");
452 if (!f) {
453 wsyserror(_("could not open domain file %s"), file);
454 return NULL;
457 file_name = file;
458 line_number = 1;
459 buffer_size = INITIAL_BUFFER_SIZE;
460 buffer = wmalloc(buffer_size);
462 pl = get_object(f);
464 /* check for illegal characters after EOF */
465 if (get_non_space_char(f)!=EOF && pl) {
466 COMPLAIN(_("extra data after end of file"));
468 * We can't just ignore garbage after the file because the "garbage"
469 * could be the data and the real garbage be in the beginning of
470 * the file (wich is now, inside pl)
472 PLRelease(pl);
473 pl = NULL;
476 wfree(buffer);
478 fclose(f);
480 if (pl) {
481 proplist_t fpl;
483 fpl = PLMakeString(file);
484 PLSetFilename(pl, fpl);
485 PLRelease(fpl);
487 return pl;