1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-18 20:10:29 +01:00
Files
wmaker/WINGs/proplist.c
John D Pell a0b283a60f WUtil: Be more strict about base directory for wmkdirhier()
The original code refused to create anything not in $WMAKER_USER_ROOT, now
we go one step further and limit creation inside its 'Library' or
'Defaults' sub-directories.
2021-08-10 09:42:43 +01:00

1879 lines
40 KiB
C

#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <ftw.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "WUtil.h"
#include "wconfig.h"
typedef enum {
WPLString = 0x57504c01,
WPLData = 0x57504c02,
WPLArray = 0x57504c03,
WPLDictionary = 0x57504c04
} WPLType;
typedef struct W_PropList {
WPLType type;
union {
char *string;
WMData *data;
WMArray *array;
WMHashTable *dict;
} d;
int retainCount;
} W_PropList;
typedef struct PLData {
const char *ptr;
int pos;
const char *filename;
int lineNumber;
} PLData;
typedef struct StringBuffer {
char *str;
int size;
} StringBuffer;
static unsigned hashPropList(const void *param);
static WMPropList *getPLString(PLData * pldata);
static WMPropList *getPLQString(PLData * pldata);
static WMPropList *getPLData(PLData * pldata);
static WMPropList *getPLArray(PLData * pldata);
static WMPropList *getPLDictionary(PLData * pldata);
static WMPropList *getPropList(PLData * pldata);
typedef Bool(*isEqualFunc) (const void *, const void *);
static const WMHashTableCallbacks WMPropListHashCallbacks = {
hashPropList,
(isEqualFunc) WMIsPropListEqualTo,
NULL,
NULL
};
static Bool caseSensitive = True;
#define BUFFERSIZE 8192
#define BUFFERSIZE_INCREMENT 1024
#if 0
# define DPUT(s) puts(s)
#else
# define DPUT(s)
#endif
#define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
(pld)->filename ? "file" : "PropList",\
(pld)->filename ? (pld)->filename : "description",\
(pld)->lineNumber, msg)
#define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
|| (c)=='+')
#define CHECK_BUFFER_SIZE(buf, ptr) \
if ((ptr) >= (buf).size-1) {\
(buf).size += BUFFERSIZE_INCREMENT;\
(buf).str = wrealloc((buf).str, (buf).size);\
}
#define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
#define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
#define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
#define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
#define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
#define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
#define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
#define MaxHashLength 64
static unsigned hashPropList(const void *param)
{
WMPropList *plist= (WMPropList *) param;
unsigned ret = 0;
unsigned ctr = 0;
const char *key;
int i, len;
switch (plist->type) {
case WPLString:
key = plist->d.string;
len = WMIN(strlen(key), MaxHashLength);
for (i = 0; i < len; i++) {
ret ^= tolower(key[i]) << ctr;
ctr = (ctr + 1) % sizeof(char *);
}
/*while (*key) {
ret ^= tolower(*key++) << ctr;
ctr = (ctr + 1) % sizeof (char *);
} */
break;
case WPLData:
key = WMDataBytes(plist->d.data);
len = WMIN(WMGetDataLength(plist->d.data), MaxHashLength);
for (i = 0; i < len; i++) {
ret ^= key[i] << ctr;
ctr = (ctr + 1) % sizeof(char *);
}
break;
default:
wwarning(_("Only string or data is supported for a proplist dictionary key"));
wassertrv(False, 0);
break;
}
return ret;
}
static WMPropList *retainPropListByCount(WMPropList * plist, int count)
{
WMPropList *key, *value;
WMHashEnumerator e;
int i;
plist->retainCount += count;
switch (plist->type) {
case WPLString:
case WPLData:
break;
case WPLArray:
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
}
break;
case WPLDictionary:
e = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
retainPropListByCount(key, count);
retainPropListByCount(value, count);
}
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, NULL);
break;
}
return plist;
}
static void releasePropListByCount(WMPropList * plist, int count)
{
WMPropList *key, *value;
WMHashEnumerator e;
int i;
plist->retainCount -= count;
switch (plist->type) {
case WPLString:
if (plist->retainCount < 1) {
wfree(plist->d.string);
wfree(plist);
}
break;
case WPLData:
if (plist->retainCount < 1) {
WMReleaseData(plist->d.data);
wfree(plist);
}
break;
case WPLArray:
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
}
if (plist->retainCount < 1) {
WMFreeArray(plist->d.array);
wfree(plist);
}
break;
case WPLDictionary:
e = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
releasePropListByCount(key, count);
releasePropListByCount(value, count);
}
if (plist->retainCount < 1) {
WMFreeHashTable(plist->d.dict);
wfree(plist);
}
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertr(False);
break;
}
}
static char *dataDescription(WMPropList * plist)
{
const unsigned char *data;
char *retVal;
int i, j, length;
data = WMDataBytes(plist->d.data);
length = WMGetDataLength(plist->d.data);
retVal = (char *)wmalloc(2 * length + length / 4 + 3);
retVal[0] = '<';
for (i = 0, j = 1; i < length; i++) {
retVal[j++] = num2char((data[i] >> 4) & 0x0f);
retVal[j++] = num2char(data[i] & 0x0f);
if ((i & 0x03) == 3 && i != length - 1) {
/* if we've just finished a 32-bit int, add a space */
retVal[j++] = ' ';
}
}
retVal[j++] = '>';
retVal[j] = '\0';
return retVal;
}
static char *stringDescription(WMPropList * plist)
{
const char *str;
char *retVal, *sPtr, *dPtr;
int len, quote;
unsigned char ch;
str = plist->d.string;
if (strlen(str) == 0) {
return wstrdup("\"\"");
}
/* FIXME: make this work with unichars. */
quote = 0;
sPtr = (char *)str;
len = 0;
while ((ch = *sPtr)) {
if (!noquote(ch)) {
quote = 1;
if (charesc(ch))
len++;
else if (numesc(ch))
len += 3;
}
sPtr++;
len++;
}
if (quote)
len += 2;
retVal = (char *)wmalloc(len + 1);
sPtr = (char *)str;
dPtr = retVal;
if (quote)
*dPtr++ = '"';
while ((ch = *sPtr)) {
if (charesc(ch)) {
*(dPtr++) = '\\';
switch (ch) {
case '\a':
*dPtr = 'a';
break;
case '\b':
*dPtr = 'b';
break;
case '\t':
*dPtr = 't';
break;
case '\n':
*dPtr = 'n';
break;
case '\v':
*dPtr = 'v';
break;
case '\f':
*dPtr = 'f';
break;
default:
*dPtr = ch; /* " or \ */
}
} else if (numesc(ch)) {
*(dPtr++) = '\\';
*(dPtr++) = '0' + ((ch >> 6) & 07);
*(dPtr++) = '0' + ((ch >> 3) & 07);
*dPtr = '0' + (ch & 07);
} else {
*dPtr = ch;
}
sPtr++;
dPtr++;
}
if (quote)
*dPtr++ = '"';
*dPtr = '\0';
return retVal;
}
static char *description(WMPropList * plist)
{
WMPropList *key, *val;
char *retstr = NULL;
char *str, *tmp, *skey, *sval;
WMHashEnumerator e;
int i;
switch (plist->type) {
case WPLString:
retstr = stringDescription(plist);
break;
case WPLData:
retstr = dataDescription(plist);
break;
case WPLArray:
retstr = wstrdup("(");
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
str = description(WMGetFromArray(plist->d.array, i));
if (i == 0) {
retstr = wstrappend(retstr, str);
} else {
tmp = (char *)wmalloc(strlen(retstr) + strlen(str) + 3);
sprintf(tmp, "%s, %s", retstr, str);
wfree(retstr);
retstr = tmp;
}
wfree(str);
}
retstr = wstrappend(retstr, ")");
break;
case WPLDictionary:
retstr = wstrdup("{");
e = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
skey = description(key);
sval = description(val);
tmp = (char *)wmalloc(strlen(retstr) + strlen(skey) + strlen(sval) + 5);
sprintf(tmp, "%s%s = %s;", retstr, skey, sval);
wfree(skey);
wfree(sval);
wfree(retstr);
retstr = tmp;
}
retstr = wstrappend(retstr, "}");
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, NULL);
break;
}
return retstr;
}
static char *indentedDescription(WMPropList * plist, int level)
{
WMPropList *key, *val;
char *retstr = NULL;
char *str, *tmp, *skey, *sval;
WMHashEnumerator e;
int i;
if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
retstr = description(plist);
if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
return retstr;
} else if (retstr) {
wfree(retstr);
retstr = NULL;
}
}
switch (plist->type) {
case WPLString:
retstr = stringDescription(plist);
break;
case WPLData:
retstr = dataDescription(plist);
break;
case WPLArray:
retstr = wstrdup("(\n");
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
str = indentedDescription(WMGetFromArray(plist->d.array, i), level + 1);
if (i == 0) {
tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 1);
sprintf(tmp, "%s%*s%s", retstr, 2 * (level + 1), "", str);
wfree(retstr);
retstr = tmp;
} else {
tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 3);
sprintf(tmp, "%s,\n%*s%s", retstr, 2 * (level + 1), "", str);
wfree(retstr);
retstr = tmp;
}
wfree(str);
}
tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 3);
sprintf(tmp, "%s\n%*s)", retstr, 2 * level, "");
wfree(retstr);
retstr = tmp;
break;
case WPLDictionary:
retstr = wstrdup("{\n");
e = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
skey = indentedDescription(key, level + 1);
sval = indentedDescription(val, level + 1);
tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(skey)
+ strlen(sval) + 6);
sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2 * (level + 1), "", skey, sval);
wfree(skey);
wfree(sval);
wfree(retstr);
retstr = tmp;
}
tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 2);
sprintf(tmp, "%s%*s}", retstr, 2 * level, "");
wfree(retstr);
retstr = tmp;
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, NULL);
break;
}
return retstr;
}
static inline int getChar(PLData * pldata)
{
int c;
c = pldata->ptr[pldata->pos];
if (c == 0) {
return 0;
}
pldata->pos++;
if (c == '\n')
pldata->lineNumber++;
return c;
}
static inline int getNonSpaceChar(PLData * pldata)
{
int c;
while (1) {
c = pldata->ptr[pldata->pos];
if (c == 0) {
break;
}
pldata->pos++;
if (c == '\n') {
pldata->lineNumber++;
} else if (!isspace(c)) {
break;
}
}
return c;
}
static char *unescapestr(const char *src)
{
char *dest = wmalloc(strlen(src) + 1);
char *dPtr;
char ch;
for (dPtr = dest; ; dPtr++) {
ch = *src++;
if (ch == '\0')
break;
else if (ch != '\\')
*dPtr = ch;
else {
ch = *(src++);
if (ch == '\0') {
*dPtr = '\\';
break;
} else if ((ch >= '0') && (ch <= '7')) {
char wch;
/* Convert octal number to character */
wch = (ch & 07);
ch = *src;
if ((ch >= '0') && (ch <= '7')) {
src++;
wch = (wch << 3) | (ch & 07);
ch = *src;
if ((ch >= '0') && (ch <= '7')) {
src++;
wch = (wch << 3) | (ch & 07);
}
}
*dPtr = wch;
} else {
switch (ch) {
case 'a':
*dPtr = '\a';
break;
case 'b':
*dPtr = '\b';
break;
case 't':
*dPtr = '\t';
break;
case 'r':
*dPtr = '\r';
break;
case 'n':
*dPtr = '\n';
break;
case 'v':
*dPtr = '\v';
break;
case 'f':
*dPtr = '\f';
break;
default:
*dPtr = ch;
}
}
}
}
*dPtr = 0;
return dest;
}
static WMPropList *getPLString(PLData * pldata)
{
WMPropList *plist;
StringBuffer sBuf;
int ptr = 0;
int c;
sBuf.str = wmalloc(BUFFERSIZE);
sBuf.size = BUFFERSIZE;
while (1) {
c = getChar(pldata);
if (ISSTRINGABLE(c)) {
CHECK_BUFFER_SIZE(sBuf, ptr);
sBuf.str[ptr++] = c;
} else {
if (c != 0) {
pldata->pos--;
}
break;
}
}
sBuf.str[ptr] = 0;
if (ptr == 0) {
plist = NULL;
} else {
char *tmp = unescapestr(sBuf.str);
plist = WMCreatePLString(tmp);
wfree(tmp);
}
wfree(sBuf.str);
return plist;
}
static WMPropList *getPLQString(PLData * pldata)
{
WMPropList *plist;
int ptr = 0, escaping = 0, ok = 1;
int c;
StringBuffer sBuf;
sBuf.str = wmalloc(BUFFERSIZE);
sBuf.size = BUFFERSIZE;
while (1) {
c = getChar(pldata);
if (!escaping) {
if (c == '\\') {
escaping = 1;
continue;
} else if (c == '"') {
break;
}
} else {
CHECK_BUFFER_SIZE(sBuf, ptr);
sBuf.str[ptr++] = '\\';
escaping = 0;
}
if (c == 0) {
COMPLAIN(pldata, _("unterminated PropList string"));
ok = 0;
break;
} else {
CHECK_BUFFER_SIZE(sBuf, ptr);
sBuf.str[ptr++] = c;
}
}
sBuf.str[ptr] = 0;
if (!ok) {
plist = NULL;
} else {
char *tmp = unescapestr(sBuf.str);
plist = WMCreatePLString(tmp);
wfree(tmp);
}
wfree(sBuf.str);
return plist;
}
static WMPropList *getPLData(PLData * pldata)
{
int ok = 1;
int len = 0;
int c1, c2;
unsigned char buf[BUFFERSIZE], byte;
WMPropList *plist;
WMData *data;
data = WMCreateDataWithCapacity(0);
while (1) {
c1 = getNonSpaceChar(pldata);
if (c1 == 0) {
COMPLAIN(pldata, _("unterminated PropList data"));
ok = 0;
break;
} else if (c1 == '>') {
break;
} else if (ishexdigit(c1)) {
c2 = getNonSpaceChar(pldata);
if (c2 == 0 || c2 == '>') {
COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
ok = 0;
break;
} else if (ishexdigit(c2)) {
byte = char2num(c1) << 4;
byte |= char2num(c2);
buf[len++] = byte;
if (len == sizeof(buf)) {
WMAppendDataBytes(data, buf, len);
len = 0;
}
} else {
COMPLAIN(pldata, _("non hexdigit character in PropList data"));
ok = 0;
break;
}
} else {
COMPLAIN(pldata, _("non hexdigit character in PropList data"));
ok = 0;
break;
}
}
if (!ok) {
WMReleaseData(data);
return NULL;
}
if (len > 0)
WMAppendDataBytes(data, buf, len);
plist = WMCreatePLData(data);
WMReleaseData(data);
return plist;
}
static WMPropList *getPLArray(PLData * pldata)
{
Bool first = True;
int ok = 1;
int c;
WMPropList *array, *obj;
array = WMCreatePLArray(NULL);
while (1) {
c = getNonSpaceChar(pldata);
if (c == 0) {
COMPLAIN(pldata, _("unterminated PropList array"));
ok = 0;
break;
} else if (c == ')') {
break;
} else if (c == ',') {
/* continue normally */
} else if (!first) {
COMPLAIN(pldata, _("missing or unterminated PropList array"));
ok = 0;
break;
} else {
pldata->pos--;
}
first = False;
obj = getPropList(pldata);
if (!obj) {
COMPLAIN(pldata, _("could not get PropList array element"));
ok = 0;
break;
}
WMAddToPLArray(array, obj);
WMReleasePropList(obj);
}
if (!ok) {
WMReleasePropList(array);
array = NULL;
}
return array;
}
static WMPropList *getPLDictionary(PLData * pldata)
{
int ok = 1;
int c;
WMPropList *dict, *key, *value;
dict = WMCreatePLDictionary(NULL, NULL);
while (1) {
c = getNonSpaceChar(pldata);
if (c == 0) {
COMPLAIN(pldata, _("unterminated PropList dictionary"));
ok = 0;
break;
} else if (c == '}') {
break;
}
DPUT("getting PropList dictionary key");
if (c == '<') {
key = getPLData(pldata);
} else if (c == '"') {
key = getPLQString(pldata);
} else if (ISSTRINGABLE(c)) {
pldata->pos--;
key = getPLString(pldata);
} else {
if (c == '=') {
COMPLAIN(pldata, _("missing PropList dictionary key"));
} else {
COMPLAIN(pldata, _("missing PropList dictionary entry key "
"or unterminated dictionary"));
}
ok = 0;
break;
}
if (!key) {
COMPLAIN(pldata, _("error parsing PropList dictionary key"));
ok = 0;
break;
}
c = getNonSpaceChar(pldata);
if (c != '=') {
WMReleasePropList(key);
COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
ok = 0;
break;
}
DPUT("getting PropList dictionary entry value for key");
value = getPropList(pldata);
if (!value) {
COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
WMReleasePropList(key);
ok = 0;
break;
}
c = getNonSpaceChar(pldata);
if (c != ';') {
COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
WMReleasePropList(key);
WMReleasePropList(value);
ok = 0;
break;
}
WMPutInPLDictionary(dict, key, value);
WMReleasePropList(key);
WMReleasePropList(value);
}
if (!ok) {
WMReleasePropList(dict);
dict = NULL;
}
return dict;
}
static WMPropList *getPropList(PLData * pldata)
{
WMPropList *plist;
int c;
c = getNonSpaceChar(pldata);
switch (c) {
case 0:
DPUT("End of PropList");
plist = NULL;
break;
case '{':
DPUT("Getting PropList dictionary");
plist = getPLDictionary(pldata);
break;
case '(':
DPUT("Getting PropList array");
plist = getPLArray(pldata);
break;
case '<':
DPUT("Getting PropList data");
plist = getPLData(pldata);
break;
case '"':
DPUT("Getting PropList quoted string");
plist = getPLQString(pldata);
break;
default:
if (ISSTRINGABLE(c)) {
DPUT("Getting PropList string");
pldata->pos--;
plist = getPLString(pldata);
} else {
COMPLAIN(pldata, _("was expecting a string, data, array or "
"dictionary. If it's a string, try enclosing " "it with \"."));
if (c == '#' || c == '/') {
wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
}
plist = NULL;
}
break;
}
return plist;
}
void WMPLSetCaseSensitive(Bool caseSensitiveness)
{
caseSensitive = caseSensitiveness;
}
WMPropList *WMCreatePLString(const char *str)
{
WMPropList *plist;
wassertrv(str != NULL, NULL);
plist = (WMPropList *) wmalloc(sizeof(W_PropList));
plist->type = WPLString;
plist->d.string = wstrdup(str);
plist->retainCount = 1;
return plist;
}
WMPropList *WMCreatePLData(WMData * data)
{
WMPropList *plist;
wassertrv(data != NULL, NULL);
plist = (WMPropList *) wmalloc(sizeof(W_PropList));
plist->type = WPLData;
plist->d.data = WMRetainData(data);
plist->retainCount = 1;
return plist;
}
WMPropList *WMCreatePLDataWithBytes(const unsigned char *bytes, unsigned int length)
{
WMPropList *plist;
wassertrv(bytes != NULL, NULL);
plist = (WMPropList *) wmalloc(sizeof(W_PropList));
plist->type = WPLData;
plist->d.data = WMCreateDataWithBytes(bytes, length);
plist->retainCount = 1;
return plist;
}
WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
{
WMPropList *plist;
wassertrv(bytes != NULL, NULL);
plist = (WMPropList *) wmalloc(sizeof(W_PropList));
plist->type = WPLData;
plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
plist->retainCount = 1;
return plist;
}
WMPropList *WMCreatePLArray(WMPropList * elem, ...)
{
WMPropList *plist, *nelem;
va_list ap;
plist = (WMPropList *) wmalloc(sizeof(W_PropList));
plist->type = WPLArray;
plist->d.array = WMCreateArray(4);
plist->retainCount = 1;
if (!elem)
return plist;
WMAddToArray(plist->d.array, WMRetainPropList(elem));
va_start(ap, elem);
while (1) {
nelem = va_arg(ap, WMPropList *);
if (!nelem) {
va_end(ap);
return plist;
}
WMAddToArray(plist->d.array, WMRetainPropList(nelem));
}
}
WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
{
WMPropList *plist, *nkey, *nvalue, *k, *v;
va_list ap;
plist = (WMPropList *) wmalloc(sizeof(W_PropList));
plist->type = WPLDictionary;
plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
plist->retainCount = 1;
if (!key || !value)
return plist;
WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
va_start(ap, value);
while (1) {
nkey = va_arg(ap, WMPropList *);
if (!nkey) {
va_end(ap);
return plist;
}
nvalue = va_arg(ap, WMPropList *);
if (!nvalue) {
va_end(ap);
return plist;
}
if (WMHashGetItemAndKey(plist->d.dict, nkey, (void **)&v, (void **)&k)) {
WMHashRemove(plist->d.dict, k);
WMReleasePropList(k);
WMReleasePropList(v);
}
WMHashInsert(plist->d.dict, WMRetainPropList(nkey), WMRetainPropList(nvalue));
}
}
WMPropList *WMRetainPropList(WMPropList * plist)
{
WMPropList *key, *value;
WMHashEnumerator e;
int i;
plist->retainCount++;
switch (plist->type) {
case WPLString:
case WPLData:
break;
case WPLArray:
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
WMRetainPropList(WMGetFromArray(plist->d.array, i));
}
break;
case WPLDictionary:
e = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
WMRetainPropList(key);
WMRetainPropList(value);
}
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, NULL);
break;
}
return plist;
}
void WMReleasePropList(WMPropList * plist)
{
WMPropList *key, *value;
WMHashEnumerator e;
int i;
plist->retainCount--;
switch (plist->type) {
case WPLString:
if (plist->retainCount < 1) {
wfree(plist->d.string);
wfree(plist);
}
break;
case WPLData:
if (plist->retainCount < 1) {
WMReleaseData(plist->d.data);
wfree(plist);
}
break;
case WPLArray:
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
WMReleasePropList(WMGetFromArray(plist->d.array, i));
}
if (plist->retainCount < 1) {
WMFreeArray(plist->d.array);
wfree(plist);
}
break;
case WPLDictionary:
e = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
WMReleasePropList(key);
WMReleasePropList(value);
}
if (plist->retainCount < 1) {
WMFreeHashTable(plist->d.dict);
wfree(plist);
}
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertr(False);
break;
}
}
void WMInsertInPLArray(WMPropList * plist, int index, WMPropList * item)
{
wassertr(plist->type == WPLArray);
retainPropListByCount(item, plist->retainCount);
WMInsertInArray(plist->d.array, index, item);
}
void WMAddToPLArray(WMPropList * plist, WMPropList * item)
{
wassertr(plist->type == WPLArray);
retainPropListByCount(item, plist->retainCount);
WMAddToArray(plist->d.array, item);
}
void WMDeleteFromPLArray(WMPropList * plist, int index)
{
WMPropList *item;
wassertr(plist->type == WPLArray);
item = WMGetFromArray(plist->d.array, index);
if (item != NULL) {
WMDeleteFromArray(plist->d.array, index);
releasePropListByCount(item, plist->retainCount);
}
}
void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
{
WMPropList *iPtr;
int i;
wassertr(plist->type == WPLArray);
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
iPtr = WMGetFromArray(plist->d.array, i);
if (WMIsPropListEqualTo(item, iPtr)) {
WMDeleteFromArray(plist->d.array, i);
releasePropListByCount(iPtr, plist->retainCount);
break;
}
}
}
void WMPutInPLDictionary(WMPropList * plist, WMPropList * key, WMPropList * value)
{
wassertr(plist->type == WPLDictionary);
/*WMRetainPropList(key); */
WMRemoveFromPLDictionary(plist, key);
retainPropListByCount(key, plist->retainCount);
retainPropListByCount(value, plist->retainCount);
WMHashInsert(plist->d.dict, key, value);
/*WMReleasePropList(key); */
}
void WMRemoveFromPLDictionary(WMPropList * plist, WMPropList * key)
{
WMPropList *k, *v;
wassertr(plist->type == WPLDictionary);
if (WMHashGetItemAndKey(plist->d.dict, key, (void **)&v, (void **)&k)) {
WMHashRemove(plist->d.dict, k);
releasePropListByCount(k, plist->retainCount);
releasePropListByCount(v, plist->retainCount);
}
}
WMPropList *WMMergePLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
{
WMPropList *key, *value, *dvalue;
WMHashEnumerator e;
wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
if (source == dest)
return dest;
e = WMEnumerateHashTable(source->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
if (recursive && value->type == WPLDictionary) {
dvalue = WMHashGet(dest->d.dict, key);
if (dvalue && dvalue->type == WPLDictionary) {
WMMergePLDictionaries(dvalue, value, True);
} else {
WMPutInPLDictionary(dest, key, value);
}
} else {
WMPutInPLDictionary(dest, key, value);
}
}
return dest;
}
WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
{
WMPropList *key, *value, *dvalue;
WMHashEnumerator e;
wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
if (source == dest) {
WMPropList *keys = WMGetPLDictionaryKeys(dest);
int i;
for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
}
WMReleasePropList(keys);
return dest;
}
e = WMEnumerateHashTable(source->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
dvalue = WMHashGet(dest->d.dict, key);
if (!dvalue)
continue;
if (WMIsPropListEqualTo(value, dvalue)) {
WMRemoveFromPLDictionary(dest, key);
} else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
WMSubtractPLDictionaries(dvalue, value, True);
}
}
return dest;
}
int WMGetPropListItemCount(WMPropList * plist)
{
switch (plist->type) {
case WPLString:
case WPLData:
return 0; /* should this be 1 instead? */
case WPLArray:
return WMGetArrayItemCount(plist->d.array);
case WPLDictionary:
return (int)WMCountHashTable(plist->d.dict);
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, 0);
break;
}
return 0;
}
Bool WMIsPLString(WMPropList * plist)
{
if (plist)
return (plist->type == WPLString);
else
return False;
}
Bool WMIsPLData(WMPropList * plist)
{
if (plist)
return (plist->type == WPLData);
else
return False;
}
Bool WMIsPLArray(WMPropList * plist)
{
if (plist)
return (plist->type == WPLArray);
else
return False;
}
Bool WMIsPLDictionary(WMPropList * plist)
{
if (plist)
return (plist->type == WPLDictionary);
else
return False;
}
Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
{
WMPropList *key1, *item1, *item2;
WMHashEnumerator enumerator;
int n, i;
if (plist->type != other->type)
return False;
switch (plist->type) {
case WPLString:
if (caseSensitive) {
return (strcmp(plist->d.string, other->d.string) == 0);
} else {
return (strcasecmp(plist->d.string, other->d.string) == 0);
}
case WPLData:
return WMIsDataEqualToData(plist->d.data, other->d.data);
case WPLArray:
n = WMGetArrayItemCount(plist->d.array);
if (n != WMGetArrayItemCount(other->d.array))
return False;
for (i = 0; i < n; i++) {
item1 = WMGetFromArray(plist->d.array, i);
item2 = WMGetFromArray(other->d.array, i);
if (!WMIsPropListEqualTo(item1, item2))
return False;
}
return True;
case WPLDictionary:
if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
return False;
enumerator = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
item2 = WMHashGet(other->d.dict, key1);
if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
return False;
}
return True;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, False);
break;
}
return False;
}
char *WMGetFromPLString(WMPropList * plist)
{
wassertrv(plist->type == WPLString, NULL);
return plist->d.string;
}
WMData *WMGetFromPLData(WMPropList * plist)
{
wassertrv(plist->type == WPLData, NULL);
return plist->d.data;
}
const unsigned char *WMGetPLDataBytes(WMPropList * plist)
{
wassertrv(plist->type == WPLData, NULL);
return WMDataBytes(plist->d.data);
}
int WMGetPLDataLength(WMPropList * plist)
{
wassertrv(plist->type == WPLData, 0);
return WMGetDataLength(plist->d.data);
}
WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
{
wassertrv(plist->type == WPLArray, NULL);
return WMGetFromArray(plist->d.array, index);
}
WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
{
wassertrv(plist->type == WPLDictionary, NULL);
return WMHashGet(plist->d.dict, key);
}
WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
{
WMPropList *array, *key;
WMHashEnumerator enumerator;
wassertrv(plist->type == WPLDictionary, NULL);
array = (WMPropList *) wmalloc(sizeof(W_PropList));
array->type = WPLArray;
array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
array->retainCount = 1;
enumerator = WMEnumerateHashTable(plist->d.dict);
while ((key = WMNextHashEnumeratorKey(&enumerator))) {
WMAddToArray(array->d.array, WMRetainPropList(key));
}
return array;
}
WMPropList *WMShallowCopyPropList(WMPropList * plist)
{
WMPropList *ret = NULL;
WMPropList *key, *item;
WMHashEnumerator e;
WMData *data;
int i;
switch (plist->type) {
case WPLString:
ret = WMCreatePLString(plist->d.string);
break;
case WPLData:
data = WMCreateDataWithData(plist->d.data);
ret = WMCreatePLData(data);
WMReleaseData(data);
break;
case WPLArray:
ret = (WMPropList *) wmalloc(sizeof(W_PropList));
ret->type = WPLArray;
ret->d.array = WMCreateArrayWithArray(plist->d.array);
ret->retainCount = 1;
for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
WMRetainPropList(WMGetFromArray(ret->d.array, i));
break;
case WPLDictionary:
ret = WMCreatePLDictionary(NULL, NULL);
e = WMEnumerateHashTable(plist->d.dict);
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
WMPutInPLDictionary(ret, key, item);
}
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, NULL);
break;
}
return ret;
}
WMPropList *WMDeepCopyPropList(WMPropList * plist)
{
WMPropList *ret = NULL;
WMPropList *key, *item;
WMHashEnumerator e;
WMData *data;
int i;
switch (plist->type) {
case WPLString:
ret = WMCreatePLString(plist->d.string);
break;
case WPLData:
data = WMCreateDataWithData(plist->d.data);
ret = WMCreatePLData(data);
WMReleaseData(data);
break;
case WPLArray:
ret = WMCreatePLArray(NULL);
for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
WMAddToArray(ret->d.array, item);
}
break;
case WPLDictionary:
ret = WMCreatePLDictionary(NULL, NULL);
e = WMEnumerateHashTable(plist->d.dict);
/* While we copy an existing dictionary there is no way that we can
* have duplicate keys, so we don't need to first remove a key/value
* pair before inserting the new key/value.
*/
while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
}
break;
default:
wwarning(_("Used proplist functions on non-WMPropLists objects"));
wassertrv(False, NULL);
break;
}
return ret;
}
WMPropList *WMCreatePropListFromDescription(const char *desc)
{
WMPropList *plist = NULL;
PLData *pldata;
pldata = (PLData *) wmalloc(sizeof(PLData));
pldata->ptr = desc;
pldata->lineNumber = 1;
plist = getPropList(pldata);
if (getNonSpaceChar(pldata) != 0 && plist) {
COMPLAIN(pldata, _("extra data after end of property list"));
/*
* We can't just ignore garbage after the end of the description
* (especially if the description was read from a file), because
* the "garbage" can be the real data and the real garbage is in
* fact in the beginning of the file (which is now inside plist)
*/
WMReleasePropList(plist);
plist = NULL;
}
wfree(pldata);
return plist;
}
char *WMGetPropListDescription(WMPropList * plist, Bool indented)
{
return (indented ? indentedDescription(plist, 0) : description(plist));
}
WMPropList *WMReadPropListFromFile(const char *file)
{
WMPropList *plist = NULL;
PLData *pldata;
char *read_buf;
FILE *f;
struct stat stbuf;
size_t length;
f = fopen(file, "rb");
if (!f) {
/* let the user print the error message if he really needs to */
/*werror(_("could not open domain file '%s' for reading"), file); */
return NULL;
}
if (stat(file, &stbuf) == 0) {
length = (size_t) stbuf.st_size;
} else {
werror(_("could not get size for file '%s'"), file);
fclose(f);
return NULL;
}
read_buf = wmalloc(length + 1);
if (fread(read_buf, length, 1, f) != 1) {
if (ferror(f)) {
werror(_("error reading from file '%s'"), file);
}
fclose(f);
wfree(read_buf);
return NULL;
}
read_buf[length] = '\0';
fclose(f);
pldata = (PLData *) wmalloc(sizeof(PLData));
pldata->ptr = read_buf;
pldata->filename = file;
pldata->lineNumber = 1;
plist = getPropList(pldata);
if (getNonSpaceChar(pldata) != 0 && plist) {
COMPLAIN(pldata, _("extra data after end of property list"));
/*
* We can't just ignore garbage after the end of the description
* (especially if the description was read from a file), because
* the "garbage" can be the real data and the real garbage is in
* fact in the beginning of the file (which is now inside plist)
*/
WMReleasePropList(plist);
plist = NULL;
}
wfree(read_buf);
wfree(pldata);
return plist;
}
WMPropList *WMReadPropListFromPipe(const char *command)
{
FILE *file;
WMPropList *plist;
PLData *pldata;
char *read_buf, *read_ptr;
size_t remain_size, line_size;
const size_t block_read_size = 4096;
const size_t block_read_margin = 512;
file = popen(command, "r");
if (!file) {
werror(_("%s:could not open menu file"), command);
return NULL;
}
/* read from file till EOF or OOM and fill proplist buffer*/
remain_size = block_read_size;
read_buf = wmalloc(remain_size);
read_ptr = read_buf;
while (fgets(read_ptr, remain_size, file) != NULL) {
line_size = strlen(read_ptr);
remain_size -= line_size;
read_ptr += line_size;
if (remain_size < block_read_margin) {
size_t read_length;
read_length = read_ptr - read_buf;
read_buf = wrealloc(read_buf, read_length + block_read_size);
read_ptr = read_buf + read_length;
remain_size = block_read_size;
}
}
pclose(file);
pldata = (PLData *) wmalloc(sizeof(PLData));
pldata->ptr = read_buf;
pldata->filename = command;
pldata->lineNumber = 1;
plist = getPropList(pldata);
if (getNonSpaceChar(pldata) != 0 && plist) {
COMPLAIN(pldata, _("extra data after end of property list"));
/*
* We can't just ignore garbage after the end of the description
* (especially if the description was read from a file), because
* the "garbage" can be the real data and the real garbage is in
* fact in the beginning of the file (which is now inside plist)
*/
WMReleasePropList(plist);
plist = NULL;
}
wfree(read_buf);
wfree(pldata);
return plist;
}
/* TODO: review this function's code */
Bool WMWritePropListToFile(WMPropList * plist, const char *path)
{
char *thePath = NULL;
char *desc;
FILE *theFile;
#ifdef HAVE_MKSTEMP
int fd, mask;
#endif
if (!wmkdirhier(path))
return False;
/* Use the path name of the destination file as a prefix for the
* mkstemp() call so that we can be sure that both files are on
* the same filesystem and the subsequent rename() will work. */
thePath = wstrconcat(path, ".XXXXXX");
#ifdef HAVE_MKSTEMP
/*
* We really just want to read the current umask, but as Coverity is
* pointing a possible security issue:
* some versions of mkstemp do not set file rights properly on the
* created file, so it is recommended so set the umask beforehand.
* As we need to set an umask to read the current value, we take this
* opportunity to set a temporary aggresive umask so Coverity won't
* complain, even if we do not really care in the present use case.
*/
mask = umask(S_IRWXG | S_IRWXO);
if ((fd = mkstemp(thePath)) < 0) {
werror(_("mkstemp (%s) failed"), thePath);
goto failure;
}
umask(mask);
fchmod(fd, 0666 & ~mask);
if ((theFile = fdopen(fd, "wb")) == NULL) {
close(fd);
}
#else
if (mktemp(thePath) == NULL) {
werror(_("mktemp (%s) failed"), thePath);
goto failure;
}
theFile = fopen(thePath, "wb");
#endif
if (theFile == NULL) {
werror(_("open (%s) failed"), thePath);
goto failure;
}
desc = indentedDescription(plist, 0);
if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
werror(_("writing to file: %s failed"), thePath);
wfree(desc);
fclose(theFile);
goto failure;
}
wfree(desc);
(void)fsync(fileno(theFile));
if (fclose(theFile) != 0) {
werror(_("fclose (%s) failed"), thePath);
goto failure;
}
/* If we used a temporary file, we still need to rename() it be the
* real file. Also, we need to try to retain the file attributes of
* the original file we are overwriting (if we are) */
if (rename(thePath, path) != 0) {
werror(_("rename ('%s' to '%s') failed"), thePath, path);
goto failure;
}
wfree(thePath);
return True;
failure:
unlink(thePath);
wfree(thePath);
return False;
}
/*
* create a directory hierarchy
*
* if the last octet of `path' is `/', the full path is
* assumed to be a directory; otherwise path is assumed to be a
* file, and the last component is stripped off. the rest is the
* the hierarchy to be created.
*
* refuses to create anything outside $WMAKER_USER_ROOT/Defaults or $WMAKER_USER_ROOT/Library
*
* returns 1 on success, 0 on failure
*/
int wmkdirhier(const char *path)
{
const char *libpath;
char *udefpath;
int cmp;
char *thePath = NULL, buf[1024];
size_t p, plen;
struct stat st;
/* Only create directories under $WMAKER_USER_ROOT/Defaults or $WMAKER_USER_ROOT/Library */
libpath = wuserdatapath();
if (strncmp(path, libpath, strlen(libpath)) == 0)
if (path[strlen(libpath)] == '/')
goto path_in_valid_tree;
udefpath = wdefaultspathfordomain("");
cmp = strncmp(path, udefpath, strlen(udefpath));
wfree(udefpath);
if (cmp == 0)
/* Note: by side effect, 'udefpath' already contains a final '/' */
goto path_in_valid_tree;
/* If we reach this point, the path is outside the allowed tree */
return 0;
path_in_valid_tree:
thePath = wstrdup(path);
/* Strip the trailing component if it is a file */
p = strlen(thePath);
while (p && thePath[p] != '/')
thePath[p--] = '\0';
thePath[p] = '\0';
/* Shortcut if it already exists */
if (stat(thePath, &st) == 0) {
wfree(thePath);
if (S_ISDIR(st.st_mode)) {
/* Is a directory alright */
return 1;
} else {
/* Exists, but not a directory, the caller
* might just as well abort now */
return 0;
}
}
memset(buf, 0, sizeof(buf));
p = strlen(buf);
plen = strlen(thePath);
do {
while (p++ < plen && thePath[p] != '/')
;
strncpy(buf, thePath, p);
if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
werror(_("Could not create path component %s"), buf);
wfree(thePath);
return 0;
}
} while (p < plen);
wfree(thePath);
return 1;
}
/* ARGSUSED2 */
static int wrmdirhier_fn(const char *path, const struct stat *st,
int type, struct FTW *ftw)
{
/* Parameter not used, but tell the compiler that it is ok */
(void) st;
(void) ftw;
switch(type) {
case FTW_D:
break;
case FTW_DP:
return rmdir(path);
break;
case FTW_F:
case FTW_SL:
case FTW_SLN:
return unlink(path);
break;
case FTW_DNR:
case FTW_NS:
default:
return EPERM;
}
/* NOTREACHED */
return 0;
}
/*
* remove a directory hierarchy
*
* refuses to remove anything outside $WMAKER_USER_ROOT/Defaults or $WMAKER_USER_ROOT/Library
*
* returns 1 on success, 0 on failure
*
* TODO: revisit what's error and what's not
*
* with inspirations from OpenBSD's bin/rm/rm.c
*/
int wrmdirhier(const char *path)
{
const char *libpath;
char *udefpath = NULL;
struct stat st;
int error;
/* Only remove directories under $WMAKER_USER_ROOT/Defaults or $WMAKER_USER_ROOT/Library */
libpath = wuserdatapath();
if (strncmp(path, libpath, strlen(libpath)) == 0)
if (path[strlen(libpath)] == '/')
goto path_in_valid_tree;
udefpath = wdefaultspathfordomain("");
if (strncmp(path, udefpath, strlen(udefpath)) == 0)
/* Note: by side effect, 'udefpath' already contains a final '/' */
goto path_in_valid_tree;
wfree(udefpath);
return EPERM;
path_in_valid_tree:
wfree(udefpath);
/* Shortcut if it doesn't exist to begin with */
if (stat(path, &st) == -1)
return ENOENT;
error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
return error;
}