1
0
forked from vitrine/wmaker

2 Commits

57 changed files with 1535 additions and 1309 deletions

2
.gitignore vendored
View File

@@ -142,4 +142,4 @@ WPrefs.app/WPrefs.desktop
.pc
# Rust stuff.
/*/target/**
/wmakerlib/target/**

View File

@@ -39,7 +39,7 @@ ACLOCAL_AMFLAGS = -I m4
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-silent-rules LINGUAS='*'
SUBDIRS = wrlib wutil-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc
SUBDIRS = wrlib WINGs src util po WindowMaker wmlib WPrefs.app doc
DIST_SUBDIRS = $(SUBDIRS) test
EXTRA_DIST = TODO BUGS BUGFORM FAQ INSTALL \

View File

@@ -10,12 +10,10 @@ libWUtil_la_LDFLAGS = -version-info @WUTIL_VERSION@
lib_LTLIBRARIES = libWUtil.la libWINGs.la
wutilrs = $(top_builddir)/wutil-rs/target/debug/libwutil_rs.a
wraster = $(top_builddir)/wrlib/libwraster.la
LDADD= libWUtil.la libWINGs.la $(wraster) $(wutilrs) @INTLIBS@
libWINGs_la_LIBADD = libWUtil.la $(wraster) $(wutilrs) @XLIBS@ @XFT_LIBS@ @FCLIBS@ @LIBM@ @PANGO_LIBS@
libWUtil_la_LIBADD = @LIBBSD@ $(wutilrs)
LDADD= libWUtil.la libWINGs.la $(top_builddir)/wrlib/libwraster.la @INTLIBS@
libWINGs_la_LIBADD = libWUtil.la $(top_builddir)/wrlib/libwraster.la @XLIBS@ @XFT_LIBS@ @FCLIBS@ @LIBM@ @PANGO_LIBS@
libWUtil_la_LIBADD = @LIBBSD@
EXTRA_DIST = BUGS make-rgb Examples Extras Tests
@@ -64,12 +62,15 @@ libWINGs_la_SOURCES = \
wwindow.c
libWUtil_la_SOURCES = \
array.c \
bagtree.c \
data.c \
error.c \
error.h \
findfile.c \
handlers.c \
hashtable.c \
memory.c \
menuparser.c \
menuparser.h \
menuparser_macros.c \

View File

@@ -213,6 +213,10 @@ void wfree(void *ptr);
void wrelease(void *ptr);
void* wretain(void *ptr);
typedef void waborthandler(int);
waborthandler* wsetabort(waborthandler* handler);
/* ---[ WINGs/error.c ]--------------------------------------------------- */
enum {
@@ -244,8 +248,8 @@ char* wexpandpath(const char *path);
int wcopy_file(const char *toPath, const char *srcFile, const char *destFile);
/* You must free the returned string! */
char* wgethomedir(void);
/* don't free the returned string */
const char* wgethomedir(void);
/* ---[ WINGs/proplist.c ]------------------------------------------------ */
@@ -396,7 +400,7 @@ extern const WMHashTableCallbacks WMStringPointerHashCallbacks;
/* keys are strings, but they are not copied */
/* ---[ wutil-rs/src/array.rs ]--------------------------------------------------- */
/* ---[ WINGs/array.c ]--------------------------------------------------- */
/*
* WMArray use an array to store the elements.
@@ -418,22 +422,29 @@ WMArray* WMCreateArrayWithDestructor(int initialSize, WMFreeDataProc *destructor
WMArray* WMCreateArrayWithArray(WMArray *array);
#define WMDuplicateArray(array) WMCreateArrayWithArray(array)
void WMEmptyArray(WMArray *array);
void WMFreeArray(WMArray *array);
int WMGetArrayItemCount(WMArray *array);
/* appends other to array. other remains unchanged */
void WMAppendArray(WMArray *array, WMArray *other);
/* add will place the element at the end of the array */
void WMAddToArray(WMArray *array, void *item);
/* insert will increment the index of elements after it by 1 */
void WMInsertInArray(WMArray *array, int index, void *item);
/* set returns the old item WITHOUT calling the
/* replace and set will return the old item WITHOUT calling the
* destructor on it even if its available. Free the returned item yourself.
*/
void* WMSetInArray(WMArray *array, int index, void *item);
void* WMReplaceInArray(WMArray *array, int index, void *item);
#define WMSetInArray(array, index, item) WMReplaceInArray(array, index, item)
/* delete and remove will remove the elements and cause the elements
* after them to decrement their indexes by 1. Also will call the
@@ -441,21 +452,20 @@ void* WMSetInArray(WMArray *array, int index, void *item);
*/
int WMDeleteFromArray(WMArray *array, int index);
int WMRemoveFromArray(WMArray *array, void *item);
#define WMRemoveFromArray(array, item) WMRemoveFromArrayMatching(array, NULL, item)
int WMRemoveFromArrayMatching(WMArray *array, WMMatchDataProc *match, void *cdata);
void* WMGetFromArray(WMArray *array, int index);
#define WMGetFirstInArray(array, item) WMFindInArray(array, NULL, item)
/* pop will return the last element from the array, also removing it
* from the array. The destructor is NOT called, even if available.
* Free the returned element if needed by yourself
*/
void* WMPopFromArray(WMArray *array);
/* Like WMFindInArray(array, NULL, item) */
int WMGetFirstInArray(WMArray *array, void *item);
int WMFindInArray(WMArray *array, WMMatchDataProc *match, void *cdata);
int WMCountInArray(WMArray *array, void *item);
@@ -469,6 +479,8 @@ void WMSortArray(WMArray *array, WMCompareDataProc *comparer);
void WMMapArray(WMArray *array, void (*function)(void*, void*), void *data);
WMArray* WMGetSubarrayWithRange(WMArray* array, WMRange aRange);
void* WMArrayFirst(WMArray *array, WMArrayIterator *iter);
void* WMArrayLast(WMArray *array, WMArrayIterator *iter);
@@ -592,16 +604,37 @@ WMData* WMCreateDataWithLength(unsigned length);
WMData* WMCreateDataWithBytes(const void *bytes, unsigned length);
/* destructor is a function called to free the data when releasing the data
* object, or NULL if no freeing of data is necesary. */
WMData* WMCreateDataWithBytesNoCopy(void *bytes, unsigned length,
WMFreeDataProc *destructor);
WMData* WMCreateDataWithData(WMData *aData);
WMData* WMRetainData(WMData *aData);
void WMReleaseData(WMData *aData);
/* Adjusting capacity */
void WMSetDataCapacity(WMData *aData, unsigned capacity);
void WMSetDataLength(WMData *aData, unsigned length);
void WMIncreaseDataLengthBy(WMData *aData, unsigned extraLength);
/* Accessing data */
const void* WMDataBytes(WMData *aData);
void WMGetDataBytes(WMData *aData, void *buffer);
void WMGetDataBytesWithLength(WMData *aData, void *buffer, unsigned length);
void WMGetDataBytesWithRange(WMData *aData, void *buffer, WMRange aRange);
WMData* WMGetSubdataWithRange(WMData *aData, WMRange aRange);
/* Testing data */
Bool WMIsDataEqualToData(WMData *aData, WMData *anotherData);
@@ -616,6 +649,10 @@ void WMAppendData(WMData *aData, WMData *anotherData);
/* Modifying data */
void WMReplaceDataBytesInRange(WMData *aData, WMRange aRange, const void *bytes);
void WMResetDataBytesInRange(WMData *aData, WMRange aRange);
void WMSetData(WMData *aData, WMData *anotherData);
@@ -731,6 +768,10 @@ WMPropList* WMCreatePLData(WMData *data);
WMPropList* WMCreatePLDataWithBytes(const unsigned char *bytes, unsigned int length);
WMPropList* WMCreatePLDataWithBytesNoCopy(unsigned char *bytes,
unsigned int length,
WMFreeDataProc *destructor);
WMPropList* WMCreatePLArray(WMPropList *elem, ...);
WMPropList* WMCreatePLDictionary(WMPropList *key, WMPropList *value, ...);

363
WINGs/array.c Normal file
View File

@@ -0,0 +1,363 @@
/*
* Dynamically Resized Array
*
* Authors: Alfredo K. Kojima <kojima@windowmaker.info>
* Dan Pascu <dan@windowmaker.info>
*
* This code is released to the Public Domain, but
* proper credit is always appreciated :)
*/
#include <stdlib.h>
#include <string.h>
#include "WUtil.h"
#define INITIAL_SIZE 8
#define RESIZE_INCREMENT 8
typedef struct W_Array {
void **items; /* the array data */
int itemCount; /* # of items in array */
int allocSize; /* allocated size of array */
WMFreeDataProc *destructor; /* the destructor to free elements */
} W_Array;
WMArray *WMCreateArray(int initialSize)
{
return WMCreateArrayWithDestructor(initialSize, NULL);
}
WMArray *WMCreateArrayWithDestructor(int initialSize, WMFreeDataProc * destructor)
{
WMArray *array;
array = wmalloc(sizeof(WMArray));
if (initialSize <= 0) {
initialSize = INITIAL_SIZE;
}
array->items = wmalloc(sizeof(void *) * initialSize);
array->itemCount = 0;
array->allocSize = initialSize;
array->destructor = destructor;
return array;
}
WMArray *WMCreateArrayWithArray(WMArray * array)
{
WMArray *newArray;
newArray = wmalloc(sizeof(WMArray));
newArray->items = wmalloc(sizeof(void *) * array->allocSize);
memcpy(newArray->items, array->items, sizeof(void *) * array->itemCount);
newArray->itemCount = array->itemCount;
newArray->allocSize = array->allocSize;
newArray->destructor = NULL;
return newArray;
}
void WMEmptyArray(WMArray * array)
{
if (array->destructor) {
while (array->itemCount > 0) {
array->itemCount--;
array->destructor(array->items[array->itemCount]);
}
}
/*memset(array->items, 0, array->itemCount * sizeof(void*)); */
array->itemCount = 0;
}
void WMFreeArray(WMArray * array)
{
if (array == NULL)
return;
WMEmptyArray(array);
wfree(array->items);
wfree(array);
}
int WMGetArrayItemCount(WMArray * array)
{
if (array == NULL)
return 0;
return array->itemCount;
}
void WMAppendArray(WMArray * array, WMArray * other)
{
if (array == NULL || other == NULL)
return;
if (other->itemCount == 0)
return;
if (array->itemCount + other->itemCount > array->allocSize) {
array->allocSize += other->allocSize;
array->items = wrealloc(array->items, sizeof(void *) * array->allocSize);
}
memcpy(array->items + array->itemCount, other->items, sizeof(void *) * other->itemCount);
array->itemCount += other->itemCount;
}
void WMAddToArray(WMArray * array, void *item)
{
if (array == NULL)
return;
if (array->itemCount >= array->allocSize) {
array->allocSize += RESIZE_INCREMENT;
array->items = wrealloc(array->items, sizeof(void *) * array->allocSize);
}
array->items[array->itemCount] = item;
array->itemCount++;
}
void WMInsertInArray(WMArray * array, int index, void *item)
{
if (array == NULL)
return;
wassertr(index >= 0 && index <= array->itemCount);
if (array->itemCount >= array->allocSize) {
array->allocSize += RESIZE_INCREMENT;
array->items = wrealloc(array->items, sizeof(void *) * array->allocSize);
}
if (index < array->itemCount) {
memmove(array->items + index + 1, array->items + index,
sizeof(void *) * (array->itemCount - index));
}
array->items[index] = item;
array->itemCount++;
}
void *WMReplaceInArray(WMArray * array, int index, void *item)
{
void *old;
if (array == NULL)
return NULL;
wassertrv(index >= 0 && index <= array->itemCount, NULL);
/* is it really useful to perform append if index == array->itemCount ? -Dan */
if (index == array->itemCount) {
WMAddToArray(array, item);
return NULL;
}
old = array->items[index];
array->items[index] = item;
return old;
}
int WMDeleteFromArray(WMArray * array, int index)
{
if (array == NULL)
return 0;
wassertrv(index >= 0 && index < array->itemCount, 0);
if (array->destructor) {
array->destructor(array->items[index]);
}
if (index < array->itemCount - 1) {
memmove(array->items + index, array->items + index + 1,
sizeof(void *) * (array->itemCount - index - 1));
}
array->itemCount--;
return 1;
}
int WMRemoveFromArrayMatching(WMArray * array, WMMatchDataProc * match, void *cdata)
{
int i;
if (array == NULL)
return 1;
if (match != NULL) {
for (i = 0; i < array->itemCount; i++) {
if ((*match) (array->items[i], cdata)) {
WMDeleteFromArray(array, i);
return 1;
}
}
} else {
for (i = 0; i < array->itemCount; i++) {
if (array->items[i] == cdata) {
WMDeleteFromArray(array, i);
return 1;
}
}
}
return 0;
}
void *WMGetFromArray(WMArray * array, int index)
{
if (index < 0 || array == NULL || index >= array->itemCount)
return NULL;
return array->items[index];
}
void *WMPopFromArray(WMArray * array)
{
if (array == NULL || array->itemCount <= 0)
return NULL;
array->itemCount--;
return array->items[array->itemCount];
}
int WMFindInArray(WMArray * array, WMMatchDataProc * match, void *cdata)
{
int i;
if (array == NULL)
return WANotFound;
if (match != NULL) {
for (i = 0; i < array->itemCount; i++) {
if ((*match) (array->items[i], cdata))
return i;
}
} else {
for (i = 0; i < array->itemCount; i++) {
if (array->items[i] == cdata)
return i;
}
}
return WANotFound;
}
int WMCountInArray(WMArray * array, void *item)
{
int i, count;
if (array == NULL)
return 0;
for (i = 0, count = 0; i < array->itemCount; i++) {
if (array->items[i] == item)
count++;
}
return count;
}
void WMSortArray(WMArray * array, WMCompareDataProc * comparer)
{
if (array == NULL)
return;
if (array->itemCount > 1) { /* Don't sort empty or single element arrays */
qsort(array->items, array->itemCount, sizeof(void *), comparer);
}
}
void WMMapArray(WMArray * array, void (*function) (void *, void *), void *data)
{
int i;
if (array == NULL)
return;
for (i = 0; i < array->itemCount; i++) {
(*function) (array->items[i], data);
}
}
WMArray *WMGetSubarrayWithRange(WMArray * array, WMRange aRange)
{
WMArray *newArray;
if (aRange.count <= 0 || array == NULL)
return WMCreateArray(0);
if (aRange.position < 0)
aRange.position = 0;
if (aRange.position >= array->itemCount)
aRange.position = array->itemCount - 1;
if (aRange.position + aRange.count > array->itemCount)
aRange.count = array->itemCount - aRange.position;
newArray = WMCreateArray(aRange.count);
memcpy(newArray->items, array->items + aRange.position, sizeof(void *) * aRange.count);
newArray->itemCount = aRange.count;
return newArray;
}
void *WMArrayFirst(WMArray * array, WMArrayIterator * iter)
{
if (array == NULL || array->itemCount == 0) {
*iter = WANotFound;
return NULL;
} else {
*iter = 0;
return array->items[0];
}
}
void *WMArrayLast(WMArray * array, WMArrayIterator * iter)
{
if (array == NULL || array->itemCount == 0) {
*iter = WANotFound;
return NULL;
} else {
*iter = array->itemCount - 1;
return array->items[*iter];
}
}
void *WMArrayNext(WMArray * array, WMArrayIterator * iter)
{
if (array == NULL) {
*iter = WANotFound;
return NULL;
}
if (*iter >= 0 && *iter < array->itemCount - 1) {
return array->items[++(*iter)];
} else {
*iter = WANotFound;
return NULL;
}
}
void *WMArrayPrevious(WMArray * array, WMArrayIterator * iter)
{
if (array == NULL) {
*iter = WANotFound;
return NULL;
}
if (*iter > 0 && *iter < array->itemCount) {
return array->items[--(*iter)];
} else {
*iter = WANotFound;
return NULL;
}
}

289
WINGs/data.c Normal file
View File

@@ -0,0 +1,289 @@
/*
* WINGs WMData function library
*
* Copyright (c) 1999-2003 Dan Pascu
*
* 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., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <string.h>
#include "WUtil.h"
typedef struct W_Data {
unsigned length; /* How many bytes we have */
unsigned capacity; /* How many bytes it can hold */
unsigned growth; /* How much to grow */
void *bytes; /* Actual data */
unsigned retainCount;
WMFreeDataProc *destructor;
int format; /* 0, 8, 16 or 32 */
} W_Data;
/* Creating and destroying data objects */
WMData *WMCreateDataWithCapacity(unsigned capacity)
{
WMData *aData;
aData = (WMData *) wmalloc(sizeof(WMData));
if (capacity > 0)
aData->bytes = wmalloc(capacity);
else
aData->bytes = NULL;
aData->capacity = capacity;
aData->growth = capacity / 2 > 0 ? capacity / 2 : 1;
aData->length = 0;
aData->retainCount = 1;
aData->format = 0;
aData->destructor = wfree;
return aData;
}
WMData *WMCreateDataWithLength(unsigned length)
{
WMData *aData;
aData = WMCreateDataWithCapacity(length);
if (length > 0) {
aData->length = length;
}
return aData;
}
WMData *WMCreateDataWithBytes(const void *bytes, unsigned length)
{
WMData *aData;
aData = WMCreateDataWithCapacity(length);
aData->length = length;
memcpy(aData->bytes, bytes, length);
return aData;
}
WMData *WMCreateDataWithBytesNoCopy(void *bytes, unsigned length, WMFreeDataProc * destructor)
{
WMData *aData;
aData = (WMData *) wmalloc(sizeof(WMData));
aData->length = length;
aData->capacity = length;
aData->growth = length / 2 > 0 ? length / 2 : 1;
aData->bytes = bytes;
aData->retainCount = 1;
aData->format = 0;
aData->destructor = destructor;
return aData;
}
WMData *WMCreateDataWithData(WMData * aData)
{
WMData *newData;
if (aData->length > 0) {
newData = WMCreateDataWithBytes(aData->bytes, aData->length);
} else {
newData = WMCreateDataWithCapacity(0);
}
newData->format = aData->format;
return newData;
}
WMData *WMRetainData(WMData * aData)
{
aData->retainCount++;
return aData;
}
void WMReleaseData(WMData * aData)
{
aData->retainCount--;
if (aData->retainCount > 0)
return;
if (aData->bytes != NULL && aData->destructor != NULL) {
aData->destructor(aData->bytes);
}
wfree(aData);
}
/* Adjusting capacity */
void WMSetDataCapacity(WMData * aData, unsigned capacity)
{
if (aData->capacity != capacity) {
aData->bytes = wrealloc(aData->bytes, capacity);
aData->capacity = capacity;
aData->growth = capacity / 2 > 0 ? capacity / 2 : 1;
}
if (aData->length > capacity) {
aData->length = capacity;
}
}
void WMSetDataLength(WMData * aData, unsigned length)
{
if (length > aData->capacity) {
WMSetDataCapacity(aData, length);
}
if (length > aData->length) {
memset((unsigned char *)aData->bytes + aData->length, 0, length - aData->length);
}
aData->length = length;
}
void WMSetDataFormat(WMData * aData, unsigned format)
{
aData->format = format;
}
void WMIncreaseDataLengthBy(WMData * aData, unsigned extraLength)
{
WMSetDataLength(aData, aData->length + extraLength);
}
/* Accessing data */
const void *WMDataBytes(WMData * aData)
{
return aData->bytes;
}
void WMGetDataBytes(WMData * aData, void *buffer)
{
wassertr(aData->length > 0);
memcpy(buffer, aData->bytes, aData->length);
}
unsigned WMGetDataFormat(WMData * aData)
{
return aData->format;
}
void WMGetDataBytesWithLength(WMData * aData, void *buffer, unsigned length)
{
wassertr(aData->length > 0);
wassertr(length <= aData->length);
memcpy(buffer, aData->bytes, length);
}
void WMGetDataBytesWithRange(WMData * aData, void *buffer, WMRange aRange)
{
wassertr(aRange.position < aData->length);
wassertr(aRange.count <= aData->length - aRange.position);
memcpy(buffer, (unsigned char *)aData->bytes + aRange.position, aRange.count);
}
WMData *WMGetSubdataWithRange(WMData * aData, WMRange aRange)
{
void *buffer;
WMData *newData;
if (aRange.count <= 0)
return WMCreateDataWithCapacity(0);
buffer = wmalloc(aRange.count);
WMGetDataBytesWithRange(aData, buffer, aRange);
newData = WMCreateDataWithBytesNoCopy(buffer, aRange.count, wfree);
newData->format = aData->format;
return newData;
}
/* Testing data */
Bool WMIsDataEqualToData(WMData * aData, WMData * anotherData)
{
if (aData->length != anotherData->length)
return False;
else if (!aData->bytes && !anotherData->bytes) /* both are empty */
return True;
else if (!aData->bytes || !anotherData->bytes) /* one of them is empty */
return False;
return (memcmp(aData->bytes, anotherData->bytes, aData->length) == 0);
}
unsigned WMGetDataLength(WMData * aData)
{
return aData->length;
}
/* Adding data */
void WMAppendDataBytes(WMData * aData, const void *bytes, unsigned length)
{
unsigned oldLength = aData->length;
unsigned newLength = oldLength + length;
if (newLength > aData->capacity) {
unsigned nextCapacity = aData->capacity + aData->growth;
unsigned nextGrowth = aData->capacity ? aData->capacity : 1;
while (nextCapacity < newLength) {
unsigned tmp = nextCapacity + nextGrowth;
nextGrowth = nextCapacity;
nextCapacity = tmp;
}
WMSetDataCapacity(aData, nextCapacity);
aData->growth = nextGrowth;
}
memcpy((unsigned char *)aData->bytes + oldLength, bytes, length);
aData->length = newLength;
}
void WMAppendData(WMData * aData, WMData * anotherData)
{
if (anotherData->length > 0)
WMAppendDataBytes(aData, anotherData->bytes, anotherData->length);
}
/* Modifying data */
void WMReplaceDataBytesInRange(WMData * aData, WMRange aRange, const void *bytes)
{
wassertr(aRange.position < aData->length);
wassertr(aRange.count <= aData->length - aRange.position);
memcpy((unsigned char *)aData->bytes + aRange.position, bytes, aRange.count);
}
void WMResetDataBytesInRange(WMData * aData, WMRange aRange)
{
wassertr(aRange.position < aData->length);
wassertr(aRange.count <= aData->length - aRange.position);
memset((unsigned char *)aData->bytes + aRange.position, 0, aRange.count);
}
void WMSetData(WMData * aData, WMData * anotherData)
{
unsigned length = anotherData->length;
WMSetDataCapacity(aData, length);
if (length > 0)
memcpy(aData->bytes, anotherData->bytes, length);
aData->length = length;
}
/* Storing data */

View File

@@ -34,6 +34,331 @@
#include <pwd.h>
#include <limits.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
const char *wgethomedir(void)
{
static char *home = NULL;
char *tmp;
struct passwd *user;
if (home)
return home;
tmp = GETENV("HOME");
if (tmp) {
home = wstrdup(tmp);
return home;
}
user = getpwuid(getuid());
if (!user) {
werror(_("could not get password entry for UID %i"), getuid());
home = "/";
return home;
}
if (!user->pw_dir)
home = "/";
else
home = wstrdup(user->pw_dir);
return home;
}
/*
* Return the home directory for the specified used
*
* If user not found, returns NULL, otherwise always returns a path that is
* statically stored.
*
* Please note you must use the path before any other call to 'getpw*' or it
* may be erased. This is a design choice to avoid duplication considering
* the use case for this function.
*/
static const char *getuserhomedir(const char *username)
{
static const char default_home[] = "/";
struct passwd *user;
user = getpwnam(username);
if (!user) {
werror(_("could not get password entry for user %s"), username);
return NULL;
}
if (!user->pw_dir)
return default_home;
else
return user->pw_dir;
}
char *wexpandpath(const char *path)
{
const char *origpath = path;
char buffer2[PATH_MAX + 2];
char buffer[PATH_MAX + 2];
int i;
memset(buffer, 0, PATH_MAX + 2);
if (*path == '~') {
const char *home;
path++;
if (*path == '/' || *path == 0) {
home = wgethomedir();
if (strlen(home) > PATH_MAX ||
wstrlcpy(buffer, home, sizeof(buffer)) >= sizeof(buffer))
goto error;
} else {
int j;
j = 0;
while (*path != 0 && *path != '/') {
if (j > PATH_MAX)
goto error;
buffer2[j++] = *path;
buffer2[j] = 0;
path++;
}
home = getuserhomedir(buffer2);
if (!home || wstrlcat(buffer, home, sizeof(buffer)) >= sizeof(buffer))
goto error;
}
}
i = strlen(buffer);
while (*path != 0 && i <= PATH_MAX) {
char *tmp;
if (*path == '$') {
int j;
path++;
/* expand $(HOME) or $HOME style environment variables */
if (*path == '(') {
path++;
j = 0;
while (*path != 0 && *path != ')') {
if (j > PATH_MAX)
goto error;
buffer2[j++] = *(path++);
}
buffer2[j] = 0;
if (*path == ')') {
path++;
tmp = getenv(buffer2);
} else {
tmp = NULL;
}
if (!tmp) {
if ((i += strlen(buffer2) + 2) > PATH_MAX)
goto error;
buffer[i] = 0;
if (wstrlcat(buffer, "$(", sizeof(buffer)) >= sizeof(buffer) ||
wstrlcat(buffer, buffer2, sizeof(buffer)) >= sizeof(buffer))
goto error;
if (*(path-1)==')') {
if (++i > PATH_MAX ||
wstrlcat(buffer, ")", sizeof(buffer)) >= sizeof(buffer))
goto error;
}
} else {
if ((i += strlen(tmp)) > PATH_MAX ||
wstrlcat(buffer, tmp, sizeof(buffer)) >= sizeof(buffer))
goto error;
}
} else {
j = 0;
while (*path != 0 && *path != '/') {
if (j > PATH_MAX)
goto error;
buffer2[j++] = *(path++);
}
buffer2[j] = 0;
tmp = getenv(buffer2);
if (!tmp) {
if ((i += strlen(buffer2) + 1) > PATH_MAX ||
wstrlcat(buffer, "$", sizeof(buffer)) >= sizeof(buffer) ||
wstrlcat(buffer, buffer2, sizeof(buffer)) >= sizeof(buffer))
goto error;
} else {
if ((i += strlen(tmp)) > PATH_MAX ||
wstrlcat(buffer, tmp, sizeof(buffer)) >= sizeof(buffer))
goto error;
}
}
} else {
buffer[i++] = *path;
path++;
}
}
if (*path!=0)
goto error;
return wstrdup(buffer);
error:
errno = ENAMETOOLONG;
werror(_("could not expand %s"), origpath);
return NULL;
}
/* return address of next char != tok or end of string whichever comes first */
static const char *skipchar(const char *string, char tok)
{
while (*string != 0 && *string == tok)
string++;
return string;
}
/* return address of next char == tok or end of string whichever comes first */
static const char *nextchar(const char *string, char tok)
{
while (*string != 0 && *string != tok)
string++;
return string;
}
/*
*----------------------------------------------------------------------
* findfile--
* Finds a file in a : separated list of paths. ~ expansion is also
* done.
*
* Returns:
* The complete path for the file (in a newly allocated string) or
* NULL if the file was not found.
*
* Side effects:
* A new string is allocated. It must be freed later.
*
*----------------------------------------------------------------------
*/
char *wfindfile(const char *paths, const char *file)
{
char *path;
const char *tmp, *tmp2;
int len, flen;
char *fullpath;
if (!file)
return NULL;
if (*file == '/' || *file == '~' || *file == '$' || !paths || *paths == 0) {
if (access(file, F_OK) < 0) {
fullpath = wexpandpath(file);
if (!fullpath)
return NULL;
if (access(fullpath, F_OK) < 0) {
wfree(fullpath);
return NULL;
} else {
return fullpath;
}
} else {
return wstrdup(file);
}
}
flen = strlen(file);
tmp = paths;
while (*tmp) {
tmp = skipchar(tmp, ':');
if (*tmp == 0)
break;
tmp2 = nextchar(tmp, ':');
len = tmp2 - tmp;
path = wmalloc(len + flen + 2);
path = memcpy(path, tmp, len);
path[len] = 0;
if (path[len - 1] != '/' &&
wstrlcat(path, "/", len + flen + 2) >= len + flen + 2) {
wfree(path);
return NULL;
}
if (wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
wfree(path);
return NULL;
}
fullpath = wexpandpath(path);
wfree(path);
if (fullpath) {
if (access(fullpath, F_OK) == 0) {
return fullpath;
}
wfree(fullpath);
}
tmp = tmp2;
}
return NULL;
}
char *wfindfileinlist(char *const *path_list, const char *file)
{
int i;
char *path;
int len, flen;
char *fullpath;
if (!file)
return NULL;
if (*file == '/' || *file == '~' || !path_list) {
if (access(file, F_OK) < 0) {
fullpath = wexpandpath(file);
if (!fullpath)
return NULL;
if (access(fullpath, F_OK) < 0) {
wfree(fullpath);
return NULL;
} else {
return fullpath;
}
} else {
return wstrdup(file);
}
}
flen = strlen(file);
for (i = 0; path_list[i] != NULL; i++) {
len = strlen(path_list[i]);
path = wmalloc(len + flen + 2);
path = memcpy(path, path_list[i], len);
path[len] = 0;
if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
wfree(path);
return NULL;
}
/* expand tilde */
fullpath = wexpandpath(path);
wfree(path);
if (fullpath) {
/* check if file exists */
if (access(fullpath, F_OK) == 0) {
return fullpath;
}
wfree(fullpath);
}
}
return NULL;
}
char *wfindfileinarray(WMPropList *array, const char *file)
{
@@ -94,3 +419,105 @@ char *wfindfileinarray(WMPropList *array, const char *file)
}
return NULL;
}
int wcopy_file(const char *dest_dir, const char *src_file, const char *dest_file)
{
char *path_dst;
int fd_src, fd_dst;
struct stat stat_src;
mode_t permission_dst;
const size_t buffer_size = 2 * 1024 * 1024; /* 4MB is a decent start choice to allow the OS to take advantage of modern disk's performance */
char *buffer; /* The buffer is not created on the stack to avoid possible stack overflow as our buffer is big */
try_again_src:
fd_src = open(src_file, O_RDONLY | O_NOFOLLOW);
if (fd_src == -1) {
if (errno == EINTR)
goto try_again_src;
werror(_("Could not open input file \"%s\": %s"), src_file, strerror(errno));
return -1;
}
/* Only accept to copy regular files */
if (fstat(fd_src, &stat_src) != 0 || !S_ISREG(stat_src.st_mode)) {
close(fd_src);
return -1;
}
path_dst = wstrconcat(dest_dir, dest_file);
try_again_dst:
fd_dst = open(path_dst, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd_dst == -1) {
if (errno == EINTR)
goto try_again_dst;
werror(_("Could not create target file \"%s\": %s"), path_dst, strerror(errno));
wfree(path_dst);
close(fd_src);
return -1;
}
buffer = malloc(buffer_size); /* Don't use wmalloc to avoid the memset(0) we don't need */
if (buffer == NULL) {
werror(_("could not allocate memory for the copy buffer"));
close(fd_dst);
goto cleanup_and_return_failure;
}
for (;;) {
ssize_t size_data;
const char *write_ptr;
size_t write_remain;
try_again_read:
size_data = read(fd_src, buffer, buffer_size);
if (size_data == 0)
break; /* End of File have been reached */
if (size_data < 0) {
if (errno == EINTR)
goto try_again_read;
werror(_("could not read from file \"%s\": %s"), src_file, strerror(errno));
close(fd_dst);
goto cleanup_and_return_failure;
}
write_ptr = buffer;
write_remain = size_data;
while (write_remain > 0) {
ssize_t write_done;
try_again_write:
write_done = write(fd_dst, write_ptr, write_remain);
if (write_done < 0) {
if (errno == EINTR)
goto try_again_write;
werror(_("could not write data to file \"%s\": %s"), path_dst, strerror(errno));
close(fd_dst);
goto cleanup_and_return_failure;
}
write_ptr += write_done;
write_remain -= write_done;
}
}
/* Keep only the permission-related part of the field: */
permission_dst = stat_src.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
if (fchmod(fd_dst, permission_dst) != 0)
wwarning(_("could not set permission 0%03o on file \"%s\": %s"),
permission_dst, path_dst, strerror(errno));
if (close(fd_dst) != 0) {
werror(_("could not close the file \"%s\": %s"), path_dst, strerror(errno));
cleanup_and_return_failure:
free(buffer);
close(fd_src);
unlink(path_dst);
wfree(path_dst);
return -1;
}
free(buffer);
wfree(path_dst);
close(fd_src);
return 0;
}

View File

@@ -279,7 +279,7 @@ Bool W_CheckIdleHandlers(void)
return (idleHandler != NULL && WMGetArrayItemCount(idleHandler) > 0);
}
handlerCopy = WMCreateArrayWithArray(idleHandler);
handlerCopy = WMDuplicateArray(idleHandler);
WM_ITERATE_ARRAY(handlerCopy, handler, iter) {
/* check if the handler still exist or was removed by a callback */
@@ -429,7 +429,7 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
count = poll(fds, nfds + extrafd, timeout);
if (count > 0 && nfds > 0) {
WMArray *handlerCopy = WMCreateArrayWithArray(inputHandler);
WMArray *handlerCopy = WMDuplicateArray(inputHandler);
int mask;
/* use WM_ITERATE_ARRAY() here */
@@ -527,7 +527,7 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
if (count > 0 && nfds > 0) {
WMArray *handlerCopy = WMCreateArrayWithArray(inputHandler);
WMArray *handlerCopy = WMDuplicateArray(inputHandler);
int mask;
/* use WM_ITERATE_ARRAY() here */

223
WINGs/memory.c Normal file
View File

@@ -0,0 +1,223 @@
/*
* Window Maker miscelaneous function library
*
* Copyright (c) 1997-2003 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., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "wconfig.h"
#include "WUtil.h"
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#ifdef HAVE_STDNORETURN
#include <stdnoreturn.h>
#endif
#ifdef USE_BOEHM_GC
#ifndef GC_DEBUG
#define GC_DEBUG
#endif /* !GC_DEBUG */
#include <gc/gc.h>
#endif /* USE_BOEHM_GC */
#ifndef False
# define False 0
#endif
#ifndef True
# define True 1
#endif
static void defaultHandler(int bla)
{
if (bla)
kill(getpid(), SIGABRT);
else
exit(1);
}
static waborthandler *aborthandler = defaultHandler;
static inline noreturn void wAbort(int bla)
{
(*aborthandler)(bla);
exit(-1);
}
waborthandler *wsetabort(waborthandler * handler)
{
waborthandler *old = aborthandler;
aborthandler = handler;
return old;
}
static int Aborting = 0; /* if we're in the middle of an emergency exit */
static WMHashTable *table = NULL;
void *wmalloc(size_t size)
{
void *tmp;
assert(size > 0);
#ifdef USE_BOEHM_GC
tmp = GC_MALLOC(size);
#else
tmp = malloc(size);
#endif
if (tmp == NULL) {
wwarning("malloc() failed. Retrying after 2s.");
sleep(2);
#ifdef USE_BOEHM_GC
tmp = GC_MALLOC(size);
#else
tmp = malloc(size);
#endif
if (tmp == NULL) {
if (Aborting) {
fputs("Really Bad Error: recursive malloc() failure.", stderr);
exit(-1);
} else {
wfatal("virtual memory exhausted");
Aborting = 1;
wAbort(False);
}
}
}
if (tmp != NULL)
memset(tmp, 0, size);
return tmp;
}
void *wrealloc(void *ptr, size_t newsize)
{
void *nptr;
if (!ptr) {
nptr = wmalloc(newsize);
} else if (newsize == 0) {
wfree(ptr);
nptr = NULL;
} else {
#ifdef USE_BOEHM_GC
nptr = GC_REALLOC(ptr, newsize);
#else
nptr = realloc(ptr, newsize);
#endif
if (nptr == NULL) {
wwarning("realloc() failed. Retrying after 2s.");
sleep(2);
#ifdef USE_BOEHM_GC
nptr = GC_REALLOC(ptr, newsize);
#else
nptr = realloc(ptr, newsize);
#endif
if (nptr == NULL) {
if (Aborting) {
fputs("Really Bad Error: recursive realloc() failure.", stderr);
exit(-1);
} else {
wfatal("virtual memory exhausted");
Aborting = 1;
wAbort(False);
}
}
}
}
return nptr;
}
void *wretain(void *ptr)
{
int *refcount;
if (!table) {
table = WMCreateHashTable(WMIntHashCallbacks);
}
refcount = WMHashGet(table, ptr);
if (!refcount) {
refcount = wmalloc(sizeof(int));
*refcount = 1;
WMHashInsert(table, ptr, refcount);
#ifdef VERBOSE
printf("== %i (%p)\n", *refcount, ptr);
#endif
} else {
(*refcount)++;
#ifdef VERBOSE
printf("+ %i (%p)\n", *refcount, ptr);
#endif
}
return ptr;
}
void wfree(void *ptr)
{
if (ptr)
#ifdef USE_BOEHM_GC
/* This should eventually be removed, once the criss-cross
* of wmalloc()d memory being free()d, malloc()d memory being
* wfree()d, various misuses of calling wfree() on objects
* allocated by libc malloc() and calling libc free() on
* objects allocated by Boehm GC (think external libraries)
* is cleaned up.
*/
if (GC_base(ptr) != 0)
GC_FREE(ptr);
else
free(ptr);
#else
free(ptr);
#endif
ptr = NULL;
}
void wrelease(void *ptr)
{
int *refcount;
refcount = WMHashGet(table, ptr);
if (!refcount) {
wwarning("trying to release unexisting data %p", ptr);
} else {
(*refcount)--;
if (*refcount < 1) {
#ifdef VERBOSE
printf("RELEASING %p\n", ptr);
#endif
WMHashRemove(table, ptr);
wfree(refcount);
wfree(ptr);
}
#ifdef VERBOSE
else {
printf("- %i (%p)\n", *refcount, ptr);
}
#endif
}
}

View File

@@ -536,14 +536,12 @@ found_end_define_fname:
while (*src != '\0') {
idx = 0;
if (*src == '~') {
char *home_head = wgethomedir();
char *home = home_head;;
const char *home = wgethomedir();
while (*home != '\0') {
if (idx < sizeof(buffer) - 2)
buffer[idx++] = *home;
home++;
}
wfree(home_head);
src++;
}

View File

@@ -942,6 +942,20 @@ WMPropList *WMCreatePLDataWithBytes(const unsigned char *bytes, unsigned int len
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;

View File

@@ -237,7 +237,7 @@ static void handleRequestEvent(XEvent * event)
}
/* delete handlers */
copy = WMCreateArrayWithArray(selHandlers);
copy = WMDuplicateArray(selHandlers);
WM_ITERATE_ARRAY(copy, handler, iter) {
if (handler && handler->flags.delete_pending) {
WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp);
@@ -261,9 +261,8 @@ static WMData *getSelectionData(Display * dpy, Window win, Atom where)
bpi = bits / 8;
wdata = WMCreateDataWithBytes(data, len * bpi);
wdata = WMCreateDataWithBytesNoCopy(data, len * bpi, (void *) XFree);
WMSetDataFormat(wdata, bits);
XFree(data);
return wdata;
}
@@ -301,7 +300,7 @@ static void handleNotifyEvent(XEvent * event)
}
/* delete callbacks */
copy = WMCreateArrayWithArray(selCallbacks);
copy = WMDuplicateArray(selCallbacks);
WM_ITERATE_ARRAY(copy, handler, iter) {
if (handler && handler->flags.delete_pending) {
WMDeleteSelectionCallback(handler->view, handler->selection, handler->timestamp);

View File

@@ -51,7 +51,7 @@ const char *wusergnusteppath(void)
static const char subdir[] = "/" GSUSER_SUBDIR;
static char *path = NULL;
char *gspath;
char *h;
const char *h;
int pathlen;
if (path)
@@ -69,12 +69,13 @@ const char *wusergnusteppath(void)
}
h = wgethomedir();
if (!h)
return NULL;
pathlen = strlen(h);
path = wmalloc(pathlen + sizeof(subdir));
strcpy(path, h);
strcpy(path + pathlen, subdir);
wfree(h);
return path;
}

View File

@@ -2994,13 +2994,10 @@ static void customPaletteMenuNewFromFile(W_ColorPanel * panel)
int i;
RImage *tmpImg = NULL;
if ((!panel->lastBrowseDir) || (strcmp(panel->lastBrowseDir, "\0") == 0)) {
char *homedir = wgethomedir();
spath = wexpandpath(homedir);
wfree(homedir);
} else {
if ((!panel->lastBrowseDir) || (strcmp(panel->lastBrowseDir, "\0") == 0))
spath = wexpandpath(wgethomedir());
else
spath = wexpandpath(panel->lastBrowseDir);
}
browseP = WMGetOpenPanel(scr);
WMSetFilePanelCanChooseDirectories(browseP, 0);

View File

@@ -761,15 +761,17 @@ static void goFloppy(WMWidget *widget, void *p_panel)
static void goHome(WMWidget *widget, void *p_panel)
{
WMFilePanel *panel = p_panel;
char *home;
const char *home;
/* Parameter not used, but tell the compiler that it is ok */
(void) widget;
/* home is statically allocated. Don't free it! */
home = wgethomedir();
if (!home)
return;
WMSetFilePanelDirectory(panel, home);
wfree(home);
}
static void handleEvents(XEvent * event, void *data)

View File

@@ -51,15 +51,12 @@ static char *xlfdToFcName(const char *xlfd)
{
FcPattern *pattern;
char *fname;
char *result;
pattern = xlfdToFcPattern(xlfd);
fname = (char *)FcNameUnparse(pattern);
result = wstrdup(fname);
free(fname);
FcPatternDestroy(pattern);
return result;
return fname;
}
static Bool hasProperty(FcPattern * pattern, const char *property)
@@ -95,7 +92,6 @@ static Bool hasPropertyWithStringValue(FcPattern * pattern, const char *object,
static char *makeFontOfSize(const char *font, int size, const char *fallback)
{
FcPattern *pattern;
char *name;
char *result;
if (font[0] == '-') {
@@ -119,9 +115,7 @@ static char *makeFontOfSize(const char *font, int size, const char *fallback)
/*FcPatternPrint(pattern); */
name = (char *)FcNameUnparse(pattern);
result = wstrdup(name);
free(name);
result = (char *)FcNameUnparse(pattern);
FcPatternDestroy(pattern);
return result;
@@ -427,7 +421,7 @@ WMFont *WMCopyFontWithStyle(WMScreen * scrPtr, WMFont * font, WMFontStyle style)
name = (char *)FcNameUnparse(pattern);
copy = WMCreateFont(scrPtr, name);
FcPatternDestroy(pattern);
free(name);
wfree(name);
return copy;
}

View File

@@ -1110,7 +1110,7 @@ static void deleteTexture(WMWidget * w, void *data)
static void extractTexture(WMWidget * w, void *data)
{
_Panel *panel = (_Panel *) data;
char *path, *homedir;
char *path;
WMOpenPanel *opanel;
WMScreen *scr = WMWidgetScreen(w);
@@ -1118,17 +1118,13 @@ static void extractTexture(WMWidget * w, void *data)
WMSetFilePanelCanChooseDirectories(opanel, False);
WMSetFilePanelCanChooseFiles(opanel, True);
homedir = wgethomedir();
if (WMRunModalFilePanelForDirectory(opanel, panel->parent, homedir, _("Select File"), NULL)) {
if (WMRunModalFilePanelForDirectory(opanel, panel->parent, wgethomedir(), _("Select File"), NULL)) {
path = WMGetFilePanelFileName(opanel);
OpenExtractPanelFor(panel);
wfree(path);
}
if (homedir) {
wfree(homedir);
}
}
static void changePage(WMWidget * w, void *data)

View File

@@ -288,7 +288,6 @@ static char *getSelectedFont(_Panel * panel, FcChar8 * curfont)
WMListItem *item;
FcPattern *pat;
char *name;
char *result;
if (curfont)
pat = FcNameParse(curfont);
@@ -322,12 +321,9 @@ static char *getSelectedFont(_Panel * panel, FcChar8 * curfont)
}
name = (char *)FcNameUnparse(pat);
result = wstrdup(name);
free(name);
FcPatternDestroy(pat);
return result;
return name;
}
static void updateSampleFont(_Panel * panel)

View File

@@ -66,7 +66,6 @@ AM_CPPFLAGS = -DRESOURCE_PATH=\"$(wpdatadir)\" -DWMAKER_RESOURCE_PATH=\"$(pkgdat
WPrefs_DEPENDENCIES = $(top_builddir)/WINGs/libWINGs.la
WPrefs_LDADD = \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a\
$(top_builddir)/WINGs/libWINGs.la\
$(top_builddir)/WINGs/libWUtil.la\
$(top_builddir)/wrlib/libwraster.la \

View File

@@ -627,7 +627,7 @@ static void browseImageCallback(WMWidget *w, void *data)
WMSetFilePanelCanChooseFiles(opanel, True);
if (!ipath)
ipath = wgethomedir();
ipath = wstrdup(wgethomedir());
if (WMRunModalFilePanelForDirectory(opanel, panel->win, ipath, _("Open Image"), NULL)) {
char *path, *fullpath;

View File

@@ -87,6 +87,8 @@ int main(int argc, char **argv)
int i;
char *display_name = "";
wsetabort(wAbort);
memset(DeadHandlers, 0, sizeof(DeadHandlers));
WMInitializeApplication("WPrefs", &argc, argv);

View File

@@ -49,17 +49,6 @@ AC_CONFIG_SRCDIR([src/WindowMaker.h])
dnl Include at the end of 'config.h', this file is generated by top-level Makefile
AH_BOTTOM([@%:@include "config-paths.h"])
dnl Rust support
AC_CHECK_PROG(CARGO, [cargo], [yes], [no])
AS_IF(test x$CARGO = xno,
AC_MSG_ERROR([cargo is required. Please set the CARGO environment variable or install the Rust toolchain from https://www.rust-lang.org/])
)
AC_SUBST(CARGO, [cargo])
AC_CHECK_PROG(RUSTC, [rustc], [yes], [no])
AS_IF(test x$RUSTC = xno,
AC_MSG_ERROR([rustc is required. Please set the RUSTC environment variable or install the Rust toolchain from https://www.rust-lang.org/])
)
AC_SUBST(RUSTC, [rustc])
dnl libtool library versioning
dnl ==========================
@@ -353,6 +342,24 @@ AS_IF([test "x$enable_mwm_hints" = "xno"],
AM_CONDITIONAL([USE_MWM_HINTS], [test "x$enable_mwm_hints" != "xno"])
dnl Boehm GC
dnl ========
m4_divert_push([INIT_PREPARE])dnl
AC_ARG_ENABLE([boehm-gc],
[AS_HELP_STRING([--enable-boehm-gc], [use Boehm GC instead of the default libc malloc() [default=no]])],
[AS_CASE(["$enableval"],
[yes], [with_boehm_gc=yes],
[no], [with_boehm_gc=no],
[AC_MSG_ERROR([bad value $enableval for --enable-boehm-gc])] )],
[with_boehm_gc=no])
m4_divert_pop([INIT_PREPARE])dnl
AS_IF([test "x$with_boehm_gc" = "xyes"],
AC_SEARCH_LIBS([GC_malloc], [gc],
[AC_DEFINE(USE_BOEHM_GC, 1, [Define if Boehm GC is to be used])],
[AC_MSG_FAILURE([--enable-boehm-gc specified but test for libgc failed])]))
dnl LCOV
dnl ====
m4_divert_push([INIT_PREPARE])dnl
@@ -957,17 +964,11 @@ AC_CONFIG_FILES(
wrlib/Makefile wrlib/po/Makefile
wrlib/tests/Makefile
dnl Rust implementation of WINGs libraries
wutil-rs/Makefile
dnl WINGs toolkit
WINGs/Makefile WINGs/WINGs/Makefile WINGs/po/Makefile
WINGs/Documentation/Makefile WINGs/Resources/Makefile WINGs/Extras/Makefile
WINGs/Examples/Makefile WINGs/Tests/Makefile
dnl Rust implementation of Window Maker core
wmaker-rs/Makefile
dnl Window Maker's core
src/Makefile src/wconfig.h po/Makefile
doc/Makefile doc/build/Makefile

View File

@@ -253,6 +253,14 @@ If found, then the library @emph{WRaster} can use the @emph{ImageMagick} library
@sc{Window Maker} support more image formats, like @emph{SVG}, @emph{BMP}, @emph{TGA}, ...
You can get it from @uref{http://www.imagemagick.org/}
@item @emph{Boehm GC}
This library can be used by the @emph{WINGs} utility toolkit to use a
@cite{Boehm-Demers-Weiser Garbage Collector} instead of the traditional
@command{malloc}/@command{free} functions from the @emph{libc}.
You have to explicitly ask for its support though (@pxref{Configure Options}).
You can get it from @uref{http://www.hboehm.info/gc/}
@end itemize
@@ -460,6 +468,8 @@ You can find more information about the libraries in the
@ref{Optional Dependencies}.
@table @option
@item --enable-boehm-gc
Never enabled by default, use Boehm GC instead of the default @emph{libc} @command{malloc()}
@item --disable-gif
Disable GIF support in @emph{WRaster} library; when enabled use @file{libgif} or @file{libungif}.

View File

@@ -6,7 +6,13 @@ bin_PROGRAMS = wmaker
EXTRA_DIST =
wmakerlib = $(top_builddir)/wmakerlib/target/debug/libwmakerlib.a
$(wmakerlib):
@(cd $(top_builddir)/wmakerlib && cargo build)
wmaker_SOURCES = \
$(wmakerlib) \
GNUstep.h \
WindowMaker.h \
actions.c \
@@ -134,7 +140,7 @@ else
nodist_wmaker_SOURCES = misc.hack_nf.c \
xmodifier.hack_nf.c
CLEANFILES = $(nodist_wmaker_SOURCES) ../wmaker-rs/target
CLEANFILES = $(nodist_wmaker_SOURCES) ../wmakerlib/target
misc.hack_nf.c: misc.c $(top_srcdir)/script/nested-func-to-macro.sh
$(AM_V_GEN)$(top_srcdir)/script/nested-func-to-macro.sh \
@@ -160,7 +166,7 @@ wmaker_LDADD = \
$(top_builddir)/WINGs/libWINGs.la\
$(top_builddir)/WINGs/libWUtil.la\
$(top_builddir)/wrlib/libwraster.la\
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
$(wmakerlib)\
@XLFLAGS@ \
@LIBXRANDR@ \
@LIBXINERAMA@ \

View File

@@ -75,24 +75,24 @@ struct WAppIcon {
Window main_window;
struct WDock *dock; /* In which dock is docked. */
struct _AppSettingsPanel *panel; /* Settings Panel */
unsigned int docked:1;
unsigned int omnipresent:1; /* If omnipresent when
unsigned int docked;
unsigned int omnipresent; /* If omnipresent when
* docked in clip */
unsigned int attracted:1; /* If it was attracted by the clip */
unsigned int launching:1;
unsigned int running:1; /* application is already running */
unsigned int relaunching:1; /* launching 2nd instance */
unsigned int forced_dock:1;
unsigned int auto_launch:1; /* launch app on startup */
unsigned int remote_start:1;
unsigned int updated:1;
unsigned int editing:1; /* editing docked icon */
unsigned int drop_launch:1; /* launching from drop action */
unsigned int paste_launch:1; /* launching from paste action */
unsigned int destroyed:1; /* appicon was destroyed */
unsigned int buggy_app:1; /* do not make dock rely on hints
unsigned int attracted; /* If it was attracted by the clip */
unsigned int launching;
unsigned int running; /* application is already running */
unsigned int relaunching; /* launching 2nd instance */
unsigned int forced_dock;
unsigned int auto_launch; /* launch app on startup */
unsigned int remote_start;
unsigned int updated;
unsigned int editing; /* editing docked icon */
unsigned int drop_launch; /* launching from drop action */
unsigned int paste_launch; /* launching from paste action */
unsigned int destroyed; /* appicon was destroyed */
unsigned int buggy_app; /* do not make dock rely on hints
* set by app */
unsigned int lock:1; /* do not allow to be destroyed */
unsigned int lock; /* do not allow to be destroyed */
};
/******** Accessors/mutators ********/

View File

@@ -367,7 +367,7 @@ static void handleHistoryKeyPress(XEvent * event, void *clientData)
case XK_Up:
if (p->histpos < WMGetArrayItemCount(p->history) - 1) {
if (p->histpos == 0)
wfree(WMSetInArray(p->history, 0, WMGetTextFieldText(p->panel->text)));
wfree(WMReplaceInArray(p->history, 0, WMGetTextFieldText(p->panel->text)));
p->histpos++;
WMSetTextFieldText(p->panel->text, WMGetFromArray(p->history, p->histpos));
}
@@ -468,7 +468,7 @@ int wAdvancedInputDialog(WScreen *scr, const char *title, const char *message, c
if (p->panel->result == WAPRDefault) {
result = WMGetTextFieldText(p->panel->text);
wfree(WMSetInArray(p->history, 0, wstrdup(result)));
wfree(WMReplaceInArray(p->history, 0, wstrdup(result)));
SaveHistory(p->history, filename);
} else
result = NULL;

View File

@@ -1796,7 +1796,7 @@ static void handleKeyPress(XEvent * event)
}
if (wwin->flags.selected && scr->selected_windows) {
scr->shortcutWindows[widx] = WMCreateArrayWithArray(scr->selected_windows);
scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
/*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
} else {
@@ -1816,7 +1816,7 @@ static void handleKeyPress(XEvent * event)
if (scr->shortcutWindows[widx]) {
WMFreeArray(scr->shortcutWindows[widx]);
}
scr->shortcutWindows[widx] = WMCreateArrayWithArray(scr->selected_windows);
scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
}
}

View File

@@ -624,6 +624,7 @@ static int real_main(int argc, char **argv)
int d, s;
setlocale(LC_ALL, "");
wsetabort(wAbort);
/* for telling WPrefs what's the name of the wmaker binary being ran */
setenv("WMAKER_BIN_NAME", argv[0], 1);

View File

@@ -132,7 +132,7 @@ static void changeImage(WSwitchPanel *panel, int idecks, int selected, Bool dim,
if (flags == desired && !force)
return;
WMSetInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
WMReplaceInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
if (!panel->bg && !panel->tile && !selected)
WMSetFrameRelief(icon, WRFlat);

View File

@@ -346,7 +346,7 @@ static void makeShortcutCommand(WMenu * menu, WMenuEntry * entry)
}
if (wwin->flags.selected && scr->selected_windows) {
scr->shortcutWindows[index] = WMCreateArrayWithArray(scr->selected_windows);
scr->shortcutWindows[index] = WMDuplicateArray(scr->selected_windows);
/*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
} else {

View File

@@ -18,50 +18,31 @@ AM_CPPFLAGS = \
liblist= @LIBRARY_SEARCH_PATH@ @INTLIBS@
wdwrite_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wdwrite_LDADD = $(top_builddir)/WINGs/libWUtil.la $(liblist)
wdread_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wdread_LDADD = $(top_builddir)/WINGs/libWUtil.la $(liblist)
wxcopy_LDADD = @XLFLAGS@ @XLIBS@
wxpaste_LDADD = @XLFLAGS@ @XLIBS@
getstyle_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
getstyle_LDADD = $(top_builddir)/WINGs/libWUtil.la $(liblist)
getstyle_SOURCES = getstyle.c fontconv.c common.h
setstyle_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
@XLFLAGS@ @XLIBS@ $(liblist)
setstyle_SOURCES = setstyle.c fontconv.c common.h
convertfonts_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
convertfonts_LDADD = $(top_builddir)/WINGs/libWUtil.la $(liblist)
convertfonts_SOURCES = convertfonts.c fontconv.c common.h
seticons_LDADD= \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
seticons_LDADD= $(top_builddir)/WINGs/libWUtil.la $(liblist)
geticonset_LDADD= \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
geticonset_LDADD= $(top_builddir)/WINGs/libWUtil.la $(liblist)
wmagnify_LDADD = \
$(top_builddir)/WINGs/libWINGs.la \
@@ -71,21 +52,18 @@ wmagnify_LDADD = \
wmsetbg_LDADD = \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wrlib/libwraster.la \
@XLFLAGS@ @LIBXINERAMA@ @XLIBS@ @INTLIBS@
wmgenmenu_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
@INTLIBS@
wmgenmenu_SOURCES = wmgenmenu.c wmgenmenu.h
wmmenugen_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
@INTLIBS@
wmmenugen_SOURCES = wmmenugen.c wmmenugen.h wmmenugen_misc.c \
@@ -97,7 +75,6 @@ wmiv_CFLAGS = @PANGO_CFLAGS@ @PTHREAD_CFLAGS@
wmiv_LDADD = \
$(top_builddir)/wrlib/libwraster.la \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
@XLFLAGS@ @XLIBS@ @GFXLIBS@ \
@PANGO_LIBS@ @PTHREAD_LIBS@ @LIBEXIF@

View File

@@ -465,7 +465,7 @@ int main(int argc, char **argv)
}
buf[strlen(buf) - 6 /* strlen("/style") */] = '\0';
homedir = wgethomedir();
homedir = wstrdup(wgethomedir());
if (strlen(homedir) > 1 && /* this is insane, wgethomedir() returns `/' on error */
strncmp(homedir, buf, strlen(homedir)) == 0) {
/* theme pack is under ${HOME}; exchange ${HOME} part

View File

@@ -1,33 +0,0 @@
# automake input file for wmaker-rs
AUTOMAKE_OPTIONS =
RUST_SOURCES = \
src/app_icon.rs \
src/application.rs \
src/app_menu.rs \
src/defaults.rs \
src/dock.rs \
src/global.rs \
src/icon.rs \
src/lib.rs \
src/menu.rs \
src/properties.rs \
src/screen.rs \
src/window.rs \
src/wings.rs \
src/wrlib.rs
RUST_EXTRA = \
Cargo.lock
target/debug/libwmaker_rs.a: $(RUST_SOURCES) $(RUST_EXTRA)
$(CARGO) build
check-local:
$(CARGO) test
clean-local:
$(CARGO) clean
all: target/debug/libwmaker_rs.a

View File

@@ -1,25 +0,0 @@
use std::ffi::c_void;
pub type AppIcon = c_void;
pub mod ffi {
use super::AppIcon;
use std::ffi::c_int;
unsafe extern "C" {
pub fn wAppIconIsDocked(app_icon: *mut AppIcon) -> c_int;
pub fn wAppIconIsRelaunching(app_icon: *mut AppIcon) -> c_int;
pub fn wAppIconGetIcon(app_icon: *mut AppIcon) -> *mut crate::icon::Icon;
pub fn wAppIconPaint(app_icon: *mut AppIcon);
pub fn create_appicon_for_application(
app: *mut crate::application::Application,
wwin: *mut crate::window::Window,
);
pub fn removeAppIconFor(app: *mut crate::application::Application);
}
}

View File

@@ -1,5 +1,5 @@
[package]
name = "wmaker-rs"
name = "wmakerlib"
version = "0.1.0"
edition = "2024"
@@ -7,4 +7,5 @@ edition = "2024"
crate-type = ["staticlib"]
[dependencies]
libc = "0.2"
x11 = "2.21.0"

59
wmakerlib/src/app_icon.rs Normal file
View File

@@ -0,0 +1,59 @@
use std::ffi::{c_char, c_int, c_short};
#[repr(C)]
pub struct AppIcon {
xindex: c_short,
yindex: c_short,
next: *mut AppIcon,
prev: *mut AppIcon,
icon: *mut crate::icon::Icon,
x_pos: c_int,
y_pos: c_int,
command: *mut c_char,
dnd_command: *mut c_char,
paste_command: *mut c_char,
wm_class: *mut c_char,
wm_instance: *mut c_char,
pid: libc::pid_t,
main_window: x11::xlib::Window,
dock: *mut crate::dock::Dock,
panel: *mut crate::app_settings_panel::AppSettingsPanel,
docked: c_int,
omnipresent: c_int,
attracted: c_int,
launched: c_int,
running: c_int,
relaunching: c_int,
forced_dock: c_int,
auto_launch: c_int,
remote_start: c_int,
updated: c_int,
editing: c_int,
drop_launch: c_int,
paste_launch: c_int,
destroyed: c_int,
buggy_app: c_int,
lock: c_int,
}
pub mod ffi {
use super::AppIcon;
use std::ffi::c_int;
unsafe extern "C" {
pub fn wAppIconIsDocked(app_icon: *mut AppIcon) -> c_int;
pub fn wAppIconIsRelaunching(app_icon: *mut AppIcon) -> c_int;
pub fn wAppIconGetIcon(app_icon: *mut AppIcon) -> *mut crate::icon::Icon;
pub fn wAppIconPaint(app_icon: *mut AppIcon);
pub fn create_appicon_for_application(
app: *mut crate::application::Application,
wwin: *mut crate::window::Window,
);
pub fn removeAppIconFor(app: *mut crate::application::Application);
}
}

View File

@@ -0,0 +1,3 @@
use std::ffi::c_void;
pub type AppSettingsPanel = c_void;

View File

@@ -1,3 +1,7 @@
use std::ffi::c_void;
pub type Dock = c_void;
pub mod ffi {
unsafe extern "C" {
pub fn wDockFinishLaunch(app_icon: *mut crate::app_icon::AppIcon);

View File

@@ -1,6 +1,7 @@
pub mod application;
pub mod app_icon;
pub mod app_menu;
pub mod app_settings_panel;
pub mod defaults;
pub mod dock;
pub mod global;

View File

@@ -1,7 +0,0 @@
[package]
name = "wutil-rs"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["staticlib"]

View File

@@ -1,22 +0,0 @@
AUTOMAKE_OPTIONS =
RUST_SOURCES = \
src/array.rs \
src/find_file.rs \
src/lib.rs \
src/memory.rs
RUST_EXTRA = \
Cargo.lock \
Cargo.toml
target/debug/libwutil_rs.a: $(RUST_SOURCES) $(RUST_EXTRA)
$(CARGO) build
check-local:
$(CARGO) test
clean-local:
$(CARGO) clean
all: target/debug/libwutil_rs.a

View File

@@ -1,449 +0,0 @@
use std::{ffi::c_void, ptr::NonNull};
pub struct Array {
items: Vec<NonNull<c_void>>,
destructor: Option<unsafe extern "C" fn(x: *mut c_void)>,
}
pub mod ffi {
use super::Array;
use std::{
ffi::{c_int, c_void},
ptr::{self, NonNull},
};
pub const NOT_FOUND: c_int = -1;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateArray(initial_size: c_int) -> *mut Array {
let cap = if initial_size < 0 {
0
} else {
initial_size as usize
};
Box::leak(Box::new(Array {
items: Vec::with_capacity(cap),
destructor: None,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateArrayWithDestructor(
initial_size: c_int,
destructor: unsafe extern "C" fn(x: *mut c_void),
) -> *mut Array {
let cap = if initial_size < 0 {
0
} else {
initial_size as usize
};
Box::leak(Box::new(Array {
items: Vec::with_capacity(cap),
destructor: Some(destructor),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateArrayWithArray(array: *mut Array) -> *mut Array {
if array.is_null() {
return ptr::null_mut();
}
let array = unsafe { &*array };
Box::leak(Box::new(Array {
items: array.items.clone(),
destructor: array.destructor,
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMEmptyArray(array: *mut Array) {
if array.is_null() {
return;
}
let array = unsafe { &mut *array };
if let Some(f) = array.destructor {
for item in &mut array.items {
unsafe { (f)(item.as_ptr()) }
}
}
array.items.clear();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFreeArray(array: *mut Array) {
if array.is_null() {
return;
}
unsafe {
WMEmptyArray(array);
let _ = ptr::read(array);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetArrayItemCount(array: *mut Array) -> c_int {
if array.is_null() {
return 0;
}
unsafe { (*array).items.len() as c_int }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMAddToArray(array: *mut Array, item: *mut c_void) {
if array.is_null() {
return;
}
if let Some(item) = NonNull::new(item) {
unsafe {
(*array).items.push(item);
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMInsertInArray(array: *mut Array, index: c_int, item: *mut c_void) {
if array.is_null() {
return;
}
if index < 0 {
return;
}
let array = unsafe { &mut (*array).items };
let index = index as usize;
if index >= array.len() {
return;
}
if let Some(item) = NonNull::new(item) {
array.insert(index, item);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSetInArray(
array: *mut Array,
index: c_int,
item: *mut c_void,
) -> *mut c_void {
if array.is_null() {
return ptr::null_mut();
}
if index < 0 {
return ptr::null_mut();
}
let index = index as usize;
/* is it really useful to perform append if index == array->itemCount ? -Dan */
if index == unsafe { (*array).items.len() } {
unsafe {
WMAddToArray(array, item);
}
return ptr::null_mut();
}
let item = match NonNull::new(item) {
Some(x) => x,
None => return ptr::null_mut(),
};
let array = unsafe { &mut (*array).items };
let old = array[index];
array[index] = item;
old.as_ptr()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDeleteFromArray(array: *mut Array, index: c_int) -> c_int {
if array.is_null() {
return 0;
}
let array = unsafe { &mut *array };
if index < 0 {
return 0;
}
let index = index as usize;
if index >= array.items.len() {
0
} else {
let old = array.items.remove(index);
if let Some(f) = array.destructor {
unsafe {
(f)(old.as_ptr());
}
}
1
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRemoveFromArray(array: *mut Array, item: *mut c_void) -> c_int {
unsafe { WMRemoveFromArrayMatching(array, None, item) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRemoveFromArrayMatching(
array: *mut Array,
pred: Option<unsafe extern "C" fn(item: *const c_void, cdata: *mut c_void) -> c_int>,
cdata: *mut c_void,
) -> c_int {
if array.is_null() {
return 1;
}
let array = unsafe { &mut *array };
let original_len = array.items.len();
match pred {
Some(f) => array.items.retain(|x| unsafe { f(x.as_ptr(), cdata) != 0 }),
None => array.items.retain(|x| ptr::eq(x.as_ptr(), cdata)),
}
(original_len - array.items.len()) as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromArray(array: *mut Array, index: c_int) -> *mut c_void {
if array.is_null() || index < 0 {
return ptr::null_mut();
}
unsafe {
(*array)
.items
.get(index as usize)
.map(|p| p.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFirstInArray(array: *mut Array, item: *mut c_void) -> c_int {
unsafe { WMFindInArray(array, None, item) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMPopFromArray(array: *mut Array) -> *mut c_void {
if array.is_null() {
return ptr::null_mut();
}
unsafe {
(*array)
.items
.pop()
.map(|p| p.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFindInArray(
array: *mut Array,
pred: Option<unsafe extern "C" fn(item: *const c_void, cdata: *mut c_void) -> c_int>,
cdata: *mut c_void,
) -> c_int {
if array.is_null() {
return NOT_FOUND;
}
let array = unsafe { &*array };
if let Some(f) = pred {
array
.items
.iter()
.enumerate()
.find(|(_, item)| unsafe { f(item.as_ptr(), cdata) != 0 })
.map(|(i, _)| i as c_int)
.unwrap_or(NOT_FOUND)
} else {
array
.items
.iter()
.enumerate()
.find(|(_, item)| ptr::eq(item.as_ptr(), cdata))
.map(|(i, _)| i as c_int)
.unwrap_or(NOT_FOUND)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCountInArray(array: *mut Array, item: *const c_void) -> c_int {
if array.is_null() {
return 0;
}
let array = unsafe { &*array };
array
.items
.iter()
.filter(|x| ptr::eq(x.as_ptr(), item))
.count() as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSortArray(
array: *mut Array,
comparator: unsafe extern "C" fn(a: *const c_void, b: *const c_void) -> c_int,
) {
if array.is_null() {
return;
}
unsafe {
(*array)
.items
.sort_by(|&a, &b| match comparator(a.as_ptr(), b.as_ptr()).signum() {
-1 => std::cmp::Ordering::Less,
0 => std::cmp::Ordering::Equal,
1 => std::cmp::Ordering::Greater,
_ => unreachable!(),
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMMapArray(
array: *mut Array,
f: unsafe extern "C" fn(*mut c_void, *mut c_void),
data: *mut c_void,
) {
if array.is_null() {
return;
}
unsafe {
for a in &mut (*array).items {
(f)(a.as_ptr(), data);
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMArrayFirst(array: *mut Array, iter: *mut c_int) -> *mut c_void {
if array.is_null() || iter.is_null() {
return ptr::null_mut();
}
let array = unsafe { &*array };
match array.items.get(0) {
None => {
unsafe {
*iter = NOT_FOUND;
}
ptr::null_mut()
}
Some(x) => {
unsafe {
*iter = 0;
}
x.as_ptr()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMArrayLast(array: *mut Array, iter: *mut c_int) -> *mut c_void {
if array.is_null() || iter.is_null() {
return ptr::null_mut();
}
let array = unsafe { &*array };
match array.items.last() {
None => {
unsafe {
*iter = NOT_FOUND;
}
ptr::null_mut()
}
Some(x) => {
unsafe {
*iter = (array.items.len() - 1) as c_int;
}
x.as_ptr()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMArrayNext(array: *mut Array, iter: *mut c_int) -> *mut c_void {
if array.is_null() || iter.is_null() {
return ptr::null_mut();
}
let array = unsafe { &*array };
let index = unsafe { *iter };
if index < 0 {
return ptr::null_mut();
}
match array.items.get(index as usize) {
Some(i) => {
unsafe {
*iter += 1;
}
i.as_ptr()
}
None => {
unsafe {
*iter = NOT_FOUND;
}
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMArrayPrevious(array: *mut Array, iter: *mut c_int) -> *mut c_void {
if array.is_null() || iter.is_null() {
return ptr::null_mut();
}
let array = unsafe { &*array };
let index = unsafe { *iter };
if index < 0 {
return ptr::null_mut();
}
match array.items.get(index as usize) {
Some(i) => {
unsafe {
*iter -= 1;
}
i.as_ptr()
}
None => {
unsafe {
*iter = NOT_FOUND;
}
ptr::null_mut()
}
}
}
}
#[cfg(test)]
mod test {
use std::{ffi::c_void, ptr};
use super::ffi::*;
#[test]
fn create_destroy_with_size() {
unsafe {
let array = WMCreateArray(10);
assert_eq!((*array).items.len(), 0);
assert!((*array).items.capacity() >= 10);
WMFreeArray(array);
}
}
#[test]
fn create_push_clear_destroy() {
static mut SENTINEL: *mut c_void = ptr::null_mut();
unsafe extern "C" fn destructor(item: *mut c_void) {
unsafe {
SENTINEL = item;
}
}
unsafe {
let array = WMCreateArrayWithDestructor(10, destructor);
assert!(SENTINEL.is_null());
let mut x = 0xdeadbeefu32;
WMAddToArray(array, (&mut x as *mut u32).cast::<c_void>());
assert_eq!(WMGetArrayItemCount(array), 1);
WMEmptyArray(array);
assert!(ptr::eq(SENTINEL, (&x as *const u32).cast::<c_void>()));
assert_eq!(0xdeadbeefu32, *SENTINEL.cast::<u32>());
SENTINEL = ptr::null_mut();
WMFreeArray(array);
assert!(SENTINEL.is_null());
}
}
}

View File

@@ -1,196 +0,0 @@
//! Self-owning shared data segment.
use std::{cell::RefCell, rc::Rc};
#[derive(Clone, Copy, Debug)]
pub enum Format {
Z = 0,
E = 8,
S = 16,
T = 32,
}
/// Reference-counted, self-owned, dynamically sized chunk of bytes.
///
/// In the original WINGs, this type either owned or borrowed a data buffer and
/// had some associated metadata. In Rust, this is little more than a thin
/// wrapper around an `Rc<RefCell<Vec<u8>>>`. It is mostly used by proplists,
/// and it should be done away with once its dependents have been ported to
/// Rust.
pub struct Data(Rc<RefCell<Inner>>);
struct Inner {
bytes: Vec<u8>,
format: Format,
}
pub mod ffi {
use super::{Data, Format, Inner};
use std::{
cell::RefCell,
ffi::{c_int, c_uint, c_void},
ptr,
rc::Rc,
};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateDataWithCapacity(capacity: c_uint) -> *mut Data {
Box::leak(Box::new(Data(Rc::new(RefCell::new(Inner {
bytes: Vec::with_capacity(capacity as usize),
format: Format::Z,
})))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateDataWithLength(length: c_uint) -> *mut Data {
Box::leak(Box::new(Data(Rc::new(RefCell::new(Inner {
bytes: vec![0; length as usize],
format: Format::Z,
})))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateDataWithBytes(
bytes: *const c_void,
length: c_uint,
) -> *mut Data {
let bytes = unsafe { &*ptr::slice_from_raw_parts(bytes.cast::<u8>(), length as usize) };
let bytes = Vec::from(bytes);
Box::leak(Box::new(Data(Rc::new(RefCell::new(Inner {
bytes,
format: Format::Z,
})))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateDataWithData(data: *mut Data) -> *mut Data {
if data.is_null() {
return ptr::null_mut();
}
let data = unsafe { &*data };
Box::leak(Box::new(Data(Rc::new(RefCell::new(Inner {
bytes: data.0.borrow().bytes.clone(),
format: data.0.borrow().format,
})))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRetainData(data: *mut Data) -> *mut Data {
if data.is_null() {
return ptr::null_mut();
}
let data = unsafe { &*data };
Box::leak(Box::new(Data(data.0.clone())))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMReleaseData(data: *mut Data) {
if data.is_null() {
return;
}
let _ = unsafe { ptr::read(data) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDataBytes(data: *mut Data) -> *const c_void {
if data.is_null() {
return ptr::null();
}
unsafe { (*data).0.borrow().bytes.as_ptr().cast::<c_void>() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMIsDataEqualToData(a: *mut Data, b: *mut Data) -> c_int {
if a.is_null() || b.is_null() {
return 0;
}
if ptr::eq(a, b) {
return 1;
}
let a = unsafe { &*a };
let b = unsafe { &*b };
(a.0.borrow().bytes == b.0.borrow().bytes) as c_int
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetDataLength(data: *mut Data) -> c_uint {
if data.is_null() {
return 0;
}
unsafe { (*data).0.borrow().bytes.len() as c_uint }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMAppendDataBytes(
data: *mut Data,
bytes: *const c_void,
length: c_uint,
) {
if data.is_null() || bytes.is_null() || length == 0 {
return;
}
let data = unsafe { &mut *data };
let bytes = unsafe { &*ptr::slice_from_raw_parts(bytes.cast::<u8>(), length as usize) };
data.0.borrow_mut().bytes.extend_from_slice(bytes);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMAppendData(data: *mut Data, ext: *mut Data) {
if data.is_null() || ext.is_null() {
return;
}
let data = unsafe { &mut *data };
let ext = unsafe { &*ext };
data.0
.borrow_mut()
.bytes
.extend_from_slice(&ext.0.borrow().bytes);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSetData(data: *mut Data, other: *mut Data) {
if data.is_null() || other.is_null() {
return;
}
let data = unsafe { &mut *data };
let other = unsafe { &*other };
data.0
.borrow_mut()
.bytes
.copy_from_slice(&other.0.borrow().bytes);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetDataFormat(data: *mut Data) -> c_uint {
if data.is_null() {
return 0;
}
return unsafe {
match (*data).0.borrow().format {
Format::Z => 0,
Format::E => 8,
Format::S => 16,
Format::T => 32,
}
};
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSetDataFormat(data: *mut Data, format: c_uint) {
if data.is_null() {
return;
}
let format = match format {
0 => Format::Z,
8 => Format::E,
16 => Format::S,
32 => Format::T,
_ => return,
};
unsafe {
(*data).0.borrow_mut().format = format;
}
}
}

View File

@@ -1,221 +0,0 @@
//! This module provides approximate reimplementations of file-finding routines
//! from the original WINGs utilities.
//!
//! The [`ffi`] submodule provides functions which may be called directly from C
//! that has not yet been ported to Rust.
//!
//! The original utilities expanded environment variables in path names
//! (expanding `$FOO/bar/baz` to use the value of the environment variable
//! `FOO`) and respected Unix-style denotations of user home directories
//! (resolving `~someuser/foo.txt` to `(home directory of
//! someuser)/foo.txt`. These behaviors have not been preserved. But a path
//! whose first component is `~` will still be resolved relatively to the
//! current user's home directory.
//!
//! Keep in mind that these utilities are not strictly correct as originally
//! designed: a file path that appears valid when it is checked in a subroutine
//! may become invalid if the file is deleted between when the path is checked
//! and when downstream code attempts to open the file. A better design would
//! open the file and return a live file pointer instead of simply returning a
//! path that is likely to work. Future work should redesign this module to
//! avoid this issue.
use std::{
env,
ffi::OsStr,
fs::File,
path::{Component, Path, PathBuf},
};
/// If `file` is an absolute path can be opened, returns that path. Paths
/// starting with `~` are treated as absolute, and the user's home directory is
/// substituted for `~` (so `~/foo` becomes `(users's home directory)/foo`). If
/// the user's home directory cannot be determined, `/` is used instead.
///
/// Returns `None` otherwise.
pub fn absolute(file: &Path) -> Option<PathBuf> {
if file.is_absolute() {
return Some(file.to_path_buf());
} else {
let mut components = file.components();
if components.next() == Some(Component::Normal(OsStr::new("~"))) {
let mut path = env::home_dir().unwrap_or_else(|| PathBuf::from("/"));
for c in components {
path.push(c);
}
Some(path)
} else {
None
}
}
}
/// Resolves `file` to a path that can be opened relative to an element of
/// `paths`, or `None` if it cannot be found. If `paths` is empty, an attempt to
/// resolve `file` relative to the current working directory will be made.
pub fn in_paths<'a>(paths: impl Iterator<Item = &'a Path>, file: &Path) -> Option<PathBuf> {
if file.file_name().map(|f| f.is_empty()).unwrap_or(false) {
return None;
}
let mut paths = paths.peekable();
if paths.peek().is_none() {
if let Ok(_) = File::open(file) {
return Some(file.to_path_buf());
}
return None;
}
let mut buf = PathBuf::new();
for parent in paths {
buf.clear();
buf.push(parent);
buf.push(file);
if let Ok(_) = File::open(&buf) {
return Some(buf);
}
}
None
}
pub mod ffi {
use super::{absolute, in_paths};
use crate::memory::alloc_bytes;
use std::{
env,
ffi::{CStr, OsStr, c_char, c_int},
iter,
os::unix::ffi::OsStrExt,
path::{Path, PathBuf},
ptr,
};
fn split_paths(paths: &CStr) -> impl Iterator<Item = &[u8]> {
paths.to_bytes().split(|b| *b == b':')
}
fn to_c_str(p: &Path) -> *mut c_char {
let os_bytes = p.as_os_str().as_encoded_bytes();
let buf = alloc_bytes(os_bytes.len() + 1);
unsafe {
ptr::copy_nonoverlapping(os_bytes.as_ptr(), buf, os_bytes.len());
}
buf.cast::<c_char>()
}
/// Attempts to find `file` under colon-separated `paths`. Checks if `file`
/// is absolute or prefixed with `~` before attempting to resolve it
/// relatively. If no file can be found, returns NULL. Non-NULL return
/// values must be freed with [`crate::memory::free_bytes`] or
/// [`crate::memory::ffi::wfree`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wfindfile(paths: *const c_char, file: *const c_char) -> *mut c_char {
if file.is_null() {
return ptr::null_mut();
}
let file = unsafe { CStr::from_ptr(file) };
let file = Path::new(OsStr::from_bytes(file.to_bytes()));
if let Some(path) = absolute(&file) {
return to_c_str(&path);
}
let path = if paths.is_null() {
in_paths(iter::empty(), file)
} else {
let paths = unsafe { CStr::from_ptr(paths) };
in_paths(
split_paths(paths).map(|p| Path::new(OsStr::from_bytes(p))),
file,
)
};
path.map(|x| to_c_str(x.as_ref()))
.unwrap_or(ptr::null_mut())
}
/// Attempts to find `file` under an element of NULL-terminated
/// `path_list`. Checks if `file` is absolute or prefixed with `~` before
/// attempting to resolve it relatively. If no file can be found, returns
/// NULL. Non-NULL return values must be freed with
/// [`crate::memory::free_bytes`] or [`crate::memory::ffi::wfree`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wfindfileinlist(
path_list: *const *const c_char,
file: *const c_char,
) -> *mut c_char {
if file.is_null() {
return ptr::null_mut();
}
let file = unsafe { CStr::from_ptr(file) };
let file = Path::new(OsStr::from_bytes(file.to_bytes()));
if let Some(path) = absolute(&file) {
return to_c_str(&path);
}
let path = if path_list.is_null() {
in_paths(iter::empty(), file)
} else {
let paths = (0usize..)
.map(|offset| unsafe { path_list.add(offset) })
.take_while(|&p| unsafe { !(*p).is_null() })
.map(|p| Path::new(OsStr::from_bytes(unsafe { CStr::from_ptr(*p).to_bytes() })));
in_paths(paths, file)
};
path.map(|x| to_c_str(x.as_ref()))
.unwrap_or(ptr::null_mut())
}
/// Attempts to expand `path` if it starts with `~` by replacing the first
/// path element with the user's home directory. Returns NULL if `path` is
/// NULL. Non-NULL return values must be freed with
/// [`crate::memory::free_bytes`] or [`crate::memory::ffi::wfree`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wexpandpath(path: *const c_char) -> *mut c_char {
if path.is_null() {
return ptr::null_mut();
}
let path = unsafe { CStr::from_ptr(path) };
let path = Path::new(OsStr::from_bytes(path.to_bytes()));
absolute(path)
.map(|p| to_c_str(&p))
.unwrap_or_else(|| to_c_str(&path))
}
/// Returns the home directory of the current user, or `"/"` if it cannot be
/// determined. The returned value must be freed with
/// [`crate::memory::free_bytes`] or [`crate::memory::ffi::wfree`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wgethomedir() -> *mut c_char {
match env::home_dir() {
Some(x) => to_c_str(x.as_ref()),
None => to_c_str(Path::new("/")),
}
}
/// Copies `src_file` into `dest_dir/dest_file`. Returns 0 on success, or -1
/// on error.
///
/// This is provided solely to support code that has not yet been ported to
/// Rust. Prefer `std::fs::copy` or another utility if you can.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wcopy_file(
dest_dir: *const c_char,
src_file: *const c_char,
dest_file: *const c_char,
) -> c_int {
if dest_dir.is_null() || src_file.is_null() || dest_file.is_null() {
return -1;
}
let src_file = unsafe { CStr::from_ptr(src_file) };
let dest_dir = unsafe { CStr::from_ptr(dest_dir) };
let dest_file = unsafe { CStr::from_ptr(dest_file) };
let src = Path::new(OsStr::from_bytes(src_file.to_bytes()));
let mut dest = PathBuf::from(OsStr::from_bytes(dest_dir.to_bytes()));
dest.push(OsStr::from_bytes(dest_file.to_bytes()));
if std::fs::copy(src, dest).is_ok() {
return 0;
} else {
return -1;
}
}
}

View File

@@ -1,4 +0,0 @@
pub mod array;
pub mod data;
pub mod find_file;
pub mod memory;

View File

@@ -1,222 +0,0 @@
//! Custom implementations of malloc/free/realloc.
//!
//! These are intended for use by C functions that need to allocate. Window
//! Maker originally provided [`wmalloc`], [`wfree`], and [`wrealloc`] for
//! customizable handling of memory exhaustion (to save workspace state before
//! aborting) and to allow optional use of the Boehm GC library. It also tracked
//! reference counts, via [`wretain`] and [`wrelease`].
//!
//! If everything gets rewritten in Rust, we won't need this module anymore. For
//! now, it helps to move our allocations into Rust so that it is more
//! straightforward to store Rust objects in heap memory that was allocated from
//! C. (Rust may have stricter requirements for heap-allocated segments than are
//! provided by arbitrary C allocators).
//!
//! TODO: We may want to restore handling of OOM errors. This would require
//! installing a customized Rust allocator, which isn't something you can do yet
//! in stable Rust. And, unless our rewrite ends up taking up obscenely more
//! memory than the baseline Window Maker code, it isn't really necessary in
//! this day and age.
use std::{alloc, mem, ptr::{self, NonNull}};
/// Tracks the layout and reference count of an allocated chunk of memory.
#[derive(Clone, Copy)]
struct Header {
ptr: NonNull<u8>,
layout: alloc::Layout,
refcount: u16,
}
impl Header {
/// Recovers the `Header` for the allocated memory chunk `b`.
///
/// ## Safety
///
/// Callers must ensure that `b` is a live allocation from [`wmalloc`] or [`wrealloc`].
unsafe fn for_alloc_bytes(b: *mut u8) -> *mut Header {
unsafe {
b.sub(mem::size_of::<Header>())
.cast::<Header>()
}
}
}
/// Allocates at least `size` bytes and returns a pointer to them.
///
/// Returns null if `size` is 0.
pub fn alloc_bytes(size: usize) -> *mut u8 {
if size == 0 {
return ptr::null_mut();
}
let header_layout = match alloc::Layout::from_size_align(mem::size_of::<Header>(), 8) {
Ok(x) => x,
Err(_) => return ptr::null_mut(),
};
let layout = match alloc::Layout::from_size_align(size, 8) {
Ok(x) => x,
Err(_) => return ptr::null_mut(),
};
let (layout, result_offset) = match header_layout.extend(layout) {
Ok(x) => x,
Err(_) => return ptr::null_mut(),
};
let full_segment = unsafe { alloc::alloc_zeroed(layout) };
if full_segment.is_null() {
return ptr::null_mut();
}
let result = unsafe { full_segment.add(result_offset) };
if result.is_null() {
return ptr::null_mut();
}
unsafe {
let header = result.sub(mem::size_of::<Header>()).cast::<Header>();
header.write_unaligned(Header {
ptr: NonNull::new_unchecked(full_segment),
layout: header_layout,
refcount: 0,
});
}
result
}
/// Frees the bytes pointed to by `b`.
///
/// ## Safety
///
/// Callers must ensure that `b` is a live allocation from [`wmalloc`] or [`wrealloc`].
pub unsafe fn free_bytes(b: *mut u8) {
if b.is_null() {
return;
}
unsafe {
let header = &*Header::for_alloc_bytes(b);
alloc::dealloc(header.ptr.as_ptr(), header.layout);
}
}
/// Functions to be called from C.
pub mod ffi {
use super::{alloc_bytes, free_bytes, Header};
use std::{ffi::c_void, ptr};
/// Allocates `size` bytes. Returns null if `sizes is 0.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wmalloc(size: usize) -> *mut c_void {
alloc_bytes(size).cast::<c_void>()
}
/// Frees `ptr`, which must have come from [`wmalloc`] or [`wrealloc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wfree(ptr: *mut c_void) {
unsafe { free_bytes(ptr.cast::<u8>()); }
}
/// Resizes `ptr` to be at least `newsize` bytes in size, returning the
/// start of the new segment.
///
/// ## Safety
///
/// Callers must ensure that `ptr` is a live allocation from [`wmalloc`] or [`wrealloc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wrealloc(ptr: *mut c_void, newsize: usize) -> *mut c_void {
unsafe {
wfree(ptr);
wmalloc(newsize).cast::<c_void>()
}
}
/// Bumps the refcount for `ptr`.
///
/// ## Safety
///
/// Callers must ensure that `b` is a live allocation from [`wmalloc`] or [`wrealloc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wretain(ptr: *mut c_void) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
unsafe {
let header = Header::for_alloc_bytes(ptr.cast::<u8>());
(*header).refcount += 1;
}
ptr
}
/// Decrements the refcount for `ptr`. If this brings the refcount to 0,
/// frees `ptr`.
///
/// ## Safety
///
/// Callers must ensure that `ptr` is a live allocation from [`wmalloc`] or [`wrealloc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wrelease(ptr: *mut c_void) {
let ptr = ptr.cast::<u8>();
if ptr.is_null() {
return;
}
let header = unsafe { &mut *Header::for_alloc_bytes(ptr) };
match header.refcount {
0 | 1 => unsafe { free_bytes(ptr) },
_ => header.refcount -= 1,
}
}
}
#[cfg(test)]
mod test {
use super::{alloc_bytes, free_bytes, ffi::wrealloc, Header};
use std::{mem, os::raw::c_void, ptr};
#[test]
fn recover_header() {
unsafe {
let x = alloc_bytes(mem::size_of::<i64>());
let header = Header::for_alloc_bytes(x);
assert_eq!(header.cast::<u8>().add(mem::size_of::<Header>()), x);
// This may be allocator-dependent, but it's a reasonable sanity check for now.
assert!((*header).ptr.as_ptr() <= header.cast::<u8>());
}
}
#[test]
fn alloc_zero_returns_null() {
assert!(alloc_bytes(0).is_null());
}
#[test]
fn free_null() {
unsafe { free_bytes(ptr::null_mut()); }
}
#[test]
fn realloc_null() {
unsafe { assert!(wrealloc(ptr::null_mut(), 0).is_null()); }
}
#[test]
fn alloc_free_nonzero() {
let x = alloc_bytes(mem::size_of::<i64>()).cast::<i64>();
assert!(!x.is_null());
unsafe { *x = 42; }
assert_eq!(unsafe { *x }, 42);
unsafe { free_bytes(x.cast::<u8>()); }
}
#[test]
fn realloc_nonzero() {
let x = alloc_bytes(mem::size_of::<i64>()).cast::<c_void>();
assert!(!x.is_null());
let y = unsafe { wrealloc(x, mem::size_of::<i32>()).cast::<i32>() };
assert!(!y.is_null());
unsafe { *y = 17; }
assert_eq!(unsafe { *y }, 17);
unsafe { free_bytes(y.cast::<u8>()); }
}
}