1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-20 12:58:08 +01:00
Files
wmaker/src/proplist.c

492 lines
8.8 KiB
C

/* proplist.c- Hand made proplist parser.
* The one in libPropList causes wmaker to crash if an error is found in
* the parsed file. This parser is also more rigid: it will not accept any
* property lists with errors, but will print more descriptive error messages
* and will hopefully not crash.
*
* Window Maker window manager
*
* Copyright (c) 1998 Alfredo K. Kojima
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include "wconfig.h"
#include "WindowMaker.h"
#include <proplist.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#if 0
#define DPUT(s) puts(s)
#else
#define DPUT(s)
#endif
#define INITIAL_BUFFER_SIZE (16*1024)
#define BUFFER_SIZE_INCREMENT 1024
static int line_number = 1;
static int buffer_size = 0;
static char *buffer = NULL;
static char *file_name;
static proplist_t get_object(FILE *f);
static proplist_t get_array(FILE *f);
static proplist_t get_string(FILE *f);
static proplist_t get_qstring(FILE *f);
static proplist_t get_dictionary(FILE *f);
static INLINE int
get_char(FILE *f)
{
int c;
c = fgetc(f);
if (c=='\n')
line_number++;
return c;
}
static INLINE int
get_non_space_char(FILE *f)
{
int c;
while (1) {
c = fgetc(f);
if (c=='\n')
line_number++;
else if (!isspace(c))
break;
}
if (c!=EOF) {
return c;
} else {
return EOF;
}
}
static char *
unescapestr(char *src)
{
char *dest = wmalloc(strlen(src)+1);
char *src_ptr, *dest_ptr;
char ch;
for (src_ptr=src, dest_ptr=dest; *src_ptr; src_ptr++, dest_ptr++)
{
if(*src_ptr != '\\')
*dest_ptr = *src_ptr;
else
{
ch = *(++src_ptr);
if((ch>='0') && (ch<='3')) /* assume next 2 chars are octal too */
{
*dest_ptr = ((ch & 07) << 6);
*dest_ptr |= ((*(++src_ptr)&07)<<3);
*dest_ptr |= *(++src_ptr)&07;
}
else
{
switch(ch)
{
case 'a' : *dest_ptr = '\a'; break;
case 'b' : *dest_ptr = '\b'; break;
case 't' : *dest_ptr = '\t'; break;
case 'r' : *dest_ptr = '\r'; break;
case 'n' : *dest_ptr = '\n'; break;
case 'v' : *dest_ptr = '\v'; break;
case 'f' : *dest_ptr = '\f'; break;
default : *dest_ptr = *src_ptr;
}
}
}
}
*dest_ptr = 0;
return dest;
}
#define CHECK_BUFFER_SIZE(ptr) \
if ((ptr) >= buffer_size-1) {\
buffer_size += BUFFER_SIZE_INCREMENT;\
buffer = wrealloc(buffer, buffer_size);\
}
#define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
|| (c)=='+')
#define COMPLAIN(msg) wwarning(_("syntax error in %s, line %i:%s"), \
file_name, line_number, msg)
static proplist_t
get_qstring(FILE *f)
{
int c;
int ptr = 0;
int escaping = 0;
int ok = 1;
while (1) {
c = get_char(f);
if (!escaping) {
if (c=='\\') {
escaping = 1;
continue;
}
if (c=='"')
break;
} else {
CHECK_BUFFER_SIZE(ptr);
buffer[ptr++] = '\\';
escaping = 0;
}
if (c==EOF) {
ptr--;
ok = 0;
COMPLAIN(_("unterminated string"));
break;
} else {
CHECK_BUFFER_SIZE(ptr);
buffer[ptr++] = c;
}
}
buffer[ptr] = 0;
if (!ok)
return NULL;
else {
char *tmp = unescapestr(buffer);
proplist_t pl = PLMakeString(tmp);
free(tmp);
return pl;
}
}
static proplist_t
get_string(FILE *f)
{
int c;
int ptr = 0;
while (1) {
c = get_char(f);
if (ISSTRINGABLE(c)) {
CHECK_BUFFER_SIZE(ptr);
buffer[ptr++] = c;
} else {
if (c!=EOF) {
ungetc(c, f);
}
break;
}
}
buffer[ptr] = 0;
if (ptr==0)
return NULL;
else {
char *tmp = unescapestr(buffer);
proplist_t pl = PLMakeString(tmp);
free(tmp);
return pl;
}
}
static proplist_t
get_array(FILE *f)
{
int c;
int ok=1, first=1;
proplist_t list, obj;
list = PLMakeArrayFromElements(NULL);
while (1) {
c = get_non_space_char(f);
if (c==EOF) {
COMPLAIN(_("unterminated array"));
ok = 0;
break;
} else if (c==')') {
break;
} else if (c==',') {
/* continue normally */
} else {
if (!first) {
COMPLAIN(_("missing , in array or unterminated array"));
ok = 0;
break;
} else {
ungetc(c, f);
}
}
first = 0;
/* get the data */
obj = get_object(f);
if (!obj) {
COMPLAIN(_("could not get array element"));
ok = 0;
break;
}
list = PLAppendArrayElement(list, obj);
PLRelease(obj);
}
if (ok)
return list;
else {
PLRelease(list);
return NULL;
}
}
static proplist_t
get_dictionary(FILE *f)
{
int c;
int ok = 1;
proplist_t dict, key, value;
dict = PLMakeDictionaryFromEntries(NULL, NULL);
while (1) {
c = get_non_space_char(f);
if (c==EOF) {
COMPLAIN(_("unterminated dictionary"));
ok = 0;
break;
} else if (c=='}') {
break;
}
/* get the entry */
/* get key */
DPUT("getting dict key");
if (c=='"')
key = get_qstring(f);
else if (ISSTRINGABLE(c)) {
ungetc(c, f);
key = get_string(f);
} else {
if (c=='=')
COMPLAIN(_("missing dictionary key"));
else
COMPLAIN(_("missing dictionary entry key or unterminated dictionary"));
ok = 0;
break;
}
if (!key) {
COMPLAIN(_("error parsing dictionary key"));
ok = 0;
break;
}
DPUT("getting =");
/* get = */
c = get_non_space_char(f);
if (c!='=') {
PLRelease(key);
COMPLAIN(_("missing = in dictionary entry"));
ok = 0;
break;
}
DPUT("getting dict entry data");
/* get data */
value = get_object(f);
if (!value) {
/*
COMPLAIN(_("error parsing dictionary entry value"));
*/
ok = 0;
PLRelease(key);
break;
}
DPUT("getting ;");
/* get ; */
c = get_non_space_char(f);
if (c!=';') {
COMPLAIN(_("missing ; in dictionary entry"));
ok = 0;
PLRelease(key);
PLRelease(value);
break;
}
dict = PLInsertDictionaryEntry(dict, key, value);
PLRelease(key);
PLRelease(value);
}
if (!ok) {
PLRelease(dict);
return NULL;
} else {
return dict;
}
}
static proplist_t
get_data(FILE *f)
{
COMPLAIN("the data datatype is not yet implemented");
return NULL;
}
static proplist_t
get_object(FILE *f)
{
int c;
proplist_t pl;
c = get_non_space_char(f);
switch (c) {
/* END OF FILE */
case EOF:
DPUT("EOF");
pl = NULL;
break;
/* dictionary */
case '{':
DPUT("getting dictionary");
pl = get_dictionary(f);
break;
/* array */
case '(':
DPUT("getting arrray");
pl = get_array(f);
break;
/* data */
case '<':
DPUT("getting data");
pl = get_data(f);
break;
/* quoted string */
case '"':
DPUT("getting qstring");
pl = get_qstring(f);
break;
/* string */
default:
if (ISSTRINGABLE(c)) {
DPUT("getting string");
/* put back */
ungetc(c, f);
pl = get_string(f);
} else {
COMPLAIN(_("was expecting a string, dictionary, data or array. If it's a string, try enclosing it with \"."));
if (c=='#' || c=='/') {
wwarning(_("Comments are not allowed inside WindowMaker owned domain files."));
}
pl = NULL;
}
break;
}
return pl;
}
proplist_t
ReadProplistFromFile(char *file)
{
FILE *f;
proplist_t pl = NULL;
f = fopen(file, "r");
if (!f) {
wsyserror(_("could not open domain file %s"), file);
return NULL;
}
file_name = file;
line_number = 1;
buffer_size = INITIAL_BUFFER_SIZE;
buffer = wmalloc(buffer_size);
pl = get_object(f);
/* check for illegal characters after EOF */
if (get_non_space_char(f)!=EOF && pl) {
COMPLAIN(_("extra data after end of file"));
/*
* We can't just ignore garbage after the file because the "garbage"
* could be the data and the real garbage be in the beginning of
* the file (wich is now, inside pl)
*/
PLRelease(pl);
pl = NULL;
}
free(buffer);
fclose(f);
if (pl) {
proplist_t fpl;
fpl = PLMakeString(file);
PLSetFilename(pl, fpl);
PLRelease(fpl);
}
return pl;
}