mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-27 08:52:30 +01:00
Initial revision
This commit is contained in:
491
src/proplist.c
Normal file
491
src/proplist.c
Normal file
@@ -0,0 +1,491 @@
|
||||
/* 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.
|
||||
*
|
||||
* WindowMaker 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) << 3);
|
||||
*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 still 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 (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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user