1
0
forked from vitrine/wmaker

14 Commits

Author SHA1 Message Date
4732296762 Port WINGs data.c (WMData) to Rust.
This is not tested super well, but I hope we can be rid of it soon enough. (Once
we have WMPropList migrated, WMData and other WINGs data structures should be
easier to prune.)
2025-10-04 12:31:54 -04:00
3846a5017c Correct oversight in how WMMapArray is supposed to work. 2025-10-03 22:55:52 -04:00
d736af8321 Drop WMData's destructor field.
WMData always owns its data and allocates it with wmalloc, so we can always free
it with wfree (and don't need to call anything else).
2025-10-03 01:19:07 -04:00
506e14cd24 Eliminate the WINGs function WMCreateDataWithBytesNoCopy.
This constructor was only needed in one particular place. We can duplidate the
data instead of borrowing it. This ensures that WMData always owns its data
segment, which simplifies porting to Rust significantly.
2025-10-03 01:16:32 -04:00
ee047b63df Eliminate the unused WINGs function WMGetSubdataWithRange. 2025-10-03 01:02:40 -04:00
0b5033a0e4 Eliminate the unused WINGs function WMCreatePLDataWithBytesNoCopy. 2025-10-03 01:01:15 -04:00
f6c897de95 Forgot to add new array.rs impl. 2025-10-03 00:50:02 -04:00
249dee3874 Fix const qualifier on strings returned by wgethomedir. 2025-10-03 00:44:58 -04:00
0d6f5b6d57 Reimplement WINGs array.c in Rust.
This is another utility that should not be used in any new (Rust) code. (We
should prefer Vec or something similar.) This should be removed once dependents
are ported to Rust.
2025-10-03 00:43:56 -04:00
e4c619bb14 Replace most WUtil functions in findfile.c with Rust impls.
This is not a bug-for-bug reimplementation, and it may need some shaking down to
ensure that everything still works. Once their dependents are ported, it would
be appropriate to dispose of them.
2025-10-02 14:26:37 -04:00
adb601eaa9 Use free() on memory returned by FcNameUnparse and hand back wfree-managed pointers from our functions.
This is necessary because we now allocate memory through a special allocator of
our own on the Rust side. Passing raw malloc'd pointers to wfree will break
things.
2025-10-02 13:26:57 -04:00
a07bdb747d Remember to AC_SUBST the Rust compiler environment variables so they're visible in Makefiles. 2025-10-02 13:24:51 -04:00
d7bde34561 Port custom allocators (WINGs memory.c) to Rust.
This introduces the crate wutil-rs, which is intended to be the destination for
migrating the API of WINGs/WINGs/WUtil.h to Rust.
2025-09-21 15:31:47 -04:00
ceeddfb9da Rip out Boehm GC support.
This is done to simplify memory management across the boundary between C and
Rust. While rewriting WINGs, we may want to be able to malloc/free with the libc
allocator on both sides of that divide.
2025-09-19 20:04:10 -04:00
36 changed files with 1219 additions and 1441 deletions

View File

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

View File

@@ -10,10 +10,12 @@ 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 $(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@
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)
EXTRA_DIST = BUGS make-rgb Examples Extras Tests
@@ -62,15 +64,12 @@ 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,10 +213,6 @@ void wfree(void *ptr);
void wrelease(void *ptr);
void* wretain(void *ptr);
typedef void waborthandler(int);
waborthandler* wsetabort(waborthandler* handler);
/* ---[ WINGs/error.c ]--------------------------------------------------- */
enum {
@@ -248,8 +244,8 @@ char* wexpandpath(const char *path);
int wcopy_file(const char *toPath, const char *srcFile, const char *destFile);
/* don't free the returned string */
const char* wgethomedir(void);
/* You must free the returned string! */
char* wgethomedir(void);
/* ---[ WINGs/proplist.c ]------------------------------------------------ */
@@ -400,7 +396,7 @@ extern const WMHashTableCallbacks WMStringPointerHashCallbacks;
/* keys are strings, but they are not copied */
/* ---[ WINGs/array.c ]--------------------------------------------------- */
/* ---[ wutil-rs/src/array.rs ]--------------------------------------------------- */
/*
* WMArray use an array to store the elements.
@@ -422,29 +418,22 @@ 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);
/* replace and set will return the old item WITHOUT calling the
/* set returns the old item WITHOUT calling the
* destructor on it even if its available. Free the returned item yourself.
*/
void* WMReplaceInArray(WMArray *array, int index, void *item);
#define WMSetInArray(array, index, item) WMReplaceInArray(array, index, item)
void* WMSetInArray(WMArray *array, int index, void *item);
/* delete and remove will remove the elements and cause the elements
* after them to decrement their indexes by 1. Also will call the
@@ -452,20 +441,21 @@ void* WMReplaceInArray(WMArray *array, int index, void *item);
*/
int WMDeleteFromArray(WMArray *array, int index);
#define WMRemoveFromArray(array, item) WMRemoveFromArrayMatching(array, NULL, item)
int WMRemoveFromArray(WMArray *array, void *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);
@@ -479,8 +469,6 @@ 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);
@@ -604,37 +592,16 @@ 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);
@@ -649,10 +616,6 @@ 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);
@@ -768,10 +731,6 @@ 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, ...);

View File

@@ -1,363 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,289 +0,0 @@
/*
* 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,331 +34,6 @@
#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)
{
@@ -419,105 +94,3 @@ 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 = WMDuplicateArray(idleHandler);
handlerCopy = WMCreateArrayWithArray(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 = WMDuplicateArray(inputHandler);
WMArray *handlerCopy = WMCreateArrayWithArray(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 = WMDuplicateArray(inputHandler);
WMArray *handlerCopy = WMCreateArrayWithArray(inputHandler);
int mask;
/* use WM_ITERATE_ARRAY() here */

View File

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

View File

@@ -942,20 +942,6 @@ 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 = WMDuplicateArray(selHandlers);
copy = WMCreateArrayWithArray(selHandlers);
WM_ITERATE_ARRAY(copy, handler, iter) {
if (handler && handler->flags.delete_pending) {
WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp);
@@ -261,8 +261,9 @@ static WMData *getSelectionData(Display * dpy, Window win, Atom where)
bpi = bits / 8;
wdata = WMCreateDataWithBytesNoCopy(data, len * bpi, (void *) XFree);
wdata = WMCreateDataWithBytes(data, len * bpi);
WMSetDataFormat(wdata, bits);
XFree(data);
return wdata;
}
@@ -300,7 +301,7 @@ static void handleNotifyEvent(XEvent * event)
}
/* delete callbacks */
copy = WMDuplicateArray(selCallbacks);
copy = WMCreateArrayWithArray(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;
const char *h;
char *h;
int pathlen;
if (path)
@@ -69,13 +69,12 @@ 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,10 +2994,13 @@ static void customPaletteMenuNewFromFile(W_ColorPanel * panel)
int i;
RImage *tmpImg = NULL;
if ((!panel->lastBrowseDir) || (strcmp(panel->lastBrowseDir, "\0") == 0))
spath = wexpandpath(wgethomedir());
else
if ((!panel->lastBrowseDir) || (strcmp(panel->lastBrowseDir, "\0") == 0)) {
char *homedir = wgethomedir();
spath = wexpandpath(homedir);
wfree(homedir);
} else {
spath = wexpandpath(panel->lastBrowseDir);
}
browseP = WMGetOpenPanel(scr);
WMSetFilePanelCanChooseDirectories(browseP, 0);

View File

@@ -761,17 +761,15 @@ static void goFloppy(WMWidget *widget, void *p_panel)
static void goHome(WMWidget *widget, void *p_panel)
{
WMFilePanel *panel = p_panel;
const char *home;
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,12 +51,15 @@ 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 fname;
return result;
}
static Bool hasProperty(FcPattern * pattern, const char *property)
@@ -92,6 +95,7 @@ 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] == '-') {
@@ -115,7 +119,9 @@ static char *makeFontOfSize(const char *font, int size, const char *fallback)
/*FcPatternPrint(pattern); */
result = (char *)FcNameUnparse(pattern);
name = (char *)FcNameUnparse(pattern);
result = wstrdup(name);
free(name);
FcPatternDestroy(pattern);
return result;
@@ -421,7 +427,7 @@ WMFont *WMCopyFontWithStyle(WMScreen * scrPtr, WMFont * font, WMFontStyle style)
name = (char *)FcNameUnparse(pattern);
copy = WMCreateFont(scrPtr, name);
FcPatternDestroy(pattern);
wfree(name);
free(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;
char *path, *homedir;
WMOpenPanel *opanel;
WMScreen *scr = WMWidgetScreen(w);
@@ -1118,13 +1118,17 @@ static void extractTexture(WMWidget * w, void *data)
WMSetFilePanelCanChooseDirectories(opanel, False);
WMSetFilePanelCanChooseFiles(opanel, True);
if (WMRunModalFilePanelForDirectory(opanel, panel->parent, wgethomedir(), _("Select File"), NULL)) {
homedir = wgethomedir();
if (WMRunModalFilePanelForDirectory(opanel, panel->parent, homedir, _("Select File"), NULL)) {
path = WMGetFilePanelFileName(opanel);
OpenExtractPanelFor(panel);
wfree(path);
}
if (homedir) {
wfree(homedir);
}
}
static void changePage(WMWidget * w, void *data)

View File

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

View File

@@ -66,6 +66,7 @@ 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 = wstrdup(wgethomedir());
ipath = wgethomedir();
if (WMRunModalFilePanelForDirectory(opanel, panel->win, ipath, _("Open Image"), NULL)) {
char *path, *fullpath;

View File

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

View File

@@ -54,10 +54,12 @@ 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 ==========================
@@ -351,24 +353,6 @@ 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
@@ -973,6 +957,9 @@ 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

View File

@@ -253,14 +253,6 @@ 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
@@ -468,8 +460,6 @@ 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

@@ -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(WMReplaceInArray(p->history, 0, WMGetTextFieldText(p->panel->text)));
wfree(WMSetInArray(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(WMReplaceInArray(p->history, 0, wstrdup(result)));
wfree(WMSetInArray(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] = WMDuplicateArray(scr->selected_windows);
scr->shortcutWindows[widx] = WMCreateArrayWithArray(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] = WMDuplicateArray(scr->selected_windows);
scr->shortcutWindows[widx] = WMCreateArrayWithArray(scr->selected_windows);
}
}

View File

@@ -624,7 +624,6 @@ 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;
WMReplaceInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
WMSetInArray(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] = WMDuplicateArray(scr->selected_windows);
scr->shortcutWindows[index] = WMCreateArrayWithArray(scr->selected_windows);
/*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
} else {

View File

@@ -18,31 +18,50 @@ AM_CPPFLAGS = \
liblist= @LIBRARY_SEARCH_PATH@ @INTLIBS@
wdwrite_LDADD = $(top_builddir)/WINGs/libWUtil.la $(liblist)
wdwrite_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wdread_LDADD = $(top_builddir)/WINGs/libWUtil.la $(liblist)
wdread_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wxcopy_LDADD = @XLFLAGS@ @XLIBS@
wxpaste_LDADD = @XLFLAGS@ @XLIBS@
getstyle_LDADD = $(top_builddir)/WINGs/libWUtil.la $(liblist)
getstyle_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(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 $(liblist)
convertfonts_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
convertfonts_SOURCES = convertfonts.c fontconv.c common.h
seticons_LDADD= $(top_builddir)/WINGs/libWUtil.la $(liblist)
seticons_LDADD= \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
geticonset_LDADD= $(top_builddir)/WINGs/libWUtil.la $(liblist)
geticonset_LDADD= \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wmagnify_LDADD = \
$(top_builddir)/WINGs/libWINGs.la \
@@ -52,18 +71,21 @@ 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 \
@@ -75,6 +97,7 @@ 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 = wstrdup(wgethomedir());
homedir = 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

7
wutil-rs/Cargo.toml Normal file
View File

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

22
wutil-rs/Makefile.am Normal file
View File

@@ -0,0 +1,22 @@
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

449
wutil-rs/src/array.rs Normal file
View File

@@ -0,0 +1,449 @@
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());
}
}
}

196
wutil-rs/src/data.rs Normal file
View File

@@ -0,0 +1,196 @@
//! 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;
}
}
}

221
wutil-rs/src/find_file.rs Normal file
View File

@@ -0,0 +1,221 @@
//! 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;
}
}
}

4
wutil-rs/src/lib.rs Normal file
View File

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

222
wutil-rs/src/memory.rs Normal file
View File

@@ -0,0 +1,222 @@
//! 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>()); }
}
}