1
0
forked from vitrine/wmaker

12 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
38 changed files with 1409 additions and 1526 deletions

View File

@@ -20,7 +20,6 @@ libWUtil_la_LIBADD = @LIBBSD@ $(wutilrs)
EXTRA_DIST = BUGS make-rgb Examples Extras Tests
# wbutton.c
libWINGs_la_SOURCES = \
configuration.c \
@@ -65,13 +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 \
menuparser.c \
menuparser.h \
menuparser_macros.c \

View File

@@ -169,6 +169,10 @@ typedef struct {
unsigned (*hash)(const void *);
/* NULL is pointer compare */
Bool (*keyIsEqual)(const void *, const void *);
/* NULL does nothing */
void* (*retainKey)(const void *);
/* NULL does nothing */
void (*releaseKey)(const void *);
} WMHashTableCallbacks;
@@ -240,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 ]------------------------------------------------ */
@@ -334,8 +338,7 @@ void WHandleEvents(void);
/* ---[ WINGs/hashtable.c ]----------------------------------------------- */
WMHashTable* WMCreateIdentityHashTable();
WMHashTable* WMCreateStringHashTable();
WMHashTable* WMCreateHashTable(const WMHashTableCallbacks callbacks);
void WMFreeHashTable(WMHashTable *table);
@@ -385,11 +388,15 @@ Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator *enumerator,
extern const WMHashTableCallbacks WMIntHashCallbacks;
/* sizeof(keys) are <= sizeof(void*) */
extern const WMHashTableCallbacks WMStringHashCallbacks;
/* keys are strings. Strings will be copied with wstrdup()
* and freed with wfree() */
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.
@@ -411,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
@@ -441,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);
@@ -468,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);
@@ -593,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);
@@ -638,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);
@@ -757,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 */

422
WINGs/hashtable.c Normal file
View File

@@ -0,0 +1,422 @@
#include <config.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "WUtil.h"
#define INITIAL_CAPACITY 23
typedef struct HashItem {
const void *key;
const void *data;
struct HashItem *next; /* collided item list */
} HashItem;
typedef struct W_HashTable {
WMHashTableCallbacks callbacks;
unsigned itemCount;
unsigned size; /* table size */
HashItem **table;
} HashTable;
#define HASH(table, key) (((table)->callbacks.hash ? \
(*(table)->callbacks.hash)(key) : hashPtr(key)) % (table)->size)
#define DUPKEY(table, key) ((table)->callbacks.retainKey ? \
(*(table)->callbacks.retainKey)(key) : (key))
#define RELKEY(table, key) if ((table)->callbacks.releaseKey) \
(*(table)->callbacks.releaseKey)(key)
static inline unsigned hashString(const void *param)
{
const char *key = param;
unsigned ret = 0;
unsigned ctr = 0;
while (*key) {
ret ^= *key++ << ctr;
ctr = (ctr + 1) % sizeof(char *);
}
return ret;
}
static inline unsigned hashPtr(const void *key)
{
return ((size_t) key / sizeof(char *));
}
static void rellocateItem(WMHashTable * table, HashItem * item)
{
unsigned h;
h = HASH(table, item->key);
item->next = table->table[h];
table->table[h] = item;
}
static void rebuildTable(WMHashTable * table)
{
HashItem *next;
HashItem **oldArray;
int i;
int oldSize;
int newSize;
oldArray = table->table;
oldSize = table->size;
newSize = table->size * 2;
table->table = wmalloc(sizeof(char *) * newSize);
table->size = newSize;
for (i = 0; i < oldSize; i++) {
while (oldArray[i] != NULL) {
next = oldArray[i]->next;
rellocateItem(table, oldArray[i]);
oldArray[i] = next;
}
}
wfree(oldArray);
}
WMHashTable *WMCreateHashTable(const WMHashTableCallbacks callbacks)
{
HashTable *table;
table = wmalloc(sizeof(HashTable));
table->callbacks = callbacks;
table->size = INITIAL_CAPACITY;
table->table = wmalloc(sizeof(HashItem *) * table->size);
return table;
}
void WMResetHashTable(WMHashTable * table)
{
HashItem *item, *tmp;
int i;
for (i = 0; i < table->size; i++) {
item = table->table[i];
while (item) {
tmp = item->next;
RELKEY(table, item->key);
wfree(item);
item = tmp;
}
}
table->itemCount = 0;
if (table->size > INITIAL_CAPACITY) {
wfree(table->table);
table->size = INITIAL_CAPACITY;
table->table = wmalloc(sizeof(HashItem *) * table->size);
} else {
memset(table->table, 0, sizeof(HashItem *) * table->size);
}
}
void WMFreeHashTable(WMHashTable * table)
{
HashItem *item, *tmp;
int i;
for (i = 0; i < table->size; i++) {
item = table->table[i];
while (item) {
tmp = item->next;
RELKEY(table, item->key);
wfree(item);
item = tmp;
}
}
wfree(table->table);
wfree(table);
}
unsigned WMCountHashTable(WMHashTable * table)
{
return table->itemCount;
}
static HashItem *hashGetItem(WMHashTable *table, const void *key)
{
unsigned h;
HashItem *item;
h = HASH(table, key);
item = table->table[h];
if (table->callbacks.keyIsEqual) {
while (item) {
if ((*table->callbacks.keyIsEqual) (key, item->key)) {
break;
}
item = item->next;
}
} else {
while (item) {
if (key == item->key) {
break;
}
item = item->next;
}
}
return item;
}
void *WMHashGet(WMHashTable * table, const void *key)
{
HashItem *item;
item = hashGetItem(table, key);
if (!item)
return NULL;
return (void *)item->data;
}
Bool WMHashGetItemAndKey(WMHashTable * table, const void *key, void **retItem, void **retKey)
{
HashItem *item;
item = hashGetItem(table, key);
if (!item)
return False;
if (retKey)
*retKey = (void *)item->key;
if (retItem)
*retItem = (void *)item->data;
return True;
}
void *WMHashInsert(WMHashTable * table, const void *key, const void *data)
{
unsigned h;
HashItem *item;
int replacing = 0;
h = HASH(table, key);
/* look for the entry */
item = table->table[h];
if (table->callbacks.keyIsEqual) {
while (item) {
if ((*table->callbacks.keyIsEqual) (key, item->key)) {
replacing = 1;
break;
}
item = item->next;
}
} else {
while (item) {
if (key == item->key) {
replacing = 1;
break;
}
item = item->next;
}
}
if (replacing) {
const void *old;
old = item->data;
item->data = data;
RELKEY(table, item->key);
item->key = DUPKEY(table, key);
return (void *)old;
} else {
HashItem *nitem;
nitem = wmalloc(sizeof(HashItem));
nitem->key = DUPKEY(table, key);
nitem->data = data;
nitem->next = table->table[h];
table->table[h] = nitem;
table->itemCount++;
}
/* OPTIMIZE: put this in an idle handler. */
if (table->itemCount > table->size) {
#ifdef DEBUG0
printf("rebuilding hash table...\n");
#endif
rebuildTable(table);
#ifdef DEBUG0
printf("finished rebuild.\n");
#endif
}
return NULL;
}
static HashItem *deleteFromList(HashTable * table, HashItem * item, const void *key)
{
HashItem *next;
if (item == NULL)
return NULL;
if ((table->callbacks.keyIsEqual && (*table->callbacks.keyIsEqual) (key, item->key))
|| (!table->callbacks.keyIsEqual && key == item->key)) {
next = item->next;
RELKEY(table, item->key);
wfree(item);
table->itemCount--;
return next;
}
item->next = deleteFromList(table, item->next, key);
return item;
}
void WMHashRemove(WMHashTable * table, const void *key)
{
unsigned h;
h = HASH(table, key);
table->table[h] = deleteFromList(table, table->table[h], key);
}
WMHashEnumerator WMEnumerateHashTable(WMHashTable * table)
{
WMHashEnumerator enumerator;
enumerator.table = table;
enumerator.index = 0;
enumerator.nextItem = table->table[0];
return enumerator;
}
void *WMNextHashEnumeratorItem(WMHashEnumerator * enumerator)
{
const void *data = NULL;
/* this assumes the table doesn't change between
* WMEnumerateHashTable() and WMNextHashEnumeratorItem() calls */
if (enumerator->nextItem == NULL) {
HashTable *table = enumerator->table;
while (++enumerator->index < table->size) {
if (table->table[enumerator->index] != NULL) {
enumerator->nextItem = table->table[enumerator->index];
break;
}
}
}
if (enumerator->nextItem) {
data = ((HashItem *) enumerator->nextItem)->data;
enumerator->nextItem = ((HashItem *) enumerator->nextItem)->next;
}
return (void *)data;
}
void *WMNextHashEnumeratorKey(WMHashEnumerator * enumerator)
{
const void *key = NULL;
/* this assumes the table doesn't change between
* WMEnumerateHashTable() and WMNextHashEnumeratorKey() calls */
if (enumerator->nextItem == NULL) {
HashTable *table = enumerator->table;
while (++enumerator->index < table->size) {
if (table->table[enumerator->index] != NULL) {
enumerator->nextItem = table->table[enumerator->index];
break;
}
}
}
if (enumerator->nextItem) {
key = ((HashItem *) enumerator->nextItem)->key;
enumerator->nextItem = ((HashItem *) enumerator->nextItem)->next;
}
return (void *)key;
}
Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator * enumerator, void **item, void **key)
{
/* this assumes the table doesn't change between
* WMEnumerateHashTable() and WMNextHashEnumeratorItemAndKey() calls */
if (enumerator->nextItem == NULL) {
HashTable *table = enumerator->table;
while (++enumerator->index < table->size) {
if (table->table[enumerator->index] != NULL) {
enumerator->nextItem = table->table[enumerator->index];
break;
}
}
}
if (enumerator->nextItem) {
if (item)
*item = (void *)((HashItem *) enumerator->nextItem)->data;
if (key)
*key = (void *)((HashItem *) enumerator->nextItem)->key;
enumerator->nextItem = ((HashItem *) enumerator->nextItem)->next;
return True;
}
return False;
}
static Bool compareStrings(const void *param1, const void *param2)
{
const char *key1 = param1;
const char *key2 = param2;
return strcmp(key1, key2) == 0;
}
typedef void *(*retainFunc) (const void *);
typedef void (*releaseFunc) (const void *);
const WMHashTableCallbacks WMIntHashCallbacks = {
NULL,
NULL,
NULL,
NULL
};
const WMHashTableCallbacks WMStringHashCallbacks = {
hashString,
compareStrings,
(retainFunc) wstrdup,
(releaseFunc) wfree
};
const WMHashTableCallbacks WMStringPointerHashCallbacks = {
hashString,
compareStrings,
NULL,
NULL
};

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

@@ -88,10 +88,10 @@ static NotificationCenter *notificationCenter = NULL;
void W_InitNotificationCenter(void)
{
notificationCenter = wmalloc(sizeof(NotificationCenter));
notificationCenter->nameTable = WMCreateStringHashTable();
notificationCenter->objectTable = WMCreateIdentityHashTable();
notificationCenter->nameTable = WMCreateHashTable(WMStringPointerHashCallbacks);
notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks);
notificationCenter->nilList = NULL;
notificationCenter->observerTable = WMCreateIdentityHashTable();
notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks);
}
void W_ReleaseNotificationCenter(void)

View File

@@ -60,6 +60,8 @@ typedef Bool(*isEqualFunc) (const void *, const void *);
static const WMHashTableCallbacks WMPropListHashCallbacks = {
hashPropList,
(isEqualFunc) WMIsPropListEqualTo,
NULL,
NULL
};
static Bool caseSensitive = True;
@@ -940,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;
@@ -988,7 +976,7 @@ WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
plist = (WMPropList *) wmalloc(sizeof(W_PropList));
plist->type = WPLDictionary;
plist->d.dict = WMCreatePropListHashTable();
plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
plist->retainCount = 1;
if (!key || !value)

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

@@ -65,7 +65,7 @@ struct W_Balloon *W_CreateBalloon(WMScreen * scr)
W_ResizeView(bPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
bPtr->flags.alignment = DEFAULT_ALIGNMENT;
bPtr->table = WMCreateIdentityHashTable();
bPtr->table = WMCreateHashTable(WMIntHashCallbacks);
bPtr->delay = DEFAULT_DELAY;

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

@@ -535,7 +535,7 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel)
if (pat)
FcPatternDestroy(pat);
families = WMCreateStringHashTable();
families = WMCreateHashTable(WMStringPointerHashCallbacks);
if (fs) {
for (i = 0; i < fs->nfont; i++) {

View File

@@ -630,7 +630,7 @@ WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * c
scrPtr->rootWin = RootWindow(display, screen);
scrPtr->fontCache = WMCreateStringHashTable();
scrPtr->fontCache = WMCreateHashTable(WMStringPointerHashCallbacks);
scrPtr->xftdraw = XftDrawCreate(scrPtr->display, W_DRAWABLE(scrPtr), scrPtr->visual, scrPtr->colormap);

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

@@ -67,7 +67,6 @@ WPrefs_DEPENDENCIES = $(top_builddir)/WINGs/libWINGs.la
WPrefs_LDADD = \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a\
$(top_builddir)/wings-rs/target/debug/libwings_rs.la\
$(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

@@ -47,6 +47,14 @@ struct {
static pid_t DeadChildren[MAX_DEATHS];
static int DeadChildrenCount = 0;
static noreturn void wAbort(Bool foo)
{
/* Parameter not used, but tell the compiler that it is ok */
(void) foo;
exit(1);
}
static void print_help(const char *progname)
{
printf(_("usage: %s [options]\n"), progname);

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 ==========================

View File

@@ -134,7 +134,7 @@ else
nodist_wmaker_SOURCES = misc.hack_nf.c \
xmodifier.hack_nf.c
CLEANFILES = $(nodist_wmaker_SOURCES)
CLEANFILES = $(nodist_wmaker_SOURCES) ../wmaker-rs/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 +160,6 @@ wmaker_LDADD = \
$(top_builddir)/WINGs/libWINGs.la\
$(top_builddir)/WINGs/libWUtil.la\
$(top_builddir)/wrlib/libwraster.la\
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a\
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
@XLFLAGS@ \
@LIBXRANDR@ \

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

@@ -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

View File

@@ -5,8 +5,3 @@ edition = "2024"
[lib]
crate-type = ["staticlib"]
[dependencies]
hashbrown = "0.16.0"
libc = "0.2.175"
x11 = "2.21.0"

View File

@@ -1,7 +1,8 @@
AUTOMAKE_OPTIONS =
RUST_SOURCES = \
src/hash_table.rs \
src/array.rs \
src/find_file.rs \
src/lib.rs \
src/memory.rs

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;
}
}
}

View File

@@ -1,328 +0,0 @@
use hashbrown::hash_map::{self, HashMap};
use std::{
borrow::Borrow,
ffi::{CStr, c_void},
hash::{Hash, Hasher},
mem,
};
pub enum HashTable {
PointerKeyed(HashMap<VoidPointer, VoidPointer>),
StringKeyed(HashMap<StringPointer, VoidPointer>),
}
impl HashTable {
pub fn new_pointer_keyed() -> Self {
HashTable::PointerKeyed(HashMap::new())
}
pub fn new_string_keyed() -> Self {
HashTable::StringKeyed(HashMap::new())
}
pub fn clear(&mut self) {
match self {
HashTable::PointerKeyed(m) => m.clear(),
HashTable::StringKeyed(m) => m.clear(),
}
}
pub fn len(&self) -> usize {
match self {
HashTable::PointerKeyed(m) => m.len(),
HashTable::StringKeyed(m) => m.len(),
}
}
pub unsafe fn get(&self, key: *const i8) -> Option<*mut i8> {
match self {
HashTable::PointerKeyed(m) => {
let key = key.cast_mut();
m.get(&key).map(|x| x.0)
}
HashTable::StringKeyed(m) => {
let key = StringPointer(key.cast_mut());
let v = m.get(&key).map(|x| x.0);
mem::forget(key);
v
}
}
}
pub unsafe fn insert(&mut self, key: *mut i8, data: VoidPointer) -> Option<VoidPointer> {
match self {
HashTable::PointerKeyed(m) => m.insert(VoidPointer(key), data),
HashTable::StringKeyed(m) => m.insert(StringPointer(key), data),
}
}
pub unsafe fn remove(&mut self, key: *const i8) {
match self {
HashTable::PointerKeyed(m) => {
let key = key.cast_mut();
m.remove(&key);
}
HashTable::StringKeyed(m) => {
let key = StringPointer(key.cast_mut());
m.remove(&key);
mem::forget(key);
}
}
}
}
#[derive(Debug, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct VoidPointer(*mut i8);
impl Drop for VoidPointer {
fn drop(&mut self) {
unsafe { libc::free(self.0.cast::<c_void>()) }
}
}
impl Borrow<*mut i8> for VoidPointer {
fn borrow(&self) -> &*mut i8 {
&self.0
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct StringPointer(*mut i8);
impl PartialEq for StringPointer {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_null(), other.0.is_null()) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => unsafe { CStr::from_ptr(self.0) == CStr::from_ptr(other.0) },
}
}
}
impl Eq for StringPointer {}
impl Hash for StringPointer {
fn hash<H: Hasher>(&self, h: &mut H) {
if self.0.is_null() {
h.write_usize(0)
} else {
unsafe { CStr::from_ptr(self.0).hash(h) }
}
}
}
impl Drop for StringPointer {
fn drop(&mut self) {
unsafe {
libc::free(self.0.cast::<c_void>());
}
}
}
pub enum Enumerator<'a> {
PointerKeyed(hash_map::IterMut<'a, VoidPointer, VoidPointer>),
StringKeyed(hash_map::IterMut<'a, StringPointer, VoidPointer>),
}
pub mod ffi {
use std::{
ffi::{c_int, c_uint, c_void},
mem, ptr,
};
use super::{Enumerator, HashTable, StringPointer, VoidPointer};
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMCreateIdentityHashTable() -> *mut HashTable {
Box::leak(Box::new(HashTable::new_pointer_keyed()))
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMCreateStringHashTable() -> *mut HashTable {
Box::leak(Box::new(HashTable::new_string_keyed()))
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMFreeHashTable(table: *mut HashTable) {
if !table.is_null() {
let _ = unsafe { Box::from_raw(table) };
}
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMResetHashTable(table: *mut HashTable) {
if !table.is_null() {
unsafe {
(*table).clear();
}
}
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMCountHashTable(table: *mut HashTable) -> c_uint {
if table.is_null() {
0
} else {
(unsafe { (*table).len() }) as c_uint
}
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMHashGet(table: *mut HashTable, key: *const c_void) -> *mut c_void {
if table.is_null() {
return ptr::null_mut();
}
let key = key.cast::<i8>();
(unsafe { (*table).get(key) })
.map(|v| v.cast::<c_void>())
.unwrap_or(ptr::null_mut())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMHashGetItemAndKey(
table: *mut HashTable,
key: *const c_void,
value_dest: *mut *mut c_void,
key_dest: *mut *const c_void,
) -> c_int {
if table.is_null() {
return 0;
}
let table = unsafe { &mut *table };
match table {
HashTable::PointerKeyed(m) => {
let key = VoidPointer(key.cast::<i8>().cast_mut());
let result = match m.get_key_value_mut(&key) {
Some((k, v)) => {
unsafe {
*key_dest = k.0.cast::<c_void>();
*value_dest = v.0.cast::<c_void>();
}
1
}
None => 0,
};
mem::forget(key);
result
}
HashTable::StringKeyed(m) => {
let key = StringPointer(key.cast::<i8>().cast_mut());
let result = match m.get_key_value_mut(&key) {
Some((k, v)) => {
unsafe {
*key_dest = k.0.cast::<c_void>();
*value_dest = v.0.cast::<c_void>();
}
1
}
None => 0,
};
mem::forget(key);
return result;
}
}
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMHashInsert(
table: *mut HashTable,
key: *mut c_void,
data: *mut c_void,
) -> *mut c_void {
if table.is_null() {
return ptr::null_mut();
}
match unsafe { (*table).insert(key.cast::<i8>(), VoidPointer(data.cast::<i8>())) } {
Some(v) => {
let raw = v.0;
mem::forget(v);
raw.cast::<c_void>()
}
None => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
pub unsafe extern "C" fn WMHashRemove(table: *mut HashTable, key: *mut c_void) {
if table.is_null() {
return;
}
unsafe {
(*table).remove(key.cast::<i8>());
}
}
/// Important note: this may leak memory if you don't pass the enumerator
/// back to [`WMFreeHashEnumerator`]. This is a breaking change from the
/// original C implementation, which did not require any resource cleanup.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMEnumerateHashTable(
table: *mut HashTable,
) -> *mut Enumerator<'static> {
if table.is_null() {
return ptr::null_mut();
}
let table = unsafe { &mut *table };
match table {
HashTable::PointerKeyed(m) => {
Box::leak(Box::new(Enumerator::PointerKeyed(m.iter_mut())))
}
HashTable::StringKeyed(m) => Box::leak(Box::new(Enumerator::StringKeyed(m.iter_mut()))),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFreeHashEnumerator(e: *mut Enumerator<'static>) {
if !e.is_null() {
let _ = unsafe { Box::from_raw(e) };
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMNextHashEnumeratorItem(e: *mut Enumerator<'static>) -> *mut c_void {
if e.is_null() {
return ptr::null_mut();
}
let e = unsafe { &mut *e };
match e {
Enumerator::PointerKeyed(i) => match i.next() {
Some((_, v)) => v.0.cast::<c_void>(),
None => ptr::null_mut(),
},
Enumerator::StringKeyed(i) => match i.next() {
Some((_, v)) => v.0.cast::<c_void>(),
None => ptr::null_mut(),
},
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMNextHashEnumeratorKey(e: *mut Enumerator<'static>) -> *mut c_void {
if e.is_null() {
return ptr::null_mut();
}
let e = unsafe { &mut *e };
match e {
Enumerator::PointerKeyed(i) => match i.next() {
Some((k, _)) => k.0.cast::<c_void>(),
None => ptr::null_mut(),
},
Enumerator::StringKeyed(i) => match i.next() {
Some((k, _)) => k.0.cast::<c_void>(),
None => ptr::null_mut(),
},
}
}
}

View File

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