Compare commits

Author SHA1 Message Date
trurl d3dac752cc Indent with tabs, as is standard for this file. 2025-12-08 13:31:05 -05:00
trurl 0893be1cea Use BTreeMap instead of HashMap for notification subscriptions.
This allows us to initialize the global NotificationCenter singleton in a const
context, which eliminates the need for OnceLock.
2025-12-08 13:24:44 -05:00
trurl d8057575ce Use a helper method for adding notification listeners. 2025-12-08 13:14:12 -05:00
trurl d8912c58e6 Fix typo in comment. 2025-12-08 13:06:58 -05:00
trurl b7f765e3f6 Rewrite WINGs/notification.c in Rust. 2025-12-08 13:06:04 -05:00
trurl d88d626fbe Remove direct creation and posting of notifications from WINGs.
We can reduce the WMNotification API surface area further by getting rid of
WMCreateNotification and WMPostNotification. WMNotification remains a
first-class object, but it is no longer possible for client code to create a
notification object directly. Notifications must now be posted through
WMPostNotificationName, which handles notification creation and destruction on
its own.

This will simplify the notification lifecycle and make the Rust rewrite
simpler. (Notifications no longer need to be reference-counted, heap-allocated
objects that might be saved somewhere after they are dispatched.)

WTextField code which reused the WMNotification struct has been modified to take
parameters of the correct type directly, instead of through a WMNotification's
void* data field.
2025-12-08 13:05:20 -05:00
trurl 5847e9d68f Ditch the notification function WMRemoveNotificationObserverWithName.
This reduces the notification API in a way that is helpful for rewriting in
Rust. This function is only used in one place, and the object that is being
deregistered is free'd immediately after WMRemoveNotificationObserverWithName is
called, so it should be safe just to use WMRemoveNotificationObserver (since I
hope it's an error to keep a notification to a free'd object registered).
2025-12-08 13:05:20 -05:00
trurl adb967ab15 Drop dead notification queue code.
This appears to have been used by now-defunct support for network
connections (WMConnection). No live code instantiates a notification queue or
pushes/dequeues notifications from a notification queue. (The global
NotificationCenter in WINGs/notification.c is still in use, so it is not going
anywhere in this commit.)
2025-12-08 13:05:20 -05:00
trurl 0c4d78a53d Drop unused macro (dead code) that invokes WMCreateNotification. 2025-12-08 13:05:20 -05:00
trurl dcd45f0677 Make some unused public WMNotification APIs private.
`WMRetainNotification`, `WMGetDefaultNotificationQueue`,
`WMDequeueNotificationMatching`, `WMEnqueueNotification`, and
`WMEnqueueCoalesceNotification`, are only used in WINGs/notification.c. To
reduce the API surface area that needs to be migrated to Rust, they are being
made private.
2025-12-08 13:05:20 -05:00
trurl 0097d1819e Move functions on Bag which were only called by FFI code inline. 2025-12-08 13:00:14 -05:00
trurl 8270124869 Nix dead code that was left commented out. 2025-12-08 13:00:14 -05:00
trurl 89183f3bcb Rewrite WMBag in Rust.
We should eventually get rid of this entirely, in favor of something along the
lines of a sorted Vec that is fully typed (so the payload isn't just a void*).
2025-12-08 13:00:14 -05:00
trurl 46e540b1b1 Prune the WMBag API in preparation for rewriting it in Rust. 2025-12-08 13:00:14 -05:00
trurl 9802b684ae Rewrite WINGs/tree.c in Rust.
This is a bit of a red herring, since WMTree is only used in wmmenugen, which I
don't think I've ever directly used. See notes at the top of tree.rs for more
musings on whether this should be in wutil-rs at all.
2025-12-08 12:48:37 -05:00
trurl 0a04a4c12e Remove depthFirst parameter from WMTreeWalk.
WMTreeWalk is only called once, with depthFirst set to true, so we might as well
just hard-code that behavior.

Also, this parameter appears to have been misnamed (since the search is always DFS)
and should properly be named to indicate that it controls if this is a pre- or
post-order traversal.
2025-12-08 12:47:39 -05:00
trurl e1a263cc5b Prune unused WMTree API functions.
This is a first step towards migrating WINGs/tree.c to Rust.

Happy finding: no parent pointer are actually needed, so we don't have to use a
shared structure for node pointers!
2025-12-08 12:47:39 -05:00
trurl c298b5f96f Avoid an unnecessary allocation in wtrimspace. 2025-12-08 12:40:33 -05:00
trurl dfd77b11a9 Don't scan ahead unnecessarily in wstrndup. 2025-12-08 12:40:33 -05:00
trurl 927cc93e0a Add string.rs to Makefile.am for wutil-rs.
Without this, `make` won't automatically rebuild wutil-rs when string.rs
changes.
2025-12-08 12:40:33 -05:00
trurl d2046de7ff Unit tests for wtokenjoin. 2025-12-08 12:40:33 -05:00
trurl a7a44397a4 Rewrite all functions from WUtils string.c in Rust.
These functions should be gotten rid of as we transition to Rust, but replacing
them with minimally tested Rust code should suffice as a first step.
2025-12-08 12:40:33 -05:00
trurl 72a1f8cb9e Throw some comments around the FST table for tokenizing command line.
This is slated for replacement, but it will help to be better documented first.
2025-12-08 12:40:33 -05:00
trurl 60902b9222 Use system strlcpy/strlcat instead of packaging our own.
These functions were added to glibc 2.38, so we don't even need to check for
libbsd. There isn't a strong need to worry about supporting older systems
because use of these functions should go away as we rewrite string.c in
Rust. And, apparently, they are not held in high regard historically. That's at
least 2 reasons to be rid of them.
2025-12-08 12:40:33 -05:00
trurl 46af2c27ee Tweak start-captive-wmaker.sh to behave a little more nicely.
* Select $DISPLAY dynamically because X11 likes :0 and Wayland likes :1 and who
knows what else might like some other value.
* Kill Xephyr after wmaker exits.
* 640x480 should be big enough for anyone. (And the window shouldn't get in the
way so much.)
2025-10-28 21:30:34 -04:00
trurl 026426e6c3 Remove VoidPointer impls that were commented out in previous commit. 2025-10-25 12:41:52 -04:00
trurl 564501953f Use wfree instead of XFree in a few more places.
This fixes some crashes found in cursory smoke tests. You can now open and close
some windows without crashing immediately.
2025-10-25 01:53:23 -04:00
trurl 8c68f95291 Update calls to malloc/free, etc., to use wmalloc/wfree/...
This fixes a lot of memory bugs which arose as a result of doing something
different from the system malloc/free when allocators were rewritten in Rust.

These changes originate from a different approach to writing the allocator in
Rust:
https://git.sdf.org/vitrine/wmaker/pulls/1/files#diff-04a0fd2319b9969373b75377716e45c836d22869

There are other function calls (to XFree) that need to be fixed, but that can be
done in another commit. This one is already getting large.
2025-10-24 15:35:07 -04:00
trurl d66eb34f16 Satisfy the dangerous_implicit_autorefs lint. 2025-10-23 16:26:47 -04:00
trurl 65726a1e6a WMHashTable ownership: it doesn't own keys or values.
The WMHashTable rewrite was prematurely merged into refactor/wutil-rs, so we're
picking up the pieces now.
2025-10-23 16:25:37 -04:00
trurl fbd6400186 Remove stale reference to libwings_rs (which should have been deleted in a rebase). 2025-10-23 16:25:01 -04:00
trurl 4f4dcf551b Restore proplist.c, which was clobbered by mistake during a rebase.
Lessons learned: don't rebase so freely, review commits properly.
2025-10-23 15:40:50 -04:00
trurl bd61e58821 Don't pass wmalloc'd memory to XFree.
This is enough to get the wmaker into a minimally running state. We should
complete this change by reviewing vitrine/wmaker#1.
2025-10-23 14:46:56 -04:00
trurl 1a8d99b2c0 PropList array items should return NULL on OOB index access. 2025-10-23 14:46:56 -04:00
trurl 7bded0055f Drop unused wAbort function from WPrefs.app. 2025-10-23 14:46:56 -04:00
trurl 32c40643c2 Replace WUtil hashtable with a Rust impl.
This tweaks the hashtable API, and it is incomplete because the WUtil proplist
impl depends heavily on a feature of the old API that is being discontinued.

Moving the proplist code into Rust is our next objective.
2025-10-23 14:46:56 -04:00
trurl 157a8e0d5a Eliminate the retainKey and releaseKey hashtable callbacks.
These fields are only ever NULL, so there's no reason to keep them.
2025-10-23 14:46:56 -04:00
trurl 252362545e Drop WMStringHashCallbacks, which is unused. 2025-10-23 14:46:56 -04:00
trurl f69227ce19 Remove support for PropList data nodes.
This type of PropList entry appears to be completely unused, so let's be rid of
it. This can be reversed in the future if we do want more complete support for
property lists, but for now it's code that we don't need.
2025-10-23 14:46:56 -04:00
trurl bc163d13f6 Provide alloc_string impl. 2025-10-23 14:46:56 -04:00
trurl 2d2dd9febe Chasing memory bugs: be consistent about wmalloc/wfree. 2025-10-23 14:46:56 -04:00
trurl 14c316615e Chasing memory bugs in memory.rs: copy only the payload length in wrealloc. 2025-10-23 14:46:56 -04:00
trurl 6a98614d13 Clarify wmalloc contract - it's safe to read before writing. 2025-10-23 14:46:56 -04:00
trurl dfdaa67b4d Chasing memory bugs in memory.rs: allocate the right layout. 2025-10-23 14:46:56 -04:00
trurl e6fd7e49f8 Clean things up a little bit with refutable let. 2025-10-23 14:46:56 -04:00
trurl 12930739ec Allocate PropList description with memory::alloc_bytes.
All memory allocations passed back from FFI functions should be allocated with
`memory::alloc_bytes`, so that C code can call `memory::free_bytes` when it's
done with them.
2025-10-23 14:46:56 -04:00
trurl cf588d6e27 Simplify path_from_cstr substantially (h/t cross). 2025-10-23 14:46:56 -04:00
trurl f3961ba66f Reimplement the PropList data structure in Rust.
While this large change has some unit tests, it has not been integration tested
thoroughly. Removing the global case insensitivity flag may be an issue in
particular.

A few of PropList API functions have been modified (mostly to get rid of
varargs). The definition of one such function has been left in C for cleanup
later.
2025-10-23 14:46:56 -04:00
trurl 5b593fb19a Expose the GSUSER_SUBDIR preprocessor symbol to Rust.
This symbol's value must be known to port `wmkdirhier` and `wrmdirhier` from
`proplist.c` to Rust.

This change introduces a basic C library under wutil-rs that is linked into the
Rust code to expose preprocessor symbols and other Autotools configuration
decisions to Rust. See the rust rewrite notes at the top of
`wutil-rs/src/defines.rs` for further thoughts.
2025-10-23 14:46:56 -04:00
trurl f2e9123db6 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-23 14:46:56 -04:00
trurl 791149fe70 Correct oversight in how WMMapArray is supposed to work. 2025-10-23 14:46:56 -04:00
trurl 52b0e6b182 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-23 14:46:56 -04:00
trurl bbcf40ee47 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-23 14:46:56 -04:00
trurl 9d07e2d3d8 Eliminate the unused WINGs function WMGetSubdataWithRange. 2025-10-23 14:46:56 -04:00
trurl dea8c36cd5 Eliminate the unused WINGs function WMCreatePLDataWithBytesNoCopy. 2025-10-23 14:46:56 -04:00
trurl 3beadbb6cb Forgot to add new array.rs impl. 2025-10-23 14:46:56 -04:00
trurl dd36130730 Fix const qualifier on strings returned by wgethomedir. 2025-10-23 14:46:56 -04:00
trurl 6ede7a5cb0 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-23 14:46:56 -04:00
trurl 2b9b915768 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-23 14:46:56 -04:00
trurl d50adaa1c8 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-23 14:46:56 -04:00
trurl 5b0ad78f01 Remember to AC_SUBST the Rust compiler environment variables so they're visible in Makefiles. 2025-10-23 14:46:56 -04:00
trurl 46fcbb0ff1 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-10-23 14:46:56 -04:00
trurl e3fb8ddbc8 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-10-23 14:46:56 -04:00
trurl 59ad67f4dc Provide a janky script for running Window Maker in a captive Xephyr X server.
If this becomes a permanent fixture, hard-coded values should be fixed.
2025-10-23 14:39:53 -04:00
92 changed files with 6530 additions and 6059 deletions
+1 -1
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 \
+6 -11
View File
@@ -10,14 +10,17 @@ 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 = $(wutilrs)
EXTRA_DIST = BUGS make-rgb Examples Extras Tests
# wbutton.c
libWINGs_la_SOURCES = \
configuration.c \
@@ -62,23 +65,15 @@ libWINGs_la_SOURCES = \
wwindow.c
libWUtil_la_SOURCES = \
array.c \
bagtree.c \
data.c \
error.c \
error.h \
findfile.c \
handlers.c \
hashtable.c \
memory.c \
menuparser.c \
menuparser.h \
menuparser_macros.c \
misc.c \
notification.c \
proplist.c \
string.c \
tree.c \
userdefaults.c \
userdefaults.h \
usleep.c \
+7 -10
View File
@@ -233,7 +233,7 @@ typedef enum {
/* text movement types */
enum {
typedef enum {
WMIllegalTextMovement,
WMReturnTextMovement,
WMEscapeTextMovement,
@@ -243,13 +243,13 @@ enum {
WMRightTextMovement,
WMUpTextMovement,
WMDownTextMovement
};
} WMTextMovementType;
/* text field special events */
enum {
typedef enum {
WMInsertTextEvent,
WMDeleteTextEvent
};
} WMTextFieldSpecialEventType;
enum {
@@ -533,14 +533,11 @@ typedef struct WMBrowserDelegate {
typedef struct WMTextFieldDelegate {
void *data;
void (*didBeginEditing)(struct WMTextFieldDelegate *self,
WMNotification *notif);
void (*didBeginEditing)(struct WMTextFieldDelegate *self, WMTextMovementType reason);
void (*didChange)(struct WMTextFieldDelegate *self,
WMNotification *notif);
void (*didChange)(struct WMTextFieldDelegate *self, WMTextFieldSpecialEventType reason);
void (*didEndEditing)(struct WMTextFieldDelegate *self,
WMNotification *notif);
void (*didEndEditing)(struct WMTextFieldDelegate *self, WMTextMovementType reason);
Bool (*shouldBeginEditing)(struct WMTextFieldDelegate *self,
WMTextField *tPtr);
-4
View File
@@ -378,10 +378,6 @@ void W_InitNotificationCenter(void);
void W_ReleaseNotificationCenter(void);
void W_FlushASAPNotificationQueue(void);
void W_FlushIdleNotificationQueue(void);
/* ---[ selection.c ]----------------------------------------------------- */
+32 -192
View File
@@ -169,15 +169,11 @@ 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;
typedef int WMArrayIterator;
typedef void *WMBagIterator;
typedef int WMBagIterator;
typedef void WMNotificationObserverAction(void *observerData,
@@ -213,10 +209,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,11 +240,14 @@ 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 ]------------------------------------------------ */
/*
* Creates the directory path and all its parents.
*/
int wmkdirhier(const char *path);
int wrmdirhier(const char *path);
@@ -276,14 +271,9 @@ char* wstrconcat(const char *str1, const char *str2);
* so always assign the returned address to avoid dangling pointers. */
char* wstrappend(char *dst, const char *src);
size_t wstrlcpy(char *, const char *, size_t);
size_t wstrlcat(char *, const char *, size_t);
void wtokensplit(char *command, char ***argv, int *argc);
char* wtokennext(char *word, char **next);
char* wtokenjoin(char **list, int count);
void wtokenfree(char **tokens, int count);
@@ -342,7 +332,8 @@ void WHandleEvents(void);
/* ---[ WINGs/hashtable.c ]----------------------------------------------- */
WMHashTable* WMCreateHashTable(const WMHashTableCallbacks callbacks);
WMHashTable* WMCreateIdentityHashTable();
WMHashTable* WMCreateStringHashTable();
void WMFreeHashTable(WMHashTable *table);
@@ -392,15 +383,11 @@ 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.
@@ -422,29 +409,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 +432,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 +460,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);
@@ -500,7 +479,7 @@ void* WMArrayPrevious(WMArray *array, WMArrayIterator *iter);
for (var = WMArrayLast(array, &(i)); (i) != WANotFound; \
var = WMArrayPrevious(array, &(i)))
/* ---[ WINGs/bagtree.c ]------------------------------------------------- */
/* ---[ wutil-rs/src/bag.rs ]------------------------------------------------- */
/*
* Tree bags use a red-black tree for storage.
@@ -515,58 +494,16 @@ void* WMArrayPrevious(WMArray *array, WMArrayIterator *iter);
* Slow for storing small numbers of elements
*/
#define WMCreateBag(size) WMCreateTreeBag()
#define WMCreateBagWithDestructor(size, d) WMCreateTreeBagWithDestructor(d)
WMBag* WMCreateTreeBag(void);
WMBag* WMCreateTreeBagWithDestructor(WMFreeDataProc *destructor);
int WMGetBagItemCount(WMBag *bag);
void WMAppendBag(WMBag *bag, WMBag *other);
void WMPutInBag(WMBag *bag, void *item);
/* insert will increment the index of elements after it by 1 */
void WMInsertInBag(WMBag *bag, int index, void *item);
/* erase will remove the element from the bag,
* but will keep the index of the other elements unchanged */
int WMEraseFromBag(WMBag *bag, int index);
/* delete and remove will remove the elements and cause the elements
* after them to decrement their indexes by 1 */
int WMDeleteFromBag(WMBag *bag, int index);
int WMRemoveFromBag(WMBag *bag, void *item);
void* WMGetFromBag(WMBag *bag, int index);
void* WMReplaceInBag(WMBag *bag, int index, void *item);
#define WMSetInBag(bag, index, item) WMReplaceInBag(bag, index, item)
/* comparer must return:
* < 0 if a < b
* > 0 if a > b
* = 0 if a = b
*/
void WMSortBag(WMBag *bag, WMCompareDataProc *comparer);
void WMSetInBag(WMBag *bag, int index, void *item);
void WMEmptyBag(WMBag *bag);
void WMFreeBag(WMBag *bag);
void WMMapBag(WMBag *bag, void (*function)(void*, void*), void *data);
int WMGetFirstInBag(WMBag *bag, void *item);
int WMCountInBag(WMBag *bag, void *item);
int WMFindInBag(WMBag *bag, WMMatchDataProc *match, void *cdata);
void* WMBagFirst(WMBag *bag, WMBagIterator *ptr);
void* WMBagLast(WMBag *bag, WMBagIterator *ptr);
@@ -578,16 +515,11 @@ void* WMBagPrevious(WMBag *bag, WMBagIterator *ptr);
void* WMBagIteratorAtIndex(WMBag *bag, int index, WMBagIterator *ptr);
int WMBagIndexForIterator(WMBag *bag, WMBagIterator ptr);
/* The following 2 macros assume that the bag doesn't change in the for loop */
#define WM_ITERATE_BAG(bag, var, i) \
for (var = WMBagFirst(bag, &(i)); (i) != NULL; \
var = WMBagNext(bag, &(i)))
/* The following macro assumes that the bag doesn't change in the for loop */
#define WM_ETARETI_BAG(bag, var, i) \
for (var = WMBagLast(bag, &(i)); (i) != NULL; \
for (var = WMBagLast(bag, &(i)); (i) >= 0; \
var = WMBagPrevious(bag, &(i)))
@@ -604,37 +536,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 +560,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);
@@ -661,14 +568,12 @@ void WMSetDataFormat(WMData *aData, unsigned format);
unsigned WMGetDataFormat(WMData *aData);
/* Storing data */
/* ---[ WINGs/tree.c ]---------------------------------------------------- */
/* ---[ wutil-rs/src/tree.rs ]---------------------------------------------------- */
/* Generic Tree and TreeNode */
WMTreeNode* WMCreateTreeNode(void *data);
WMTreeNode* WMCreateTreeNodeWithDestructor(void *data, WMFreeDataProc *destructor);
WMTreeNode* WMInsertItemInTree(WMTreeNode *parent, int index, void *item);
#define WMAddItemToTree(parent, item) WMInsertItemInTree(parent, -1, item)
@@ -677,48 +582,23 @@ WMTreeNode* WMInsertNodeInTree(WMTreeNode *parent, int index, WMTreeNode *aNode)
#define WMAddNodeToTree(parent, aNode) WMInsertNodeInTree(parent, -1, aNode)
void WMDestroyTreeNode(WMTreeNode *aNode);
void WMDeleteLeafForTreeNode(WMTreeNode *aNode, int index);
void WMRemoveLeafForTreeNode(WMTreeNode *aNode, void *leaf);
void* WMReplaceDataForTreeNode(WMTreeNode *aNode, void *newData);
void* WMGetDataForTreeNode(WMTreeNode *aNode);
int WMGetTreeNodeDepth(WMTreeNode *aNode);
WMTreeNode* WMGetParentForTreeNode(WMTreeNode *aNode);
/* Sort only the leaves of the passed node */
void WMSortLeavesForTreeNode(WMTreeNode *aNode, WMCompareDataProc *comparer);
/* Sort all tree recursively starting from the passed node */
void WMSortTree(WMTreeNode *aNode, WMCompareDataProc *comparer);
/* Returns the first node which matches node's data with cdata by 'match' */
WMTreeNode* WMFindInTree(WMTreeNode *aTree, WMMatchDataProc *match, void *cdata);
void WMSortTree(WMTreeNode *aNode, int (*comparer)(const WMTreeNode *a, const WMTreeNode *b));
/* Returns the first node where node's data matches cdata by 'match' and node is
* at most `limit' depths down from `aTree'. */
WMTreeNode *WMFindInTreeWithDepthLimit(WMTreeNode * aTree, WMMatchDataProc * match, void *cdata, int limit);
/* Returns first tree node that has data == cdata */
#define WMGetFirstInTree(aTree, cdata) WMFindInTree(aTree, NULL, cdata)
WMTreeNode *WMFindInTreeWithDepthLimit(WMTreeNode * aTree, int (*match)(const WMTreeNode *item, const void *cdata), void *cdata, int limit);
/* Walk every node of aNode with `walk' */
void WMTreeWalk(WMTreeNode *aNode, WMTreeWalkProc * walk, void *data, Bool DepthFirst);
void WMTreeWalk(WMTreeNode *aNode, WMTreeWalkProc * walk, void *data);
/* ---[ WINGs/data.c ]---------------------------------------------------- */
/* ---[ WINGs/notification.c ]---------------------------------------------------- */
WMNotification* WMCreateNotification(const char *name, void *object, void *clientData);
void WMReleaseNotification(WMNotification *notification);
WMNotification* WMRetainNotification(WMNotification *notification);
void* WMGetNotificationClientData(WMNotification *notification);
void* WMGetNotificationObject(WMNotification *notification);
@@ -729,52 +609,27 @@ const char* WMGetNotificationName(WMNotification *notification);
void WMAddNotificationObserver(WMNotificationObserverAction *observerAction,
void *observer, const char *name, void *object);
void WMPostNotification(WMNotification *notification);
void WMRemoveNotificationObserver(void *observer);
void WMRemoveNotificationObserverWithName(void *observer, const char *name,
void *object);
void WMPostNotificationName(const char *name, void *object, void *clientData);
WMNotificationQueue* WMGetDefaultNotificationQueue(void);
WMNotificationQueue* WMCreateNotificationQueue(void);
void WMDequeueNotificationMatching(WMNotificationQueue *queue,
WMNotification *notification,
unsigned mask);
void WMEnqueueNotification(WMNotificationQueue *queue,
WMNotification *notification,
WMPostingStyle postingStyle);
void WMEnqueueCoalesceNotification(WMNotificationQueue *queue,
WMNotification *notification,
WMPostingStyle postingStyle,
unsigned coalesceMask);
/* Property Lists handling */
/* ---[ WINGs/proplist.c ]------------------------------------------------ */
/* Property Lists handling */
WMPropList* WMCreatePLArray(WMPropList *elem, ...);
void WMPLSetCaseSensitive(Bool caseSensitive);
/* ---[ wutil-rs/src/prop_list.rs ]--------------------------------------- */
WMPropList* WMCreatePLString(const char *str);
WMPropList* WMCreatePLData(WMData *data);
WMPropList* WMCreatePLArrayFromSlice(WMPropList *elems, unsigned int length);
WMPropList* WMCreatePLDataWithBytes(const unsigned char *bytes, unsigned int length);
WMPropList* WMCreateEmptyPLArray();
WMPropList* WMCreatePLDataWithBytesNoCopy(unsigned char *bytes,
unsigned int length,
WMFreeDataProc *destructor);
WMPropList* WMCreatePLDictionary(WMPropList *key, WMPropList *value);
WMPropList* WMCreatePLArray(WMPropList *elem, ...);
WMPropList* WMCreatePLDictionary(WMPropList *key, WMPropList *value, ...);
WMPropList* WMCreateEmptyPLDictionary();
WMPropList* WMRetainPropList(WMPropList *plist);
@@ -812,8 +667,6 @@ int WMGetPropListItemCount(WMPropList *plist);
Bool WMIsPLString(WMPropList *plist);
Bool WMIsPLData(WMPropList *plist);
Bool WMIsPLArray(WMPropList *plist);
Bool WMIsPLDictionary(WMPropList *plist);
@@ -823,14 +676,6 @@ Bool WMIsPropListEqualTo(WMPropList *plist, WMPropList *other);
/* Returns a reference. Do not free it! */
char* WMGetFromPLString(WMPropList *plist);
/* Returns a reference. Do not free it! */
WMData* WMGetFromPLData(WMPropList *plist);
/* Returns a reference. Do not free it! */
const unsigned char* WMGetPLDataBytes(WMPropList *plist);
int WMGetPLDataLength(WMPropList *plist);
/* Returns a reference. */
WMPropList* WMGetFromPLArray(WMPropList *plist, int index);
@@ -838,14 +683,9 @@ WMPropList* WMGetFromPLArray(WMPropList *plist, int index);
WMPropList* WMGetFromPLDictionary(WMPropList *plist, WMPropList *key);
/* Returns a PropList array with all the dictionary keys. Release it when
* you're done. Keys in array are retained from the original dictionary
* not copied and need NOT to be released individually. */
* you're done. */
WMPropList* WMGetPLDictionaryKeys(WMPropList *plist);
/* Creates only the first level deep object. All the elements inside are
* retained from the original */
WMPropList* WMShallowCopyPropList(WMPropList *plist);
/* Makes a completely separate replica of the original proplist */
WMPropList* WMDeepCopyPropList(WMPropList *plist);
-363
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;
}
}
-745
View File
@@ -1,745 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "WUtil.h"
typedef struct W_Node {
struct W_Node *parent;
struct W_Node *left;
struct W_Node *right;
int color;
void *data;
int index;
} W_Node;
typedef struct W_Bag {
W_Node *root;
W_Node *nil; /* sentinel */
int count;
void (*destructor) (void *item);
} W_Bag;
#define IS_LEFT(node) (node == node->parent->left)
#define IS_RIGHT(node) (node == node->parent->right)
static void leftRotate(W_Bag * tree, W_Node * node)
{
W_Node *node2;
node2 = node->right;
node->right = node2->left;
node2->left->parent = node;
node2->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = node2;
} else {
if (IS_LEFT(node)) {
node->parent->left = node2;
} else {
node->parent->right = node2;
}
}
node2->left = node;
node->parent = node2;
}
static void rightRotate(W_Bag * tree, W_Node * node)
{
W_Node *node2;
node2 = node->left;
node->left = node2->right;
node2->right->parent = node;
node2->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = node2;
} else {
if (IS_LEFT(node)) {
node->parent->left = node2;
} else {
node->parent->right = node2;
}
}
node2->right = node;
node->parent = node2;
}
static void treeInsert(W_Bag * tree, W_Node * node)
{
W_Node *y = tree->nil;
W_Node *x = tree->root;
while (x != tree->nil) {
y = x;
if (node->index <= x->index)
x = x->left;
else
x = x->right;
}
node->parent = y;
if (y == tree->nil)
tree->root = node;
else if (node->index <= y->index)
y->left = node;
else
y->right = node;
}
static void rbTreeInsert(W_Bag * tree, W_Node * node)
{
W_Node *y;
treeInsert(tree, node);
node->color = 'R';
while (node != tree->root && node->parent->color == 'R') {
if (IS_LEFT(node->parent)) {
y = node->parent->parent->right;
if (y->color == 'R') {
node->parent->color = 'B';
y->color = 'B';
node->parent->parent->color = 'R';
node = node->parent->parent;
} else {
if (IS_RIGHT(node)) {
node = node->parent;
leftRotate(tree, node);
}
node->parent->color = 'B';
node->parent->parent->color = 'R';
rightRotate(tree, node->parent->parent);
}
} else {
y = node->parent->parent->left;
if (y->color == 'R') {
node->parent->color = 'B';
y->color = 'B';
node->parent->parent->color = 'R';
node = node->parent->parent;
} else {
if (IS_LEFT(node)) {
node = node->parent;
rightRotate(tree, node);
}
node->parent->color = 'B';
node->parent->parent->color = 'R';
leftRotate(tree, node->parent->parent);
}
}
}
tree->root->color = 'B';
}
static void rbDeleteFixup(W_Bag * tree, W_Node * node)
{
W_Node *w;
while (node != tree->root && node->color == 'B') {
if (IS_LEFT(node)) {
w = node->parent->right;
if (w->color == 'R') {
w->color = 'B';
node->parent->color = 'R';
leftRotate(tree, node->parent);
w = node->parent->right;
}
if (w->left->color == 'B' && w->right->color == 'B') {
w->color = 'R';
node = node->parent;
} else {
if (w->right->color == 'B') {
w->left->color = 'B';
w->color = 'R';
rightRotate(tree, w);
w = node->parent->right;
}
w->color = node->parent->color;
node->parent->color = 'B';
w->right->color = 'B';
leftRotate(tree, node->parent);
node = tree->root;
}
} else {
w = node->parent->left;
if (w->color == 'R') {
w->color = 'B';
node->parent->color = 'R';
rightRotate(tree, node->parent);
w = node->parent->left;
}
if (w->left->color == 'B' && w->right->color == 'B') {
w->color = 'R';
node = node->parent;
} else {
if (w->left->color == 'B') {
w->right->color = 'B';
w->color = 'R';
leftRotate(tree, w);
w = node->parent->left;
}
w->color = node->parent->color;
node->parent->color = 'B';
w->left->color = 'B';
rightRotate(tree, node->parent);
node = tree->root;
}
}
}
node->color = 'B';
}
static W_Node *treeMinimum(W_Node * node, W_Node * nil)
{
while (node->left != nil)
node = node->left;
return node;
}
static W_Node *treeMaximum(W_Node * node, W_Node * nil)
{
while (node->right != nil)
node = node->right;
return node;
}
static W_Node *treeSuccessor(W_Node * node, W_Node * nil)
{
W_Node *y;
if (node->right != nil) {
return treeMinimum(node->right, nil);
}
y = node->parent;
while (y != nil && node == y->right) {
node = y;
y = y->parent;
}
return y;
}
static W_Node *treePredecessor(W_Node * node, W_Node * nil)
{
W_Node *y;
if (node->left != nil) {
return treeMaximum(node->left, nil);
}
y = node->parent;
while (y != nil && node == y->left) {
node = y;
y = y->parent;
}
return y;
}
static W_Node *rbTreeDelete(W_Bag * tree, W_Node * node)
{
W_Node *nil = tree->nil;
W_Node *x, *y;
if (node->left == nil || node->right == nil) {
y = node;
} else {
y = treeSuccessor(node, nil);
}
if (y->left != nil) {
x = y->left;
} else {
x = y->right;
}
x->parent = y->parent;
if (y->parent == nil) {
tree->root = x;
} else {
if (IS_LEFT(y)) {
y->parent->left = x;
} else {
y->parent->right = x;
}
}
if (y != node) {
node->index = y->index;
node->data = y->data;
}
if (y->color == 'B') {
rbDeleteFixup(tree, x);
}
return y;
}
static W_Node *treeSearch(W_Node * root, W_Node * nil, int index)
{
if (root == nil || root->index == index) {
return root;
}
if (index < root->index) {
return treeSearch(root->left, nil, index);
} else {
return treeSearch(root->right, nil, index);
}
}
static W_Node *treeFind(W_Node * root, W_Node * nil, void *data)
{
W_Node *tmp;
if (root == nil || root->data == data)
return root;
tmp = treeFind(root->left, nil, data);
if (tmp != nil)
return tmp;
tmp = treeFind(root->right, nil, data);
return tmp;
}
#if 0
static char buf[512];
static void printNodes(W_Node * node, W_Node * nil, int depth)
{
if (node == nil) {
return;
}
printNodes(node->left, nil, depth + 1);
memset(buf, ' ', depth * 2);
buf[depth * 2] = 0;
if (IS_LEFT(node))
printf("%s/(%2i\n", buf, node->index);
else
printf("%s\\(%2i\n", buf, node->index);
printNodes(node->right, nil, depth + 1);
}
void PrintTree(WMBag * bag)
{
W_TreeBag *tree = (W_TreeBag *) bag->data;
printNodes(tree->root, tree->nil, 0);
}
#endif
WMBag *WMCreateTreeBag(void)
{
return WMCreateTreeBagWithDestructor(NULL);
}
WMBag *WMCreateTreeBagWithDestructor(WMFreeDataProc * destructor)
{
WMBag *bag;
bag = wmalloc(sizeof(WMBag));
bag->nil = wmalloc(sizeof(W_Node));
bag->nil->left = bag->nil->right = bag->nil->parent = bag->nil;
bag->nil->index = WBNotFound;
bag->root = bag->nil;
bag->destructor = destructor;
return bag;
}
int WMGetBagItemCount(WMBag * self)
{
return self->count;
}
void WMAppendBag(WMBag * self, WMBag * bag)
{
WMBagIterator ptr;
void *data;
for (data = WMBagFirst(bag, &ptr); data != NULL; data = WMBagNext(bag, &ptr)) {
WMPutInBag(self, data);
}
}
void WMPutInBag(WMBag * self, void *item)
{
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = self->count;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
self->count++;
}
void WMInsertInBag(WMBag * self, int index, void *item)
{
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = index;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
while ((ptr = treeSuccessor(ptr, self->nil)) != self->nil) {
ptr->index++;
}
self->count++;
}
static int treeDeleteNode(WMBag * self, W_Node *ptr)
{
if (ptr != self->nil) {
W_Node *tmp;
self->count--;
tmp = treeSuccessor(ptr, self->nil);
while (tmp != self->nil) {
tmp->index--;
tmp = treeSuccessor(tmp, self->nil);
}
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
return 1;
}
return 0;
}
int WMRemoveFromBag(WMBag * self, void *item)
{
W_Node *ptr = treeFind(self->root, self->nil, item);
return treeDeleteNode(self, ptr);
}
int WMEraseFromBag(WMBag * self, int index)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
if (ptr != self->nil) {
self->count--;
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
wassertrv(self->count == 0 || self->root->index >= 0, 1);
return 1;
} else {
return 0;
}
}
int WMDeleteFromBag(WMBag * self, int index)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
return treeDeleteNode(self, ptr);
}
void *WMGetFromBag(WMBag * self, int index)
{
W_Node *node;
node = treeSearch(self->root, self->nil, index);
if (node != self->nil)
return node->data;
else
return NULL;
}
int WMGetFirstInBag(WMBag * self, void *item)
{
W_Node *node;
node = treeFind(self->root, self->nil, item);
if (node != self->nil)
return node->index;
else
return WBNotFound;
}
static int treeCount(W_Node * root, W_Node * nil, void *item)
{
int count = 0;
if (root == nil)
return 0;
if (root->data == item)
count++;
if (root->left != nil)
count += treeCount(root->left, nil, item);
if (root->right != nil)
count += treeCount(root->right, nil, item);
return count;
}
int WMCountInBag(WMBag * self, void *item)
{
return treeCount(self->root, self->nil, item);
}
void *WMReplaceInBag(WMBag * self, int index, void *item)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
void *old = NULL;
if (item == NULL) {
self->count--;
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
} else if (ptr != self->nil) {
old = ptr->data;
ptr->data = item;
} else {
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = index;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
self->count++;
}
return old;
}
void WMSortBag(WMBag * self, WMCompareDataProc * comparer)
{
void **items;
W_Node *tmp;
int i;
if (self->count == 0)
return;
items = wmalloc(sizeof(void *) * self->count);
i = 0;
tmp = treeMinimum(self->root, self->nil);
while (tmp != self->nil) {
items[i++] = tmp->data;
tmp = treeSuccessor(tmp, self->nil);
}
qsort(&items[0], self->count, sizeof(void *), comparer);
i = 0;
tmp = treeMinimum(self->root, self->nil);
while (tmp != self->nil) {
tmp->index = i;
tmp->data = items[i++];
tmp = treeSuccessor(tmp, self->nil);
}
wfree(items);
}
static void deleteTree(WMBag * self, W_Node * node)
{
if (node == self->nil)
return;
deleteTree(self, node->left);
if (self->destructor)
self->destructor(node->data);
deleteTree(self, node->right);
wfree(node);
}
void WMEmptyBag(WMBag * self)
{
deleteTree(self, self->root);
self->root = self->nil;
self->count = 0;
}
void WMFreeBag(WMBag * self)
{
WMEmptyBag(self);
wfree(self->nil);
wfree(self);
}
static void mapTree(W_Bag * tree, W_Node * node, void (*function) (void *, void *), void *data)
{
if (node == tree->nil)
return;
mapTree(tree, node->left, function, data);
(*function) (node->data, data);
mapTree(tree, node->right, function, data);
}
void WMMapBag(WMBag * self, void (*function) (void *, void *), void *data)
{
mapTree(self, self->root, function, data);
}
static int findInTree(W_Bag * tree, W_Node * node, WMMatchDataProc * function, void *cdata)
{
int index;
if (node == tree->nil)
return WBNotFound;
index = findInTree(tree, node->left, function, cdata);
if (index != WBNotFound)
return index;
if ((*function) (node->data, cdata)) {
return node->index;
}
return findInTree(tree, node->right, function, cdata);
}
int WMFindInBag(WMBag * self, WMMatchDataProc * match, void *cdata)
{
return findInTree(self, self->root, match, cdata);
}
void *WMBagFirst(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
node = treeMinimum(self->root, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagLast(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
node = treeMaximum(self->root, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagNext(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
if (*ptr == NULL)
return NULL;
node = treeSuccessor(*ptr, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagPrevious(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
if (*ptr == NULL)
return NULL;
node = treePredecessor(*ptr, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagIteratorAtIndex(WMBag * self, int index, WMBagIterator * ptr)
{
W_Node *node;
node = treeSearch(self->root, self->nil, index);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
int WMBagIndexForIterator(WMBag * bag, WMBagIterator ptr)
{
/* Parameter not used, but tell the compiler that it is ok */
(void) bag;
return ((W_Node *) ptr)->index;
}
-289
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 */
+1 -1
View File
@@ -498,7 +498,7 @@ static void registerDescriptionList(WMScreen * scr, WMView * view, WMArray * ope
for (i = 0; i < count; i++) {
text = WMGetDragOperationItemText(WMGetFromArray(operationArray, i));
wstrlcpy(textListItem, text, size);
strlcpy(textListItem, text, size);
/* to next text offset */
textListItem = &(textListItem[strlen(textListItem) + 1]);
+2 -429
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)
{
@@ -401,8 +76,8 @@ char *wfindfileinarray(WMPropList *array, const char *file)
path = wmalloc(len + flen + 2);
path = memcpy(path, p, len);
path[len] = 0;
if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
if (strlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
strlcat(path, file, len + flen + 2) >= len + flen + 2) {
wfree(path);
return NULL;
}
@@ -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 = wmalloc(buffer_size);
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:
wfree(buffer);
close(fd_src);
unlink(path_dst);
wfree(path_dst);
return -1;
}
wfree(buffer);
wfree(path_dst);
close(fd_src);
return 0;
}
+3 -15
View File
@@ -274,12 +274,11 @@ Bool W_CheckIdleHandlers(void)
WMArrayIterator iter;
if (!idleHandler || WMGetArrayItemCount(idleHandler) == 0) {
W_FlushIdleNotificationQueue();
/* make sure an observer in queue didn't added an idle handler */
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 */
@@ -292,8 +291,6 @@ Bool W_CheckIdleHandlers(void)
WMFreeArray(handlerCopy);
W_FlushIdleNotificationQueue();
/* this is not necesarrily False, because one handler can re-add itself */
return (WMGetArrayItemCount(idleHandler) > 0);
}
@@ -304,7 +301,6 @@ void W_CheckTimerHandlers(void)
struct timeval now;
if (!timerHandler) {
W_FlushASAPNotificationQueue();
return;
}
@@ -331,8 +327,6 @@ void W_CheckTimerHandlers(void)
wfree(handler);
}
}
W_FlushASAPNotificationQueue();
}
/*
@@ -384,7 +378,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
nfds = 0;
if (!extrafd && nfds == 0) {
W_FlushASAPNotificationQueue();
return False;
}
@@ -429,7 +422,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 */
@@ -461,8 +454,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
wfree(fds);
W_FlushASAPNotificationQueue();
return (count > 0);
#else
#ifdef HAVE_SELECT
@@ -479,7 +470,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
nfds = 0;
if (inputfd < 0 && nfds == 0) {
W_FlushASAPNotificationQueue();
return False;
}
@@ -527,7 +517,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 */
@@ -556,8 +546,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
WMFreeArray(handlerCopy);
}
W_FlushASAPNotificationQueue();
return (count > 0);
#else /* not HAVE_SELECT, not HAVE_POLL */
# error Neither select nor poll. You lose.
-422
View File
@@ -1,422 +0,0 @@
#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
};
-55
View File
@@ -1,55 +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 <sys/types.h>
#include "WUtil.h"
#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <unistd.h>
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;
}
+3 -1
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++;
}
+2 -2
View File
@@ -652,7 +652,7 @@ static void mpm_get_hostname(WParserMacro *this, WMenuParser parser)
return;
}
}
wstrlcpy((char *) this->value, h, sizeof(this->value) );
strlcpy((char *) this->value, h, sizeof(this->value) );
}
/* Name of the current user */
@@ -677,7 +677,7 @@ static void mpm_get_user_name(WParserMacro *this, WMenuParser parser)
user = pw_user->pw_name;
if (user == NULL) goto error_no_username;
}
wstrlcpy((char *) this->value, user, sizeof(this->value) );
strlcpy((char *) this->value, user, sizeof(this->value) );
}
/* Number id of the user under which we are running */
-482
View File
@@ -1,482 +0,0 @@
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "WUtil.h"
#include "WINGsP.h"
typedef struct W_Notification {
const char *name;
void *object;
void *clientData;
int refCount;
} Notification;
const char *WMGetNotificationName(WMNotification * notification)
{
return notification->name;
}
void *WMGetNotificationObject(WMNotification * notification)
{
return notification->object;
}
void *WMGetNotificationClientData(WMNotification * notification)
{
return notification->clientData;
}
WMNotification *WMCreateNotification(const char *name, void *object, void *clientData)
{
Notification *nPtr;
nPtr = wmalloc(sizeof(Notification));
nPtr->name = name;
nPtr->object = object;
nPtr->clientData = clientData;
nPtr->refCount = 1;
return nPtr;
}
void WMReleaseNotification(WMNotification * notification)
{
notification->refCount--;
if (notification->refCount < 1) {
wfree(notification);
}
}
WMNotification *WMRetainNotification(WMNotification * notification)
{
notification->refCount++;
return notification;
}
/***************** Notification Center *****************/
typedef struct NotificationObserver {
WMNotificationObserverAction *observerAction;
void *observer;
const char *name;
void *object;
struct NotificationObserver *prev; /* for tables */
struct NotificationObserver *next;
struct NotificationObserver *nextAction; /* for observerTable */
} NotificationObserver;
typedef struct W_NotificationCenter {
WMHashTable *nameTable; /* names -> observer lists */
WMHashTable *objectTable; /* object -> observer lists */
NotificationObserver *nilList; /* obervers that catch everything */
WMHashTable *observerTable; /* observer -> NotificationObserver */
} NotificationCenter;
/* default (and only) center */
static NotificationCenter *notificationCenter = NULL;
void W_InitNotificationCenter(void)
{
notificationCenter = wmalloc(sizeof(NotificationCenter));
notificationCenter->nameTable = WMCreateHashTable(WMStringPointerHashCallbacks);
notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks);
notificationCenter->nilList = NULL;
notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks);
}
void W_ReleaseNotificationCenter(void)
{
if (notificationCenter) {
if (notificationCenter->nameTable)
WMFreeHashTable(notificationCenter->nameTable);
if (notificationCenter->objectTable)
WMFreeHashTable(notificationCenter->objectTable);
if (notificationCenter->observerTable)
WMFreeHashTable(notificationCenter->observerTable);
wfree(notificationCenter);
notificationCenter = NULL;
}
}
void
WMAddNotificationObserver(WMNotificationObserverAction * observerAction,
void *observer, const char *name, void *object)
{
NotificationObserver *oRec, *rec;
oRec = wmalloc(sizeof(NotificationObserver));
oRec->observerAction = observerAction;
oRec->observer = observer;
oRec->name = name;
oRec->object = object;
oRec->next = NULL;
oRec->prev = NULL;
/* put this action in the list of actions for this observer */
rec = (NotificationObserver *) WMHashInsert(notificationCenter->observerTable, observer, oRec);
if (rec) {
/* if this is not the first action for the observer */
oRec->nextAction = rec;
} else {
oRec->nextAction = NULL;
}
if (!name && !object) {
/* catch-all */
oRec->next = notificationCenter->nilList;
if (notificationCenter->nilList) {
notificationCenter->nilList->prev = oRec;
}
notificationCenter->nilList = oRec;
} else if (!name) {
/* any message coming from object */
rec = (NotificationObserver *) WMHashInsert(notificationCenter->objectTable, object, oRec);
oRec->next = rec;
if (rec) {
rec->prev = oRec;
}
} else {
/* name && (object || !object) */
rec = (NotificationObserver *) WMHashInsert(notificationCenter->nameTable, name, oRec);
oRec->next = rec;
if (rec) {
rec->prev = oRec;
}
}
}
void WMPostNotification(WMNotification * notification)
{
NotificationObserver *orec, *tmp;
WMRetainNotification(notification);
/* tell the observers that want to know about a particular message */
orec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, notification->name);
while (orec) {
tmp = orec->next;
if (!orec->object || !notification->object || orec->object == notification->object) {
/* tell the observer */
if (orec->observerAction) {
(*orec->observerAction) (orec->observer, notification);
}
}
orec = tmp;
}
/* tell the observers that want to know about an object */
orec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, notification->object);
while (orec) {
tmp = orec->next;
/* tell the observer */
if (orec->observerAction) {
(*orec->observerAction) (orec->observer, notification);
}
orec = tmp;
}
/* tell the catch all observers */
orec = notificationCenter->nilList;
while (orec) {
tmp = orec->next;
/* tell the observer */
if (orec->observerAction) {
(*orec->observerAction) (orec->observer, notification);
}
orec = tmp;
}
WMReleaseNotification(notification);
}
void WMRemoveNotificationObserver(void *observer)
{
NotificationObserver *orec, *tmp, *rec;
/* get the list of actions the observer is doing */
orec = (NotificationObserver *) WMHashGet(notificationCenter->observerTable, observer);
/*
* FOREACH orec IN actionlist for observer
* DO
* remove from respective lists/tables
* free
* END
*/
while (orec) {
tmp = orec->nextAction;
if (!orec->name && !orec->object) {
/* catch-all */
if (notificationCenter->nilList == orec)
notificationCenter->nilList = orec->next;
} else if (!orec->name) {
/* any message coming from object */
rec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, orec->object);
if (rec == orec) {
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->objectTable, orec->object, orec->next);
} else {
WMHashRemove(notificationCenter->objectTable, orec->object);
}
}
} else {
/* name && (object || !object) */
rec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, orec->name);
if (rec == orec) {
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->nameTable, orec->name, orec->next);
} else {
WMHashRemove(notificationCenter->nameTable, orec->name);
}
}
}
if (orec->prev)
orec->prev->next = orec->next;
if (orec->next)
orec->next->prev = orec->prev;
wfree(orec);
orec = tmp;
}
WMHashRemove(notificationCenter->observerTable, observer);
}
void WMRemoveNotificationObserverWithName(void *observer, const char *name, void *object)
{
NotificationObserver *orec, *tmp, *rec;
NotificationObserver *newList = NULL;
/* get the list of actions the observer is doing */
orec = (NotificationObserver *) WMHashGet(notificationCenter->observerTable, observer);
WMHashRemove(notificationCenter->observerTable, observer);
/* rebuild the list of actions for the observer */
while (orec) {
tmp = orec->nextAction;
if (orec->name == name && orec->object == object) {
if (!name && !object) {
if (notificationCenter->nilList == orec)
notificationCenter->nilList = orec->next;
} else if (!name) {
rec =
(NotificationObserver *) WMHashGet(notificationCenter->objectTable,
orec->object);
if (rec == orec) {
assert(rec->prev == NULL);
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->objectTable,
orec->object, orec->next);
} else {
WMHashRemove(notificationCenter->objectTable, orec->object);
}
}
} else {
rec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable,
orec->name);
if (rec == orec) {
assert(rec->prev == NULL);
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->nameTable,
orec->name, orec->next);
} else {
WMHashRemove(notificationCenter->nameTable, orec->name);
}
}
}
if (orec->prev)
orec->prev->next = orec->next;
if (orec->next)
orec->next->prev = orec->prev;
wfree(orec);
} else {
/* append this action in the new action list */
orec->nextAction = NULL;
if (!newList) {
newList = orec;
} else {
NotificationObserver *p;
p = newList;
while (p->nextAction) {
p = p->nextAction;
}
p->nextAction = orec;
}
}
orec = tmp;
}
/* reinsert the list to the table */
if (newList) {
WMHashInsert(notificationCenter->observerTable, observer, newList);
}
}
void WMPostNotificationName(const char *name, void *object, void *clientData)
{
WMNotification *notification;
notification = WMCreateNotification(name, object, clientData);
WMPostNotification(notification);
WMReleaseNotification(notification);
}
/**************** Notification Queues ****************/
typedef struct W_NotificationQueue {
WMArray *asapQueue;
WMArray *idleQueue;
struct W_NotificationQueue *next;
} NotificationQueue;
static WMNotificationQueue *notificationQueueList = NULL;
/* default queue */
static WMNotificationQueue *notificationQueue = NULL;
WMNotificationQueue *WMGetDefaultNotificationQueue(void)
{
if (!notificationQueue)
notificationQueue = WMCreateNotificationQueue();
return notificationQueue;
}
WMNotificationQueue *WMCreateNotificationQueue(void)
{
NotificationQueue *queue;
queue = wmalloc(sizeof(NotificationQueue));
queue->asapQueue = WMCreateArrayWithDestructor(8, (WMFreeDataProc *) WMReleaseNotification);
queue->idleQueue = WMCreateArrayWithDestructor(8, (WMFreeDataProc *) WMReleaseNotification);
queue->next = notificationQueueList;
notificationQueueList = queue;
return queue;
}
void WMEnqueueNotification(WMNotificationQueue * queue, WMNotification * notification, WMPostingStyle postingStyle)
{
WMEnqueueCoalesceNotification(queue, notification, postingStyle, WNCOnName | WNCOnSender);
}
#define NOTIF ((WMNotification*)cdata)
#define ITEM ((WMNotification*)item)
static int matchSenderAndName(const void *item, const void *cdata)
{
return (NOTIF->object == ITEM->object && strcmp(NOTIF->name, ITEM->name) == 0);
}
static int matchSender(const void *item, const void *cdata)
{
return (NOTIF->object == ITEM->object);
}
static int matchName(const void *item, const void *cdata)
{
return (strcmp(NOTIF->name, ITEM->name) == 0);
}
#undef NOTIF
#undef ITEM
void WMDequeueNotificationMatching(WMNotificationQueue * queue, WMNotification * notification, unsigned mask)
{
WMMatchDataProc *matchFunc;
if ((mask & WNCOnName) && (mask & WNCOnSender))
matchFunc = matchSenderAndName;
else if (mask & WNCOnName)
matchFunc = matchName;
else if (mask & WNCOnSender)
matchFunc = matchSender;
else
return;
WMRemoveFromArrayMatching(queue->asapQueue, matchFunc, notification);
WMRemoveFromArrayMatching(queue->idleQueue, matchFunc, notification);
}
void
WMEnqueueCoalesceNotification(WMNotificationQueue * queue,
WMNotification * notification, WMPostingStyle postingStyle, unsigned coalesceMask)
{
if (coalesceMask != WNCNone)
WMDequeueNotificationMatching(queue, notification, coalesceMask);
switch (postingStyle) {
case WMPostNow:
WMPostNotification(notification);
WMReleaseNotification(notification);
break;
case WMPostASAP:
WMAddToArray(queue->asapQueue, notification);
break;
case WMPostWhenIdle:
WMAddToArray(queue->idleQueue, notification);
break;
}
}
void W_FlushASAPNotificationQueue(void)
{
WMNotificationQueue *queue = notificationQueueList;
while (queue) {
while (WMGetArrayItemCount(queue->asapQueue)) {
WMPostNotification(WMGetFromArray(queue->asapQueue, 0));
WMDeleteFromArray(queue->asapQueue, 0);
}
queue = queue->next;
}
}
void W_FlushIdleNotificationQueue(void)
{
WMNotificationQueue *queue = notificationQueueList;
while (queue) {
while (WMGetArrayItemCount(queue->idleQueue)) {
WMPostNotification(WMGetFromArray(queue->idleQueue, 0));
WMDeleteFromArray(queue->idleQueue, 0);
}
queue = queue->next;
}
}
+6 -1842
View File
File diff suppressed because it is too large Load Diff
+4 -3
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);
-425
View File
@@ -1,425 +0,0 @@
/*
* Until FreeBSD gets their act together;
* http://www.mail-archive.com/freebsd-hackers@freebsd.org/msg69469.html
*/
#if defined( FREEBSD )
# undef _XOPEN_SOURCE
#endif
#include "wconfig.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#ifdef HAVE_BSD_STRING_H
#include <bsd/string.h>
#endif
#include "WUtil.h"
#define PRC_ALPHA 0
#define PRC_BLANK 1
#define PRC_ESCAPE 2
#define PRC_DQUOTE 3
#define PRC_EOS 4
#define PRC_SQUOTE 5
typedef struct {
short nstate;
short output;
} DFA;
static DFA mtable[9][6] = {
{{3, 1}, {0, 0}, {4, 0}, {1, 0}, {8, 0}, {6, 0}},
{{1, 1}, {1, 1}, {2, 0}, {3, 0}, {5, 0}, {1, 1}},
{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {5, 0}, {1, 1}},
{{3, 1}, {5, 0}, {4, 0}, {1, 0}, {5, 0}, {6, 0}},
{{3, 1}, {3, 1}, {3, 1}, {3, 1}, {5, 0}, {3, 1}},
{{-1, -1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* final state */
{{6, 1}, {6, 1}, {7, 0}, {6, 1}, {5, 0}, {3, 0}},
{{6, 1}, {6, 1}, {6, 1}, {6, 1}, {5, 0}, {6, 1}},
{{-1, -1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* final state */
};
char *wtokennext(char *word, char **next)
{
char *ptr;
char *ret, *t;
int state, ctype;
t = ret = wmalloc(strlen(word) + 1);
ptr = word;
state = 0;
while (1) {
if (*ptr == 0)
ctype = PRC_EOS;
else if (*ptr == '\\')
ctype = PRC_ESCAPE;
else if (*ptr == '"')
ctype = PRC_DQUOTE;
else if (*ptr == '\'')
ctype = PRC_SQUOTE;
else if (*ptr == ' ' || *ptr == '\t')
ctype = PRC_BLANK;
else
ctype = PRC_ALPHA;
if (mtable[state][ctype].output) {
*t = *ptr;
t++;
*t = 0;
}
state = mtable[state][ctype].nstate;
ptr++;
if (mtable[state][0].output < 0) {
break;
}
}
if (*ret == 0) {
wfree(ret);
ret = NULL;
}
if (ctype == PRC_EOS)
*next = NULL;
else
*next = ptr;
return ret;
}
/* separate a string in tokens, taking " and ' into account */
void wtokensplit(char *command, char ***argv, int *argc)
{
char *token, *line;
int count;
count = 0;
line = command;
do {
token = wtokennext(line, &line);
if (token) {
if (count == 0)
*argv = wmalloc(sizeof(**argv));
else
*argv = wrealloc(*argv, (count + 1) * sizeof(**argv));
(*argv)[count++] = token;
}
} while (token != NULL && line != NULL);
*argc = count;
}
char *wtokenjoin(char **list, int count)
{
int i, j;
char *flat_string, *wspace;
j = 0;
for (i = 0; i < count; i++) {
if (list[i] != NULL && list[i][0] != 0) {
j += strlen(list[i]);
if (strpbrk(list[i], " \t"))
j += 2;
}
}
flat_string = wmalloc(j + count + 1);
for (i = 0; i < count; i++) {
if (list[i] != NULL && list[i][0] != 0) {
if (i > 0 &&
wstrlcat(flat_string, " ", j + count + 1) >= j + count + 1)
goto error;
wspace = strpbrk(list[i], " \t");
if (wspace &&
wstrlcat(flat_string, "\"", j + count + 1) >= j + count + 1)
goto error;
if (wstrlcat(flat_string, list[i], j + count + 1) >= j + count + 1)
goto error;
if (wspace &&
wstrlcat(flat_string, "\"", j + count + 1) >= j + count + 1)
goto error;
}
}
return flat_string;
error:
wfree(flat_string);
return NULL;
}
void wtokenfree(char **tokens, int count)
{
while (count--)
wfree(tokens[count]);
wfree(tokens);
}
char *wtrimspace(const char *s)
{
const char *t;
if (s == NULL)
return NULL;
while (isspace(*s) && *s)
s++;
t = s + strlen(s) - 1;
while (t > s && isspace(*t))
t--;
return wstrndup(s, t - s + 1);
}
char *wstrdup(const char *str)
{
assert(str != NULL);
return strcpy(wmalloc(strlen(str) + 1), str);
}
char *wstrndup(const char *str, size_t len)
{
char *copy;
assert(str != NULL);
len = WMIN(len, strlen(str));
copy = strncpy(wmalloc(len + 1), str, len);
copy[len] = 0;
return copy;
}
char *wstrconcat(const char *str1, const char *str2)
{
char *str;
size_t slen, slen1;
if (!str1 && str2)
return wstrdup(str2);
else if (str1 && !str2)
return wstrdup(str1);
else if (!str1 && !str2)
return NULL;
slen1 = strlen(str1);
slen = slen1 + strlen(str2) + 1;
str = wmalloc(slen);
strcpy(str, str1);
strcpy(str + slen1, str2);
return str;
}
char *wstrappend(char *dst, const char *src)
{
size_t slen;
if (!src || *src == 0)
return dst;
else if (!dst)
return wstrdup(src);
slen = strlen(dst) + strlen(src) + 1;
dst = wrealloc(dst, slen);
strcat(dst, src);
return dst;
}
#ifdef HAVE_STRLCAT
size_t
wstrlcat(char *dst, const char *src, size_t siz)
{
return strlcat(dst, src, siz);
}
#else
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
wstrlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
#endif /* HAVE_STRLCAT */
#ifdef HAVE_STRLCPY
size_t
wstrlcpy(char *dst, const char *src, size_t siz)
{
return strlcpy(dst, src, siz);
}
#else
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
wstrlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif /* HAVE_STRLCPY */
/* transform `s' so that the result is safe to pass to the shell as an argument.
* returns a newly allocated string.
* with very heavy inspirations from NetBSD's shquote(3).
*/
char *wshellquote(const char *s)
{
char *p, *r, *last, *ret;
size_t slen;
int needs_quoting;
if (!s)
return NULL;
needs_quoting = !*s; /* the empty string does need quoting */
/* do not quote if consists only of the following characters */
for (p = (char *)s; *p && !needs_quoting; p++) {
needs_quoting = !(isalnum(*p) || (*p == '+') || (*p == '/') ||
(*p == '.') || (*p == ',') || (*p == '-'));
}
if (!needs_quoting)
return wstrdup(s);
for (slen = 0, p = (char *)s; *p; p++) /* count space needed (worst case) */
slen += *p == '\'' ? 4 : 1; /* every single ' becomes ''\' */
slen += 2 /* leading + trailing "'" */ + 1 /* NULL */;
ret = r = wmalloc(slen);
p = (char *)s;
last = p;
if (*p != '\'') /* if string doesn't already begin with "'" */
*r++ ='\''; /* start putting it in quotes */
while (*p) {
last = p;
if (*p == '\'') { /* turn each ' into ''\' */
if (p != s) /* except if it's the first ', in which case */
*r++ = '\''; /* only escape it */
*r++ = '\\';
*r++ = '\'';
while (*++p && *p == '\'') { /* keep turning each consecutive 's into \' */
*r++ = '\\';
*r++ = '\'';
}
if (*p) /* if more input follows, terminate */
*r++ = '\''; /* what we have so far */
} else {
*r++ = *p++;
}
}
if (*last != '\'') /* if the last one isn't already a ' */
*r++ = '\''; /* terminate the whole shebang */
*r = '\0';
return ret; /* technically, we lose (but not leak) a couple of */
/* bytes (twice the number of consecutive 's in the */
/* input or so), but since these are relatively rare */
/* and short-lived strings, not sure if a trip to */
/* wstrdup+wfree worths the gain. */
}
-255
View File
@@ -1,255 +0,0 @@
#include <string.h>
#include "WUtil.h"
typedef struct W_TreeNode {
void *data;
/*unsigned int uflags:16; */
WMArray *leaves;
int depth;
struct W_TreeNode *parent;
WMFreeDataProc *destructor;
} W_TreeNode;
static void destroyNode(void *data)
{
WMTreeNode *aNode = (WMTreeNode *) data;
if (aNode->destructor) {
(*aNode->destructor) (aNode->data);
}
if (aNode->leaves) {
WMFreeArray(aNode->leaves);
}
wfree(aNode);
}
WMTreeNode *WMCreateTreeNode(void *data)
{
return WMCreateTreeNodeWithDestructor(data, NULL);
}
WMTreeNode *WMCreateTreeNodeWithDestructor(void *data, WMFreeDataProc * destructor)
{
WMTreeNode *aNode;
aNode = (WMTreeNode *) wmalloc(sizeof(W_TreeNode));
aNode->destructor = destructor;
aNode->data = data;
aNode->parent = NULL;
aNode->depth = 0;
aNode->leaves = NULL;
/*aNode->leaves = WMCreateArrayWithDestructor(1, destroyNode); */
return aNode;
}
WMTreeNode *WMInsertItemInTree(WMTreeNode * parent, int index, void *item)
{
WMTreeNode *aNode;
wassertrv(parent != NULL, NULL);
aNode = WMCreateTreeNodeWithDestructor(item, parent->destructor);
aNode->parent = parent;
aNode->depth = parent->depth + 1;
if (!parent->leaves) {
parent->leaves = WMCreateArrayWithDestructor(1, destroyNode);
}
if (index < 0) {
WMAddToArray(parent->leaves, aNode);
} else {
WMInsertInArray(parent->leaves, index, aNode);
}
return aNode;
}
static void updateNodeDepth(WMTreeNode * aNode, int depth)
{
int i;
aNode->depth = depth;
if (aNode->leaves) {
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
updateNodeDepth(WMGetFromArray(aNode->leaves, i), depth + 1);
}
}
}
WMTreeNode *WMInsertNodeInTree(WMTreeNode * parent, int index, WMTreeNode * aNode)
{
wassertrv(parent != NULL, NULL);
wassertrv(aNode != NULL, NULL);
aNode->parent = parent;
updateNodeDepth(aNode, parent->depth + 1);
if (!parent->leaves) {
parent->leaves = WMCreateArrayWithDestructor(1, destroyNode);
}
if (index < 0) {
WMAddToArray(parent->leaves, aNode);
} else {
WMInsertInArray(parent->leaves, index, aNode);
}
return aNode;
}
void WMDestroyTreeNode(WMTreeNode * aNode)
{
wassertr(aNode != NULL);
if (aNode->parent && aNode->parent->leaves) {
WMRemoveFromArray(aNode->parent->leaves, aNode);
} else {
destroyNode(aNode);
}
}
void WMDeleteLeafForTreeNode(WMTreeNode * aNode, int index)
{
wassertr(aNode != NULL);
wassertr(aNode->leaves != NULL);
WMDeleteFromArray(aNode->leaves, index);
}
static int sameData(const void *item, const void *data)
{
return (((WMTreeNode *) item)->data == data);
}
void WMRemoveLeafForTreeNode(WMTreeNode * aNode, void *leaf)
{
int index;
wassertr(aNode != NULL);
wassertr(aNode->leaves != NULL);
index = WMFindInArray(aNode->leaves, sameData, leaf);
if (index != WANotFound) {
WMDeleteFromArray(aNode->leaves, index);
}
}
void *WMReplaceDataForTreeNode(WMTreeNode * aNode, void *newData)
{
void *old;
wassertrv(aNode != NULL, NULL);
old = aNode->data;
aNode->data = newData;
return old;
}
void *WMGetDataForTreeNode(WMTreeNode * aNode)
{
return aNode->data;
}
int WMGetTreeNodeDepth(WMTreeNode * aNode)
{
return aNode->depth;
}
WMTreeNode *WMGetParentForTreeNode(WMTreeNode * aNode)
{
return aNode->parent;
}
void WMSortLeavesForTreeNode(WMTreeNode * aNode, WMCompareDataProc * comparer)
{
wassertr(aNode != NULL);
if (aNode->leaves) {
WMSortArray(aNode->leaves, comparer);
}
}
static void sortLeavesForNode(WMTreeNode * aNode, WMCompareDataProc * comparer)
{
int i;
if (!aNode->leaves)
return;
WMSortArray(aNode->leaves, comparer);
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
sortLeavesForNode(WMGetFromArray(aNode->leaves, i), comparer);
}
}
void WMSortTree(WMTreeNode * aNode, WMCompareDataProc * comparer)
{
wassertr(aNode != NULL);
sortLeavesForNode(aNode, comparer);
}
static WMTreeNode *findNodeInTree(WMTreeNode * aNode, WMMatchDataProc * match, void *cdata, int limit)
{
if (match == NULL && aNode->data == cdata)
return aNode;
else if (match && (*match) (aNode->data, cdata))
return aNode;
if (aNode->leaves && limit != 0) {
WMTreeNode *leaf;
int i;
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
leaf = findNodeInTree(WMGetFromArray(aNode->leaves, i),
match, cdata, limit > 0 ? limit - 1 : limit);
if (leaf)
return leaf;
}
}
return NULL;
}
WMTreeNode *WMFindInTree(WMTreeNode * aTree, WMMatchDataProc * match, void *cdata)
{
wassertrv(aTree != NULL, NULL);
return findNodeInTree(aTree, match, cdata, -1);
}
WMTreeNode *WMFindInTreeWithDepthLimit(WMTreeNode * aTree, WMMatchDataProc * match, void *cdata, int limit)
{
wassertrv(aTree != NULL, NULL);
wassertrv(limit >= 0, NULL);
return findNodeInTree(aTree, match, cdata, limit);
}
void WMTreeWalk(WMTreeNode * aNode, WMTreeWalkProc * walk, void *data, Bool DepthFirst)
{
int i;
WMTreeNode *leaf;
wassertr(aNode != NULL);
if (DepthFirst)
(*walk)(aNode, data);
if (aNode->leaves) {
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
leaf = (WMTreeNode *)WMGetFromArray(aNode->leaves, i);
WMTreeWalk(leaf, walk, data, DepthFirst);
}
}
if (!DepthFirst)
(*walk)(aNode, data);
}
+2 -36
View File
@@ -46,40 +46,6 @@ static void synchronizeUserDefaults(void *foo);
#define UD_SYNC_INTERVAL 2000
#endif
const char *wusergnusteppath(void)
{
static const char subdir[] = "/" GSUSER_SUBDIR;
static char *path = NULL;
char *gspath;
const char *h;
int pathlen;
if (path)
/* Value have been already computed, re-use it */
return path;
gspath = GETENV("WMAKER_USER_ROOT");
if (gspath) {
gspath = wexpandpath(gspath);
if (gspath) {
path = gspath;
return path;
}
wwarning(_("variable WMAKER_USER_ROOT defined with invalid path, not used"));
}
h = wgethomedir();
if (!h)
return NULL;
pathlen = strlen(h);
path = wmalloc(pathlen + sizeof(subdir));
strcpy(path, h);
strcpy(path + pathlen, subdir);
return path;
}
const char *wuserdatapath(void)
{
static char *path = NULL;
@@ -331,7 +297,7 @@ WMUserDefaults *WMGetStandardUserDefaults(void)
/* terminate list */
defaults->searchList[2] = NULL;
defaults->searchListArray = WMCreatePLArray(NULL, NULL);
defaults->searchListArray = WMCreateEmptyPLArray();
i = 0;
while (defaults->searchList[i]) {
@@ -404,7 +370,7 @@ WMUserDefaults *WMGetDefaultsFromPath(const char *path)
/* terminate list */
defaults->searchList[1] = NULL;
defaults->searchListArray = WMCreatePLArray(NULL, NULL);
defaults->searchListArray = WMCreateEmptyPLArray();
i = 0;
while (defaults->searchList[i]) {
+8 -11
View File
@@ -44,9 +44,6 @@ void WMInitializeApplication(const char *applicationName, int *argc, char **argv
WMApplication.argv[i] = wstrdup(argv[i]);
}
WMApplication.argv[i] = NULL;
/* initialize notification center */
W_InitNotificationCenter();
}
void WMReleaseApplication(void) {
@@ -60,7 +57,7 @@ void WMReleaseApplication(void) {
*/
w_save_defaults_changes();
W_ReleaseNotificationCenter();
W_ClearNotificationCenter();
if (WMApplication.applicationName) {
wfree(WMApplication.applicationName);
@@ -101,21 +98,21 @@ static char *checkFile(const char *path, const char *folder, const char *ext, co
slen = strlen(path) + strlen(resource) + 1 + extralen;
ret = wmalloc(slen);
if (wstrlcpy(ret, path, slen) >= slen)
if (strlcpy(ret, path, slen) >= slen)
goto error;
if (folder &&
(wstrlcat(ret, "/", slen) >= slen ||
wstrlcat(ret, folder, slen) >= slen))
(strlcat(ret, "/", slen) >= slen ||
strlcat(ret, folder, slen) >= slen))
goto error;
if (ext &&
(wstrlcat(ret, "/", slen) >= slen ||
wstrlcat(ret, ext, slen) >= slen))
(strlcat(ret, "/", slen) >= slen ||
strlcat(ret, ext, slen) >= slen))
goto error;
if (wstrlcat(ret, "/", slen) >= slen ||
wstrlcat(ret, resource, slen) >= slen)
if (strlcat(ret, "/", slen) >= slen ||
strlcat(ret, resource, slen) >= slen)
goto error;
if (access(ret, F_OK) != 0)
+1 -1
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 = WMCreateHashTable(WMIntHashCallbacks);
bPtr->table = WMCreateIdentityHashTable();
bPtr->delay = DEFAULT_DELAY;
+9 -9
View File
@@ -314,7 +314,7 @@ static void removeColumn(WMBrowser * bPtr, int column)
wfree(bPtr->titles[i]);
bPtr->titles[i] = NULL;
}
WMRemoveNotificationObserverWithName(bPtr, WMListSelectionDidChangeNotification, bPtr->columns[i]);
WMRemoveNotificationObserver(bPtr->columns[i]);
WMDestroyWidget(bPtr->columns[i]);
bPtr->columns[i] = NULL;
}
@@ -720,14 +720,14 @@ char *WMGetBrowserPathToColumn(WMBrowser * bPtr, int column)
path = wmalloc(slen);
/* ignore first `/' */
for (i = 0; i <= column; i++) {
if (wstrlcat(path, bPtr->pathSeparator, slen) >= slen)
if (strlcat(path, bPtr->pathSeparator, slen) >= slen)
goto error;
item = WMGetListSelectedItem(bPtr->columns[i]);
if (!item)
break;
if (wstrlcat(path, item->text, slen) >= slen)
if (strlcat(path, item->text, slen) >= slen)
goto error;
}
@@ -782,7 +782,7 @@ WMArray *WMGetBrowserPaths(WMBrowser * bPtr)
path = wmalloc(slen);
/* ignore first `/' */
for (i = 0; i <= column; i++) {
wstrlcat(path, bPtr->pathSeparator, slen);
strlcat(path, bPtr->pathSeparator, slen);
if (i == column) {
item = lastItem;
} else {
@@ -790,7 +790,7 @@ WMArray *WMGetBrowserPaths(WMBrowser * bPtr)
}
if (!item)
break;
wstrlcat(path, item->text, slen);
strlcat(path, item->text, slen);
}
WMAddToArray(paths, path);
}
@@ -1130,25 +1130,25 @@ static char *createTruncatedString(WMFont * font, const char *text, int *textLen
if (width >= 3 * dLen) {
int tmpTextLen = *textLen;
if (wstrlcpy(textBuf, text, slen) >= slen)
if (strlcpy(textBuf, text, slen) >= slen)
goto error;
while (tmpTextLen && (WMWidthOfString(font, textBuf, tmpTextLen) + 3 * dLen > width))
tmpTextLen--;
if (wstrlcpy(textBuf + tmpTextLen, "...", slen) >= slen)
if (strlcpy(textBuf + tmpTextLen, "...", slen) >= slen)
goto error;
*textLen = tmpTextLen + 3;
} else if (width >= 2 * dLen) {
if (wstrlcpy(textBuf, "..", slen) >= slen)
if (strlcpy(textBuf, "..", slen) >= slen)
goto error;
*textLen = 2;
} else if (width >= dLen) {
if (wstrlcpy(textBuf, ".", slen) >= slen)
if (strlcpy(textBuf, ".", slen) >= slen)
goto error;
*textLen = 1;
+6 -3
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);
+8 -10
View File
@@ -513,12 +513,12 @@ static void listDirectoryOnColumn(WMFilePanel * panel, int column, const char *p
if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
continue;
if (wstrlcpy(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf))
if (strlcpy(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf))
goto out;
if (strcmp(path, "/") != 0 &&
wstrlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf))
strlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf))
goto out;
if (wstrlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf))
if (strlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf))
goto out;
if (stat(pbuf, &stat_buf) != 0) {
@@ -626,11 +626,11 @@ static void createDir(WMWidget *widget, void *p_panel)
file = wmalloc(slen);
if (directory &&
(wstrlcat(file, directory, slen) >= slen ||
wstrlcat(file, "/", slen) >= slen))
(strlcat(file, directory, slen) >= slen ||
strlcat(file, "/", slen) >= slen))
goto out;
if (wstrlcat(file, dirName, slen) >= slen)
if (strlcat(file, dirName, slen) >= slen)
goto out;
if (mkdir(file, 00777) != 0) {
@@ -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)
+9 -3
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;
}
+4 -4
View File
@@ -535,7 +535,7 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel)
if (pat)
FcPatternDestroy(pat);
families = WMCreateHashTable(WMStringPointerHashCallbacks);
families = WMCreateStringHashTable();
if (fs) {
for (i = 0; i < fs->nfont; i++) {
@@ -558,7 +558,7 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel)
WMListItem *item;
WM_ITERATE_ARRAY(array, fam, i) {
wstrlcpy(buffer, fam->name, sizeof(buffer));
strlcpy(buffer, fam->name, sizeof(buffer));
item = WMAddListItem(panel->famLs, buffer);
item->clientData = fam;
@@ -640,7 +640,7 @@ static void familyClick(WMWidget * w, void *data)
int top = 0;
WMListItem *fitem;
wstrlcpy(buffer, face->typeface, sizeof(buffer));
strlcpy(buffer, face->typeface, sizeof(buffer));
if (strcasecmp(face->typeface, "Roman") == 0)
top = 1;
if (strcasecmp(face->typeface, "Regular") == 0)
@@ -773,7 +773,7 @@ static void setFontPanelFontName(FontPanel * panel, const char *family, const ch
int top = 0;
WMListItem *fitem;
wstrlcpy(buffer, face->typeface, sizeof(buffer));
strlcpy(buffer, face->typeface, sizeof(buffer));
if (strcasecmp(face->typeface, "Roman") == 0)
top = 1;
if (top)
+1 -1
View File
@@ -629,7 +629,7 @@ WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * c
scrPtr->rootWin = RootWindow(display, screen);
scrPtr->fontCache = WMCreateHashTable(WMStringPointerHashCallbacks);
scrPtr->fontCache = WMCreateStringHashTable();
scrPtr->xftdraw = XftDrawCreate(scrPtr->display, W_DRAWABLE(scrPtr), scrPtr->visual, scrPtr->colormap);
-10
View File
@@ -166,16 +166,6 @@ typedef struct W_Text {
WMArray *xdndDestinationTypes;
} Text;
/* not used */
#if 0
#define NOTIFY(T,C,N,A) {\
WMNotification *notif = WMCreateNotification(N,T,A);\
if ((T)->delegate && (T)->delegate->C)\
(*(T)->delegate->C)((T)->delegate,notif);\
WMPostNotification(notif);\
WMReleaseNotification(notif);}
#endif
#define TYPETEXT 0
#if 0
+41 -32
View File
@@ -68,12 +68,6 @@ typedef struct W_TextField {
} flags;
} TextField;
#define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
if ((T)->delegate && (T)->delegate->C)\
(*(T)->delegate->C)((T)->delegate,notif);\
WMPostNotification(notif);\
WMReleaseNotification(notif);}
#define MIN_TEXT_BUFFER 2
#define TEXT_BUFFER_INCR 8
@@ -404,7 +398,7 @@ void WMInsertTextFieldText(WMTextField * tPtr, const char *text, int position)
if (position < 0 || position >= tPtr->textLen) {
/* append the text at the end */
wstrlcat(tPtr->text, text, tPtr->bufferSize);
strlcat(tPtr->text, text, tPtr->bufferSize);
tPtr->textLen += len;
tPtr->cursorPosition += len;
incrToFit(tPtr);
@@ -473,7 +467,7 @@ void WMSetTextFieldText(WMTextField * tPtr, const char *text)
tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
}
wstrlcpy(tPtr->text, text, tPtr->bufferSize);
strlcpy(tPtr->text, text, tPtr->bufferSize);
}
tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
@@ -906,7 +900,10 @@ static void handleEvents(XEvent * event, void *data)
paintTextField(tPtr);
NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
if (tPtr->delegate && tPtr->delegate->didBeginEditing) {
(*tPtr->delegate->didBeginEditing)(tPtr->delegate, 0);
}
WMPostNotificationName(WMTextDidBeginEditingNotification, tPtr, NULL);
tPtr->flags.notIllegalMovement = 0;
break;
@@ -921,8 +918,10 @@ static void handleEvents(XEvent * event, void *data)
paintTextField(tPtr);
if (!tPtr->flags.notIllegalMovement) {
NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
(void *)WMIllegalTextMovement);
if (tPtr->delegate && tPtr->delegate->didEndEditing) {
(*tPtr->delegate->didEndEditing)(tPtr->delegate, WMIllegalTextMovement);
}
WMPostNotificationName(WMTextDidEndEditingNotification, tPtr, (void *)WMIllegalTextMovement);
}
break;
@@ -943,7 +942,8 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
char buffer[64];
KeySym ksym;
const char *textEvent = NULL;
void *data = NULL;
WMTextMovementType movement_type;
WMTextFieldSpecialEventType special_field_event_type;
int count, refresh = 0;
int control_pressed = 0;
int cancelSelection = 1;
@@ -975,14 +975,14 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
tPtr->view->prevFocusChain);
tPtr->flags.notIllegalMovement = 1;
}
data = (void *)WMBacktabTextMovement;
movement_type = WMBacktabTextMovement;
} else {
if (tPtr->view->nextFocusChain) {
W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
tPtr->view->nextFocusChain);
tPtr->flags.notIllegalMovement = 1;
}
data = (void *)WMTabTextMovement;
movement_type = WMTabTextMovement;
}
textEvent = WMTextDidEndEditingNotification;
@@ -994,7 +994,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
case XK_Escape:
if (!modified) {
data = (void *)WMEscapeTextMovement;
movement_type = WMEscapeTextMovement;
textEvent = WMTextDidEndEditingNotification;
relay = False;
@@ -1008,7 +1008,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
/* FALLTHRU */
case XK_Return:
if (!modified) {
data = (void *)WMReturnTextMovement;
movement_type = WMReturnTextMovement;
textEvent = WMTextDidEndEditingNotification;
relay = False;
@@ -1165,7 +1165,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
if (!modified) {
if (tPtr->selection.count) {
WMDeleteTextFieldRange(tPtr, tPtr->selection);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
} else if (tPtr->cursorPosition > 0) {
int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
@@ -1174,7 +1174,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
range.position = tPtr->cursorPosition + i;
range.count = -i;
WMDeleteTextFieldRange(tPtr, range);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
}
@@ -1197,7 +1197,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
if (!modified) {
if (tPtr->selection.count) {
WMDeleteTextFieldRange(tPtr, tPtr->selection);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
} else if (tPtr->cursorPosition < tPtr->textLen) {
WMRange range;
@@ -1205,7 +1205,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
tPtr->textLen - tPtr->cursorPosition);
WMDeleteTextFieldRange(tPtr, range);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
}
@@ -1220,7 +1220,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
if (tPtr->selection.count)
WMDeleteTextFieldRange(tPtr, tPtr->selection);
WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
data = (void *)WMInsertTextEvent;
special_field_event_type = WMInsertTextEvent;
textEvent = WMTextDidChangeNotification;
relay = False;
@@ -1255,21 +1255,22 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
/*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
if (textEvent) {
WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
if (tPtr->delegate) {
if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
(*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
(*tPtr->delegate->didBeginEditing) (tPtr->delegate, movement_type);
else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
(*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
(*tPtr->delegate->didEndEditing) (tPtr->delegate, movement_type);
else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
(*tPtr->delegate->didChange) (tPtr->delegate, notif);
(*tPtr->delegate->didChange) (tPtr->delegate, special_field_event_type);
}
WMPostNotification(notif);
WMReleaseNotification(notif);
if (textEvent == WMTextDidBeginEditingNotification || textEvent == WMTextDidEndEditingNotification) {
WMPostNotificationName(textEvent, tPtr, (void *)movement_type);
} else if (textEvent == WMTextDidChangeNotification) {
WMPostNotificationName(textEvent, tPtr, (void *)special_field_event_type);
}
}
if (refresh)
@@ -1345,7 +1346,10 @@ static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp
str = (char *)WMDataBytes(data);
WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
if (tPtr->delegate && tPtr->delegate->didChange) {
(*tPtr->delegate->didChange)(tPtr->delegate, WMInsertTextEvent);
}
WMPostNotificationName(WMTextDidChangeNotification, tPtr, (void *)WMInsertTextEvent);
} else {
int n;
@@ -1355,7 +1359,10 @@ static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp
str[n] = 0;
WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
XFree(str);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
if (tPtr->delegate && tPtr->delegate->didChange) {
(*tPtr->delegate->didChange)(tPtr->delegate, WMInsertTextEvent);
}
WMPostNotificationName(WMTextDidChangeNotification, tPtr, (void *)WMInsertTextEvent);
}
}
}
@@ -1477,8 +1484,10 @@ static void handleTextFieldActionEvents(XEvent * event, void *data)
text[n] = 0;
WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
XFree(text);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void *)WMInsertTextEvent);
if (tPtr->delegate && tPtr->delegate->didChange) {
(*tPtr->delegate->didChange)(tPtr->delegate, WMInsertTextEvent);
}
WMPostNotificationName(WMTextDidChangeNotification, tPtr, (void *)WMInsertTextEvent);
}
} else {
tPtr->flags.waitingSelection = 1;
+8 -4
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)
@@ -2224,7 +2228,7 @@ static void prepareForClose(_Panel * panel)
WMUserDefaults *udb = WMGetStandardUserDefaults();
int i;
textureList = WMCreatePLArray(NULL, NULL);
textureList = WMCreateEmptyPLArray();
/* store list of textures */
for (i = 8; i < WMGetListNumberOfRows(panel->texLs); i++) {
@@ -2246,7 +2250,7 @@ static void prepareForClose(_Panel * panel)
WMReleasePropList(textureList);
/* store list of colors */
textureList = WMCreatePLArray(NULL, NULL);
textureList = WMCreateEmptyPLArray();
for (i = 0; i < wlengthof(sample_colors); i++) {
WMColor *color;
char *str;
+5 -1
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)
+1 -1
View File
@@ -151,7 +151,7 @@ static void storeData(_Panel * panel)
SetIntegerForKey(WMGetSliderValue(panel->hceS), "HotCornerEdge");
list = WMCreatePLArray(NULL, NULL);
list = WMCreateEmptyPLArray();
for (i = 0; i < sizeof(panel->hcactionsT) / sizeof(WMTextField *); i++) {
str = WMGetTextFieldText(panel->hcactionsT[i]);
if (strlen(str) == 0)
+1 -1
View File
@@ -365,7 +365,7 @@ char *capture_shortcut(Display *dpy, Bool *capturing, Bool convert_case)
if ((numlock_mask != Mod5Mask) && (ev.xkey.state & Mod5Mask))
strcat(buffer, "Mod5+");
wstrlcat(buffer, key, sizeof(buffer));
strlcat(buffer, key, sizeof(buffer));
return wstrdup(buffer);
}
+1 -1
View File
@@ -66,9 +66,9 @@ 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)/wmaker-rs/target/debug/libwmaker_rs.a\
$(top_builddir)/wrlib/libwraster.la \
@XLFLAGS@ @XLIBS@ \
@LIBM@ \
+2 -2
View File
@@ -203,7 +203,7 @@ static void storeData(_Panel * panel)
int i;
char *p;
list = WMCreatePLArray(NULL, NULL);
list = WMCreateEmptyPLArray();
for (i = 0; i < WMGetListNumberOfRows(panel->icoL); i++) {
p = WMGetListItem(panel->icoL, i)->text;
tmp = WMCreatePLString(p);
@@ -211,7 +211,7 @@ static void storeData(_Panel * panel)
}
SetObjectForKey(list, "IconPath");
list = WMCreatePLArray(NULL, NULL);
list = WMCreateEmptyPLArray();
for (i = 0; i < WMGetListNumberOfRows(panel->pixL); i++) {
p = WMGetListItem(panel->pixL, i)->text;
tmp = WMCreatePLString(p);
+1 -1
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;
+2 -2
View File
@@ -705,10 +705,10 @@ static void loadConfigurations(WMScreen * scr, WMWindow * mainw)
}
if (!db) {
db = WMCreatePLDictionary(NULL, NULL);
db = WMCreateEmptyPLDictionary();
}
if (!gdb) {
gdb = WMCreatePLDictionary(NULL, NULL);
gdb = WMCreateEmptyPLDictionary();
}
GlobalDB = gdb;
+1 -4
View File
@@ -824,18 +824,15 @@ static void stopEditItem(WEditMenu * menu, Bool apply)
menu->flags.isEditing = 0;
}
static void textEndedEditing(struct WMTextFieldDelegate *self, WMNotification * notif)
static void textEndedEditing(struct WMTextFieldDelegate *self, WMTextMovementType reason)
{
WEditMenu *menu = (WEditMenu *) self->data;
uintptr_t reason;
int i;
WEditMenuItem *item;
if (!menu->flags.isEditing)
return;
reason = (uintptr_t)WMGetNotificationClientData(notif);
switch (reason) {
case WMEscapeTextMovement:
stopEditItem(menu, False);
+6 -11
View File
@@ -47,14 +47,6 @@ 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);
@@ -87,8 +79,6 @@ int main(int argc, char **argv)
int i;
char *display_name = "";
wsetabort(wAbort);
memset(DeadHandlers, 0, sizeof(DeadHandlers));
WMInitializeApplication("WPrefs", &argc, argv);
@@ -155,7 +145,12 @@ int main(int argc, char **argv)
exit(0);
}
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
Initialize(scr);
+5 -51
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
@@ -407,39 +391,6 @@ dnl the flag 'O_NOFOLLOW' for 'open' is used in WINGs
WM_FUNC_OPEN_NOFOLLOW
dnl Check for strlcat/strlcpy
dnl =========================
m4_divert_push([INIT_PREPARE])dnl
AC_ARG_WITH([libbsd],
[AS_HELP_STRING([--without-libbsd], [do not use libbsd for strlcat and strlcpy [default=check]])],
[AS_IF([test "x$with_libbsd" != "xno"],
[with_libbsd=bsd],
[with_libbsd=]
)],
[with_libbsd=bsd])
m4_divert_pop([INIT_PREPARE])dnl
tmp_libs=$LIBS
AC_SEARCH_LIBS([strlcat],[$with_libbsd],
[AC_DEFINE(HAVE_STRLCAT, 1, [Define if strlcat is available])],
[],
[]
)
AC_SEARCH_LIBS([strlcpy],[$with_libbsd],
[AC_DEFINE(HAVE_STRLCAT, 1, [Define if strlcpy is available])],
[],
[]
)
LIBS=$tmp_libs
LIBBSD=
AS_IF([test "x$ac_cv_search_strlcat" = "x-lbsd" -o "x$ac_cv_search_strlcpy" = "x-lbsd"],
[LIBBSD=-lbsd
AC_CHECK_HEADERS([bsd/string.h])]
)
AC_SUBST(LIBBSD)
dnl Check for OpenBSD kernel memory interface - kvm(3)
dnl ==================================================
AS_IF([test "x$WM_OSDEP" = "xbsd"],
@@ -973,6 +924,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
-10
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}.
+2 -1
View File
@@ -134,7 +134,7 @@ else
nodist_wmaker_SOURCES = misc.hack_nf.c \
xmodifier.hack_nf.c
CLEANFILES = $(nodist_wmaker_SOURCES) ../wmaker-rs/target
CLEANFILES = $(nodist_wmaker_SOURCES)
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,6 +160,7 @@ 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@ \
+1 -1
View File
@@ -1360,7 +1360,7 @@ static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_ins
val = WMGetFromPLDictionary(adict, iconk);
} else {
/* no dictionary for app, so create one */
adict = WMCreatePLDictionary(NULL, NULL);
adict = WMCreateEmptyPLDictionary();
WMPutInPLDictionary(dict, key, adict);
WMReleasePropList(adict);
val = NULL;
+4 -4
View File
@@ -94,7 +94,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
wwarning(_("appmenu: bad menu entry \"%s\" in window %lx"), slist[*index], win);
return NULL;
}
if (wstrlcpy(title, &slist[*index][pos], sizeof(title)) >= sizeof(title)) {
if (strlcpy(title, &slist[*index][pos], sizeof(title)) >= sizeof(title)) {
wwarning(_("appmenu: menu command size exceeded in window %lx"), win);
return NULL;
}
@@ -127,7 +127,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
slist[*index], win);
return NULL;
}
wstrlcpy(title, &slist[*index][pos], sizeof(title));
strlcpy(title, &slist[*index][pos], sizeof(title));
rtext[0] = 0;
} else {
if (sscanf(slist[*index], "%i %i %i %i %s %n",
@@ -137,7 +137,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
slist[*index], win);
return NULL;
}
wstrlcpy(title, &slist[*index][pos], sizeof(title));
strlcpy(title, &slist[*index][pos], sizeof(title));
}
data = wmalloc(sizeof(WAppMenuData));
if (data == NULL) {
@@ -174,7 +174,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
return NULL;
}
wstrlcpy(title, &slist[*index][pos], sizeof(title));
strlcpy(title, &slist[*index][pos], sizeof(title));
*index += 1;
submenu = parseMenuCommand(scr, win, slist, count, index);
+2 -2
View File
@@ -307,7 +307,7 @@ void wClientCheckProperty(WWindow * wwin, XPropertyEvent * event)
wWindowUpdateName(wwin, tmp);
}
if (tmp)
XFree(tmp);
wfree(tmp);
}
break;
@@ -616,7 +616,7 @@ void wClientCheckProperty(WWindow * wwin, XPropertyEvent * event)
wWindowUpdateGNUstepAttr(wwin, attr);
XFree(attr);
wfree(attr);
} else {
wNETWMCheckClientHintChange(wwin, event);
}
+7 -2
View File
@@ -862,7 +862,12 @@ static void initDefaults(void)
unsigned int i;
WDefaultEntry *entry;
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
for (i = 0; i < wlengthof(optionList); i++) {
entry = &optionList[i];
@@ -2200,7 +2205,7 @@ static int getKeybind(WScreen * scr, WDefaultEntry * entry, WMPropList * value,
return True;
}
wstrlcpy(buf, val, MAX_SHORTCUT_LENGTH);
strlcpy(buf, val, MAX_SHORTCUT_LENGTH);
b = (char *)buf;
+6 -6
View File
@@ -246,7 +246,7 @@ static void SaveHistory(WMArray * history, const char *filename)
int i;
WMPropList *plhistory;
plhistory = WMCreatePLArray(NULL);
plhistory = WMCreateEmptyPLArray();
for (i = 0; i < WMGetArrayItemCount(history); ++i)
WMAddToPLArray(plhistory, WMCreatePLString(WMGetFromArray(history, i)));
@@ -363,7 +363,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));
}
@@ -464,7 +464,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;
@@ -605,9 +605,9 @@ static void listPixmaps(WScreen *scr, WMList *lPtr, const char *path)
if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
continue;
if (wstrlcpy(pbuf, apath, sizeof(pbuf)) >= sizeof(pbuf) ||
wstrlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf) ||
wstrlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf)) {
if (strlcpy(pbuf, apath, sizeof(pbuf)) >= sizeof(pbuf) ||
strlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf) ||
strlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf)) {
wwarning(_("full path for file \"%s\" in \"%s\" is longer than %d bytes, skipped"),
dentry->d_name, path, (int) (sizeof(pbuf) - 1) );
continue;
+11 -9
View File
@@ -1603,11 +1603,13 @@ static WMPropList *make_icon_state(WAppIcon *btn)
snprintf(buffer, sizeof(buffer), "%hi,%hi", wAppIconGetXIndex(btn), wAppIconGetYIndex(btn));
position = WMCreatePLString(buffer);
node = WMCreatePLDictionary(dCommand, command,
dName, name,
dAutoLaunch, autolaunch,
dLock, lock,
dForced, forced, dBuggyApplication, buggy, dPosition, position, NULL);
node = WMCreatePLDictionary(dCommand, command);
WMPutInPLDictionary(node, dName, name);
WMPutInPLDictionary(node, dAutoLaunch, autolaunch);
WMPutInPLDictionary(node, dLock, lock);
WMPutInPLDictionary(node, dForced, forced);
WMPutInPLDictionary(node, dBuggyApplication, buggy);
WMPutInPLDictionary(node, dPosition, position);
WMReleasePropList(command);
WMReleasePropList(name);
WMReleasePropList(position);
@@ -1642,7 +1644,7 @@ static WMPropList *dockSaveState(WDock *dock)
WMPropList *value, *key;
char buffer[256];
list = WMCreatePLArray(NULL);
list = WMCreateEmptyPLArray();
for (i = (dock->type == WM_DOCK ? 0 : 1); i < dock->max_icons; i++) {
WAppIcon *btn = dock->icon_array[i];
@@ -1657,7 +1659,7 @@ static WMPropList *dockSaveState(WDock *dock)
}
}
dock_state = WMCreatePLDictionary(dApplications, list, NULL);
dock_state = WMCreatePLDictionary(dApplications, list);
if (dock->type == WM_DOCK) {
snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
@@ -5070,7 +5072,7 @@ static WMPropList *drawerSaveState(WDock *drawer)
ai = drawer->icon_array[0];
/* Store its name */
pstr = WMCreatePLString(wAppIconGetWmInstance(ai));
drawer_state = WMCreatePLDictionary(dName, pstr, NULL); /* we need this final NULL */
drawer_state = WMCreatePLDictionary(dName, pstr);
WMReleasePropList(pstr);
/* Store its position */
@@ -5112,7 +5114,7 @@ void wDrawersSaveState(WScreen *scr)
make_keys();
all_drawers = WMCreatePLArray(NULL);
all_drawers = WMCreateEmptyPLArray();
for (i=0, dc = scr->drawers;
i < scr->drawer_count;
i++, dc = dc->next) {
+2 -2
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);
}
}
-1
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);
+3 -2
View File
@@ -2309,7 +2309,8 @@ static void saveMenuInfo(WMPropList * dict, WMenu * menu, WMPropList * key)
snprintf(buffer, sizeof(buffer), "%i,%i", menu->frame_x, menu->frame_y);
value = WMCreatePLString(buffer);
list = WMCreatePLArray(value, NULL);
list = WMCreateEmptyPLArray();
WMAddToPLArray(list, value);
if (menu->flags.lowered)
WMAddToPLArray(list, WMCreatePLString("lowered"));
WMPutInPLDictionary(dict, key, list);
@@ -2322,7 +2323,7 @@ void wMenuSaveState(WScreen * scr)
WMPropList *menus, *key;
int save_menus = 0;
menus = WMCreatePLDictionary(NULL, NULL);
menus = WMCreateEmptyPLDictionary();
if (scr->switch_menu && scr->switch_menu->flags.buttoned) {
key = WMCreatePLString("SwitchMenu");
+1 -1
View File
@@ -406,7 +406,7 @@ static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu
ptr = wmalloc(sizeof(Shortcut));
wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
strlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
b = (char *)buf;
/* get modifiers */
+6 -3
View File
@@ -968,8 +968,6 @@ void wScreenSaveState(WScreen * scr)
old_state = scr->session_state;
scr->session_state = WMCreatePLDictionary(NULL, NULL);
WMPLSetCaseSensitive(True);
/* save dock state to file */
if (!wPreferences.flags.nodock) {
wDockSaveState(scr, old_state);
@@ -1009,8 +1007,13 @@ void wScreenSaveState(WScreen * scr)
WMPutInPLDictionary(scr->session_state, dWorkspace, foo);
}
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* clean up */
WMPLSetCaseSensitive(False);
/* WMPLSetCaseSensitive(False); */
wMenuSaveState(scr);
+22 -15
View File
@@ -241,14 +241,15 @@ static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp)
snprintf(buffer, sizeof(buffer), "%u", mask);
shortcut = WMCreatePLString(buffer);
win_state = WMCreatePLDictionary(sName, name,
sCommand, cmd,
sWorkspace, workspace,
sShaded, shaded,
sMiniaturized, miniaturized,
sMaximized, maximized,
sHidden, hidden,
sShortcutMask, shortcut, sGeometry, geometry, NULL);
win_state = WMCreatePLDictionary(sName, name);
WMPutInPLDictionary(win_state, sCommand, cmd);
WMPutInPLDictionary(win_state, sWorkspace, workspace);
WMPutInPLDictionary(win_state, sShaded, shaded);
WMPutInPLDictionary(win_state, sMiniaturized, miniaturized);
WMPutInPLDictionary(win_state, sMaximized, maximized);
WMPutInPLDictionary(win_state, sHidden, hidden);
WMPutInPLDictionary(win_state, sShortcutMask, shortcut);
WMPutInPLDictionary(win_state, sGeometry, geometry);
WMReleasePropList(name);
WMReleasePropList(cmd);
@@ -313,7 +314,7 @@ void wSessionSaveState(WScreen * scr)
return;
}
list = WMCreatePLArray(NULL);
list = WMCreateEmptyPLArray();
wapp_list = WMCreateArray(16);
@@ -487,8 +488,6 @@ void wSessionRestoreState(WScreen *scr)
if (!scr->session_state)
return;
WMPLSetCaseSensitive(True);
apps = WMGetFromPLDictionary(scr->session_state, sApplications);
if (!apps)
return;
@@ -579,8 +578,13 @@ void wSessionRestoreState(WScreen *scr)
if (class)
wfree(class);
}
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* clean up */
WMPLSetCaseSensitive(False);
/* WMPLSetCaseSensitive(False); */
}
void wSessionRestoreLastWorkspace(WScreen * scr)
@@ -594,8 +598,6 @@ void wSessionRestoreLastWorkspace(WScreen * scr)
if (!scr->session_state)
return;
WMPLSetCaseSensitive(True);
wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
if (!wks || !WMIsPLString(wks))
return;
@@ -605,8 +607,13 @@ void wSessionRestoreLastWorkspace(WScreen * scr)
if (!value)
return;
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* clean up */
WMPLSetCaseSensitive(False);
/* WMPLSetCaseSensitive(False); */
/* Get the workspace number for the workspace name */
w = wGetWorkspaceNumber(scr, value);
+6 -5
View File
@@ -109,16 +109,16 @@ void Shutdown(WShutdownMode mode)
}
}
static void restoreWindows(WMBag * bag, WMBagIterator iter)
static void restoreWindows(WMBag * bag, WMBagIterator *iter)
{
WCoreWindow *next;
WCoreWindow *core;
WWindow *wwin;
if (iter == NULL) {
core = WMBagFirst(bag, &iter);
if (*iter < 0) {
core = WMBagFirst(bag, iter);
} else {
core = WMBagNext(bag, &iter);
core = WMBagNext(bag, iter);
}
if (core == NULL)
@@ -168,7 +168,8 @@ void RestoreDesktop(WScreen * scr)
wDestroyInspectorPanels();
/* reparent windows back to the root window, keeping the stacking order */
restoreWindows(scr->stacking_list, NULL);
WMBagIterator iter = -1;
restoreWindows(scr->stacking_list, &iter);
XUngrabServer(dpy);
XSetInputFocus(dpy, PointerRoot, RevertToParent, CurrentTime);
+1 -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);
+32 -17
View File
@@ -174,15 +174,18 @@ static WMPropList *get_value_from_instanceclass(const char *value)
key = WMCreatePLString(value);
WMPLSetCaseSensitive(True);
if (w_global.domain.window_attr->dictionary)
val = key ? WMGetFromPLDictionary(w_global.domain.window_attr->dictionary, key) : NULL;
if (key)
WMReleasePropList(key);
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
return val;
}
@@ -219,8 +222,6 @@ void wDefaultFillAttributes(const char *instance, const char *class,
dn = get_value_from_instanceclass(instance);
dc = get_value_from_instanceclass(class);
WMPLSetCaseSensitive(True);
if ((w_global.domain.window_attr->dictionary) && (useGlobalDefault))
da = WMGetFromPLDictionary(w_global.domain.window_attr->dictionary, AnyWindow);
@@ -311,8 +312,13 @@ void wDefaultFillAttributes(const char *instance, const char *class,
APPLY_VAL(value, no_language_button, ANoLanguageButton);
#endif
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* clean up */
WMPLSetCaseSensitive(False);
/* WMPLSetCaseSensitive(False); */
}
static WMPropList *get_generic_value(const char *instance, const char *class,
@@ -322,8 +328,6 @@ static WMPropList *get_generic_value(const char *instance, const char *class,
value = NULL;
WMPLSetCaseSensitive(True);
/* Search the icon name using class and instance */
if (class && instance) {
char *buffer;
@@ -371,7 +375,12 @@ static WMPropList *get_generic_value(const char *instance, const char *class,
value = WMGetFromPLDictionary(dict, option);
}
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
return value;
}
@@ -545,15 +554,13 @@ void wDefaultChangeIcon(const char *instance, const char *class, const char *fil
int same = 0;
if (!dict) {
dict = WMCreatePLDictionary(NULL, NULL);
dict = WMCreateEmptyPLDictionary();
if (dict)
db->dictionary = dict;
else
return;
}
WMPLSetCaseSensitive(True);
if (instance && class) {
char *buffer;
@@ -570,7 +577,7 @@ void wDefaultChangeIcon(const char *instance, const char *class, const char *fil
if (file) {
value = WMCreatePLString(file);
icon_value = WMCreatePLDictionary(AIcon, value, NULL);
icon_value = WMCreatePLDictionary(AIcon, value);
WMReleasePropList(value);
def_win = WMGetFromPLDictionary(dict, AnyWindow);
@@ -600,7 +607,12 @@ void wDefaultChangeIcon(const char *instance, const char *class, const char *fil
if (icon_value)
WMReleasePropList(icon_value);
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
}
void wDefaultPurgeInfo(const char *instance, const char *class)
@@ -612,8 +624,6 @@ void wDefaultPurgeInfo(const char *instance, const char *class)
init_wdefaults();
}
WMPLSetCaseSensitive(True);
buffer = wmalloc(strlen(class) + strlen(instance) + 2);
sprintf(buffer, "%s.%s", instance, class);
key = WMCreatePLString(buffer);
@@ -631,7 +641,12 @@ void wDefaultPurgeInfo(const char *instance, const char *class)
wfree(buffer);
WMReleasePropList(key);
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
}
/* --------------------------- Local ----------------------- */
+3 -3
View File
@@ -283,10 +283,10 @@ void wWindowDestroy(WWindow *wwin)
XFree(wwin->wm_hints);
if (wwin->wm_instance)
XFree(wwin->wm_instance);
wfree(wwin->wm_instance);
if (wwin->wm_class)
XFree(wwin->wm_class);
wfree(wwin->wm_class);
if (wwin->wm_gnustep_attr)
wfree(wwin->wm_gnustep_attr);
@@ -1413,7 +1413,7 @@ WWindow *wManageWindow(WScreen *scr, Window window)
/* Update name must come after WApplication stuff is done */
wWindowUpdateName(wwin, title);
if (title)
XFree(title);
wfree(title);
XUngrabServer(dpy);
+1 -1
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 {
+9 -6
View File
@@ -597,7 +597,7 @@ static void saveSettings(WMWidget *button, void *client_data)
dict = db->dictionary;
if (!dict) {
dict = WMCreatePLDictionary(NULL, NULL);
dict = WMCreateEmptyPLDictionary();
if (dict) {
db->dictionary = dict;
} else {
@@ -609,10 +609,8 @@ static void saveSettings(WMWidget *button, void *client_data)
if (showIconFor(WMWidgetScreen(button), panel, NULL, NULL, USE_TEXT_FIELD) < 0)
return;
WMPLSetCaseSensitive(True);
winDic = WMCreatePLDictionary(NULL, NULL);
appDic = WMCreatePLDictionary(NULL, NULL);
winDic = WMCreateEmptyPLDictionary();
appDic = WMCreateEmptyPLDictionary();
/* Save the icon info */
/* The flag "Ignore client suplied icon is not selected" */
@@ -709,8 +707,13 @@ static void saveSettings(WMWidget *button, void *client_data)
UpdateDomainFile(db);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* clean up */
WMPLSetCaseSensitive(False);
/* WMPLSetCaseSensitive(False); */
}
static void applySettings(WMWidget *button, void *client_data)
+3 -3
View File
@@ -797,7 +797,7 @@ void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
ws = menu->entry_no - MC_WORKSPACE1;
while (i > 0) {
wstrlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
strlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
entry = wMenuAddCallback(menu, title, switchWSCommand, (void *)ws);
entry->flags.indicator = 1;
@@ -852,10 +852,10 @@ void wWorkspaceSaveState(WScreen * scr, WMPropList * old_state)
make_keys();
old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
parr = WMCreatePLArray(NULL);
parr = WMCreateEmptyPLArray();
for (i = 0; i < scr->workspace_count; i++) {
pstr = WMCreatePLString(scr->workspaces[i]->name);
wks_state = WMCreatePLDictionary(dName, pstr, NULL);
wks_state = WMCreatePLDictionary(dName, pstr);
WMReleasePropList(pstr);
if (!wPreferences.flags.noclip) {
pstr = wClipSaveWorkspaceState(scr, i);
+94
View File
@@ -0,0 +1,94 @@
#!/bin/sh
set -e
project_base="$(dirname $0)"
export WMAKER_USER_ROOT="$HOME/tmp/GNUstep"
gs_base="$WMAKER_USER_ROOT"
gs_defaults="$gs_base/Defaults"
gs_system_defaults="$project_base"/WindowMaker/Defaults
wm_base="$gs_base/Library/WindowMaker"
wm_backgrounds="$wm_base/Backgrounds"
wm_iconsets="$wm_base/IconSets"
wm_pixmaps="$wm_base/Pixmaps"
gs_icons="$gs_base/Library/Icons"
wm_style="$wm_base/Style"
wm_styles="$wm_base/Styles"
wm_themes="$wm_base/Themes"
WindowMaker="$project_base/src/.libs/wmaker"
convertfonts="$project_base/util/convertfonts"
make_dir_if_needed ()
{
if [ ! -d "$1" ] ; then
install -m 0755 -d "$1"
fi
}
rename_dir_if_possible ()
{
if [ ! -d "$2" ] ; then
if [ -d "$1" ] ; then
mv "$1" "$2"
fi
fi
}
copy_defaults_if_needed ()
{
file="$gs_defaults/$1"
system_file="$gs_system_defaults/$1"
if [ ! -f "$file" ] ; then
install -m 0644 "$system_file" "$file"
fi
}
make_dir_if_needed "$gs_defaults"
make_dir_if_needed "$wm_base"
make_dir_if_needed "$wm_backgrounds"
make_dir_if_needed "$wm_iconsets"
make_dir_if_needed "$wm_pixmaps"
make_dir_if_needed "$gs_icons"
rename_dir_if_possible "$wm_style" "$wm_styles"
make_dir_if_needed "$wm_styles"
make_dir_if_needed "$wm_themes"
export LD_LIBRARY_PATH="$project_base/wrlib/.libs:$project_base/WINGs/.libs:$LD_LIBRARY_PATH"
copy_defaults_if_needed WindowMaker
copy_defaults_if_needed WMRootMenu
copy_defaults_if_needed WMState
#copy_defaults_if_needed WMWindowAttributes
if [ -x $convertfonts -a ! -e "$wm_base/.fonts_converted" ] ; then
# --keep-xlfd is used in order to preserve the original information
$convertfonts --keep-xlfd "$gs_defaults/WindowMaker"
if [ -f "$gs_defaults/WMGLOBAL" ] ; then
$convertfonts --keep-xlfd "$gs_defaults/WMGLOBAL"
fi
find "$wm_styles" -mindepth 1 -maxdepth 1 -type f -print0 |
xargs -0 -r -n 1 $convertfonts --keep-xlfd
touch "$wm_base/.fonts_converted"
fi
if [ -n "$1" -a -x "$WindowMaker$1" ] ; then
WindowMaker="$WindowMaker$1"
shift
fi
for i in $(seq 5 10) ; do
if [ "x$DISPLAY" != "x:$i" ] ; then
xephyr_display=":$i"
break
fi
done
echo "Running Xephyr on display $xephyr_display"
Xephyr -screen 640x480 "$xephyr_display" &
xephyr_pid=$!
DISPLAY="$xephyr_display" gdb \
--directory "$project_base" \
--quiet \
--args "$WindowMaker" -display "$xephyr_display" --for-real "$@"
kill $xephyr_pid
+25 -19
View File
@@ -18,70 +18,75 @@ AM_CPPFLAGS = \
liblist= @LIBRARY_SEARCH_PATH@ @INTLIBS@
wdwrite_LDADD = $(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
wdwrite_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wdread_LDADD = $(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
wdread_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wxcopy_LDADD = @XLFLAGS@ @XLIBS@ \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a
wxpaste_LDADD = @XLFLAGS@ @XLIBS@
getstyle_LDADD = $(top_builddir)/WINGs/libWUtil.la\
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
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)/wmaker-rs/target/debug/libwmaker_rs.a\
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
@XLFLAGS@ @XLIBS@ $(liblist)
setstyle_SOURCES = setstyle.c fontconv.c common.h
convertfonts_LDADD = $(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
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\
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
seticons_LDADD= \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
geticonset_LDADD= $(top_builddir)/WINGs/libWUtil.la\
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
geticonset_LDADD= \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wmagnify_LDADD = \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
$(top_builddir)/wrlib/libwraster.la \
@XLFLAGS@ @XLIBS@ @INTLIBS@
wmsetbg_LDADD = \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
$(top_builddir)/wrlib/libwraster.la \
@XLFLAGS@ @LIBXINERAMA@ @XLIBS@ @INTLIBS@
wmgenmenu_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
@INTLIBS@
wmgenmenu_SOURCES = wmgenmenu.c wmgenmenu.h
wmmenugen_LDADD = \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
@INTLIBS@
wmmenugen_SOURCES = wmmenugen.c wmmenugen.h wmmenugen_misc.c \
@@ -93,7 +98,8 @@ wmiv_CFLAGS = @PANGO_CFLAGS@ @PTHREAD_CFLAGS@
wmiv_LDADD = \
$(top_builddir)/wrlib/libwraster.la \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
@XLFLAGS@ @XLIBS@ @GFXLIBS@ \
@PANGO_LIBS@ @PTHREAD_LIBS@ @LIBEXIF@
+6 -1
View File
@@ -131,7 +131,12 @@ int main(int argc, char **argv)
/* this contradicts big time with getstyle */
setlocale(LC_ALL, "");
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
style = WMReadPropListFromFile(file);
if (!style) {
+1 -1
View File
@@ -109,7 +109,7 @@ int main(int argc, char **argv)
if (window_attrs && WMIsPLDictionary(window_attrs)) {
icon_value = WMGetFromPLDictionary(window_attrs, icon_key);
if (icon_value) {
icondic = WMCreatePLDictionary(icon_key, icon_value, NULL);
icondic = WMCreatePLDictionary(icon_key, icon_value);
WMPutInPLDictionary(iconset, window_name, icondic);
}
}
+7 -2
View File
@@ -337,7 +337,12 @@ int main(int argc, char **argv)
return 1;
}
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
path = wdefaultspathfordomain("WindowMaker");
@@ -357,7 +362,7 @@ int main(int argc, char **argv)
prop = val;
}
style = WMCreatePLDictionary(NULL, NULL);
style = WMCreateEmptyPLDictionary();
for (i = 0; options[i] != NULL; i++) {
key = WMCreatePLString(options[i]);
+7 -2
View File
@@ -427,7 +427,12 @@ int main(int argc, char **argv)
file = argv[0];
WMPLSetCaseSensitive(False);
/*
* Rust rewrite note: this API surface has been removed, but we leave in
* a record of where it was invoked to set a value other than the
* default, in case it helps to track down bugs in the future.
*/
/* WMPLSetCaseSensitive(False); */
path = wdefaultspathfordomain("WindowMaker");
@@ -465,7 +470,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
+1 -1
View File
@@ -103,7 +103,7 @@ int main(int argc, char **argv)
dict = WMReadPropListFromFile(path);
if (!dict) {
dict = WMCreatePLDictionary(key, value, NULL);
dict = WMCreatePLDictionary(key, value);
} else {
WMPutInPLDictionary(dict, key, value);
}
+8 -8
View File
@@ -35,8 +35,8 @@
static void addWMMenuEntryCallback(WMMenuEntry *aEntry);
static void assemblePLMenuFunc(WMTreeNode *aNode, void *data);
static int dirParseFunc(const char *filename, const struct stat *st, int tflags, struct FTW *ftw);
static int menuSortFunc(const void *left, const void *right);
static int nodeFindSubMenuByNameFunc(const void *item, const void *cdata);
static int menuSortFunc(const WMTreeNode *left, const WMTreeNode *right);
static int nodeFindSubMenuByNameFunc(const WMTreeNode *tree, const void *cdata);
static WMTreeNode *findPositionInMenu(const char *submenu);
@@ -178,7 +178,7 @@ int main(int argc, char **argv)
}
WMSortTree(menu, menuSortFunc);
WMTreeWalk(menu, assemblePLMenuFunc, previousDepth, True);
WMTreeWalk(menu, assemblePLMenuFunc, previousDepth);
i = WMGetArrayItemCount(plMenuNodes);
if (i > 2) { /* more than one submenu unprocessed is almost certainly an error */
@@ -324,13 +324,13 @@ static void assemblePLMenuFunc(WMTreeNode *aNode, void *data)
/* sort the menu tree; callback for WMSortTree()
*/
static int menuSortFunc(const void *left, const void *right)
static int menuSortFunc(const WMTreeNode *left, const WMTreeNode *right)
{
WMMenuEntry *leftwm;
WMMenuEntry *rightwm;
leftwm = (WMMenuEntry *)WMGetDataForTreeNode(*(WMTreeNode **)left);
rightwm = (WMMenuEntry *)WMGetDataForTreeNode(*(WMTreeNode **)right);
leftwm = (WMMenuEntry *)WMGetDataForTreeNode(left);
rightwm = (WMMenuEntry *)WMGetDataForTreeNode(right);
/* submenus first */
if (!leftwm->CmdLine && rightwm->CmdLine)
@@ -380,11 +380,11 @@ static WMTreeNode *findPositionInMenu(const char *submenu)
/* find node where Name = cdata and node is a submenu
*/
static int nodeFindSubMenuByNameFunc(const void *item, const void *cdata)
static int nodeFindSubMenuByNameFunc(const WMTreeNode *tree, const void *cdata)
{
WMMenuEntry *wm;
wm = (WMMenuEntry *)item;
wm = (WMMenuEntry *)WMGetDataForTreeNode(tree);
if (wm->CmdLine) /* if it has a cmdline, it can't be a submenu */
return 0;
+2 -2
View File
@@ -1199,14 +1199,14 @@ static void changeTextureForWorkspace(const char *domain, char *texture, int wor
array = getValueForKey("WindowMaker", "WorkspaceSpecificBack");
if (!array) {
array = WMCreatePLArray(NULL, NULL);
array = WMCreateEmptyPLArray();
}
j = WMGetPropListItemCount(array);
if (workspace >= j) {
WMPropList *empty;
empty = WMCreatePLArray(NULL, NULL);
empty = WMCreateEmptyPLArray();
while (j++ < workspace - 1) {
WMAddToPLArray(array, empty);
+1 -2
View File
@@ -1,11 +1,10 @@
pub mod application;
pub mod app_icon;
pub mod app_menu;
pub mod application;
pub mod defaults;
pub mod dock;
pub mod global;
pub mod icon;
pub mod memory;
pub mod menu;
pub mod properties;
pub mod screen;
-180
View File
@@ -1,180 +0,0 @@
//! Provides an FFI-compatible allocator for C.
//!
//! This replaces the bulk of the code form WINGs/memory.c, but
//! it should go away once we're in a place that we don't need
//! to rely on `malloc` anymore in C code.
use std::alloc::{Layout, alloc_zeroed, dealloc, realloc};
use std::collections::BTreeMap;
use std::ffi::c_void;
use std::sync::Mutex;
#[derive(Clone, Copy, Debug)]
struct Allocation {
layout: Layout,
rc: u32,
}
type WMallocMap = BTreeMap<usize, Allocation>;
static ALLOCS: Mutex<WMallocMap> = Mutex::new(WMallocMap::new());
const ALIGN: usize = 64;
/// A wrapper around the Rust allocator API's `alloc_zeroed`
/// function for FFI.
///
/// Note: This will always return nil on a zero-sized
/// allocation. Don't try to use this to allocate Rust ZSTs;
/// it is only suitable for FFI.
///
/// # Safety
/// This will return a 64-byte aligned pointer of the specified
/// length, or nil if the allocation cannot be satisfied. It is
/// the caller's responsibility to ensure that the result is not
/// null, to avoid violating memory safety, and to make sure
/// that any alignment for any value the pointer is used to
/// refer to is suitably aligned.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wmalloc(size: usize) -> *mut c_void {
if size == 0 {
return std::ptr::null_mut();
}
let layout = Layout::from_size_align(size, ALIGN).expect("layout makes sense");
let ptr = unsafe { alloc_zeroed(layout) };
if ptr.is_null() {
return std::ptr::null_mut();
}
let rc = 1;
let alloc = Allocation { layout, rc };
let mut allocs = ALLOCS.lock().expect("lock not poisoned");
allocs.insert(ptr.addr(), alloc);
ptr.cast()
}
/// A wrapper around the Rust allocator API's `dealloc` function
/// for FFI.
///
/// If the pointer argument is nil, this is a nop. Otherwise,
/// it must have been allocated by `wmalloc` or `wrealloc`.
///
/// # Safety
/// The caller must ensure that the pointer that is passed to
/// this function was allocated by `wmalloc`. Note that this
/// will not free unless this was the allocation's last
/// reference.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wfree(ptr: *mut c_void) {
if ptr.is_null() {
return;
}
let alloc = {
let mut allocs = ALLOCS.lock().expect("wfree: allocs is unpoisoned");
let addr = ptr.addr();
let alloc = allocs.get_mut(&addr).expect("wfree: allocs records ptr");
alloc.rc -= 1;
let alloc = alloc.clone();
if alloc.rc == 0 {
allocs.remove(&addr).expect("wfree: allocs unaltered");
}
alloc
};
// We can call `dealloc` without holding the ALLOCS lock.
if alloc.rc == 0 {
unsafe {
dealloc(ptr.cast(), alloc.layout);
}
}
}
/// A wrapper around the Rust allocator API's `realloc` function
/// for FFI calls.
///
/// Note that this attempts will always free and return NULL on
/// a reallocation of size 0, so do not try to use this to
/// allocate Rust ZSTs: it is purely for FFI. If the source
/// pointer is nil and size is positive, then the behavior is
/// that of `wmalloc`.
///
/// The pointer argument just have been allocated previously
/// with `wmalloc` or `wrealloc`.
///
/// If successful, returns a non-nil pointer, and the old
/// pointer should be considered invalid, and must not be
/// dereferenced. If the old size was smaller than the new
/// size, elements, elements after the old size are zeroed.
/// Elements between the start of the array and the minimum of
/// the old and new sizes are unchanged.
///
/// On failure, returns nil, and does not free the source
/// pointer or otherwise alter its contents.
///
/// # Safety
/// This will return a 64-byte aligned pointer of the specified
/// length, or nil if the allocation cannot be satisfied. It is
/// the caller's responsibility to ensure that the result is not
/// null, to avoid violating memory safety, and to make sure
/// that any alignment for any value the pointer is used to
/// refer to is suitably aligned.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wrealloc(ptr: *mut c_void, size: usize) -> *mut c_void {
if size == 0 {
unsafe {
wfree(ptr);
}
return std::ptr::null_mut();
}
if ptr.is_null() {
return unsafe { wmalloc(size) };
}
let new_layout = Layout::from_size_align(size, ALIGN).expect("layout ok");
let addr = ptr.addr();
let old_layout = {
let allocs = ALLOCS.lock().expect("wrealloc: allocs is unpoisoned");
let alloc = allocs.get(&addr).expect("wrealloc: allocs records ptr");
alloc.layout
};
let old_size = old_layout.size();
let ptr = unsafe { realloc(ptr.cast(), old_layout, size) };
if ptr.is_null() {
return std::ptr::null_mut();
}
{
let mut allocs = ALLOCS.lock().expect("wrealloc: allocs still unpoisoned");
let alloc = allocs.get_mut(&addr).expect("wmrealloc: allocs knows ptr");
alloc.layout = new_layout;
}
if old_size < size {
let zlen = size - old_size;
let zptr = ptr.wrapping_add(old_size);
unsafe {
std::ptr::write_bytes(zptr, 0, zlen);
}
}
ptr.cast()
}
/// Increments the reference count on `ptr`.
///
/// # Safety
/// The caller must insure that `ptr` refers to a valid, active
/// allocation that was made with `wmalloc` or `wrealloc`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wretain(ptr: *mut c_void) -> *mut c_void {
let addr = ptr.addr();
let mut allocs = ALLOCS.lock().expect("wretain: allocs is unpoisoned");
let alloc = allocs.get_mut(&addr).expect("wretain: allocs records ptr");
alloc.rc += 1;
ptr
}
/// Decrements the reference count on `ptr`, and frees if it
/// reaches 0, but wrapping `wfree`.
///
/// # Safety
/// `ptr` must be valid as an argument to `wfree`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wrelease(ptr: *mut c_void) {
unsafe {
wfree(ptr);
}
}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "wutil-rs"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["staticlib"]
[build-dependencies]
cc = "1.0"
[dependencies]
atomic-write-file = "0.3"
hashbrown = "0.16.0"
nom = "8.0"
nom-language = "0.1"
x11 = "2.21.0"
+31
View File
@@ -0,0 +1,31 @@
AUTOMAKE_OPTIONS =
RUST_SOURCES = \
src/array.rs \
src/bag.rs \
src/data.rs \
src/defines.c \
src/defines.rs \
src/find_file.rs \
src/hash_table.rs \
src/lib.rs \
src/memory.rs \
src/notification.rs \
src/prop_list.rs \
src/string.rs
src/tree.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
+8
View File
@@ -0,0 +1,8 @@
use cc;
fn main() {
cc::Build::new()
.file("src/defines.c")
.compile("defines");
println!("cargo::rerun-if-changed=src/defines.c");
}
+449
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());
}
}
}
+372
View File
@@ -0,0 +1,372 @@
//! Simple sorted set.
//!
//! ## Rust rewrite notes
//!
//! This was originally a full-blown red-black tree, but it was really only used
//! to keep a sorted list. It has been rewritten as a sorted `Vec`. We
//! technically no longer have O(log(n)) insertion time. Given set sizes and
//! actual execution time, that shouldn't matter.
//!
//! Prefer a proper Rust collection over this for new code.
use std::{
ffi::{c_int, c_void},
ptr::NonNull,
};
#[derive(Default)]
pub struct Bag {
inner: Vec<(c_int, NonNull<c_void>)>,
}
/// Sentinel iterator value. In C, just check for negative values.
pub const NOT_FOUND: c_int = -1;
impl Bag {
/// Returns `Ok(index)` if an item keyed by `key` is in `self`, else
/// `Err(index)` if an item keyed by `key` could be inserted at `index`.
fn search(&self, key: c_int) -> Result<usize, usize> {
self.inner.binary_search_by_key(&key, |(key, _)| *key)
}
/// Sets a value associated with `key` to `value`. Clobbers any extant value.
fn set(&mut self, key: c_int, value: NonNull<c_void>) {
match self.search(key) {
Ok(i) => self.inner[i] = (key, value),
Err(i) => self.inner.insert(i, (key, value)),
}
}
}
pub mod ffi {
use super::{Bag, NOT_FOUND};
use std::{
ffi::{c_int, c_void},
ptr::{self, NonNull},
};
/// Basic constructor. Free with [`WMFreeBag`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateTreeBag() -> *mut Bag {
Box::leak(Box::new(Bag::default()))
}
/// Retrieves the value associated with `key`, or null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromBag(bag: *mut Bag, key: c_int) -> *mut c_void {
if bag.is_null() {
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
bag
.search(key)
.ok()
.and_then(|index| bag.inner.get(index).copied())
.map(|(_, value)| value.as_ptr())
.unwrap_or(ptr::null_mut())
}
/// Sets the value associated with `key` to `item`. If `item` is null,
/// removes any extant value.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSetInBag(bag: *mut Bag, key: c_int, item: *mut c_void) {
if bag.is_null() {
return;
}
let bag = unsafe { &mut *bag };
if let Some(item) = NonNull::new(item) {
bag.set(key, item);
} else {
if let Some(i) = bag.search(key).ok() {
bag.inner.remove(i);
}
}
}
/// Clears the contents of `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMEmptyBag(bag: *mut Bag) {
if bag.is_null() {
return;
}
unsafe {
(*bag).inner.clear();
}
}
/// Deletes `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFreeBag(bag: *mut Bag) {
if bag.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(bag);
}
}
/// Initializes `ptr` to the first element of `bag` and returns that
/// element. Sets `ptr` to [`NOT_FOUND`] and returns null if `bag` is empty.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagFirst(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if let Some((_, value)) = bag.inner.first() {
*ptr = 0;
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Initializes `ptr` to the last element of `bag` and returns that
/// element. Sets `ptr` to [`NOT_FOUND`] and returns null if `bag` is empty.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagLast(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if let Some((_, value)) = bag.inner.last() {
*ptr = (bag.inner.len() - 1) as c_int;
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Advances `ptr` to the next element of `bag` and returns it. Sets `ptr`
/// to [`NOT_FOUND`] and returns null if `ptr` is already at the end of
/// `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagNext(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if *ptr < 0 {
return ptr::null_mut();
}
*ptr += 1;
if let Some((_, value)) = bag.inner.get(*ptr as usize) {
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Decrements `ptr` to the previous element of `bag` and returns it. Sets `ptr`
/// to [`NOT_FOUND`] and returns null if `ptr` is already at the beginning of
/// `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagPrevious(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if *ptr <= 0 {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
*ptr -= 1;
if let Some((_, value)) = bag.inner.get(*ptr as usize) {
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Sets `ptr` to the element of `bag` with `key` and returns the associated
/// value. Sets `ptr` to [`NOT_FOUND`] and returns null if there is no such
/// element.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagIteratorAtIndex(
bag: *mut Bag,
key: c_int,
ptr: *mut c_int,
) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if key < 0 {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
if let Some(index) = bag.search(key).ok() {
*ptr = index as c_int;
bag.inner[index].1.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
}
#[cfg(test)]
mod test {
use super::{ffi, Bag, NOT_FOUND};
use std::{
ffi::{c_int, c_void},
ptr::NonNull,
};
fn void_of<T>(t: &mut T) -> NonNull<c_void> {
NonNull::new(t as *mut _ as *mut c_void).unwrap()
}
#[test]
fn insert_out_of_order() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
assert_eq!(
b.inner,
&[
(3, void_of(&mut y)),
(5, void_of(&mut x)),
(7, void_of(&mut z))
]
);
}
#[test]
fn iterate_forward() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagFirst(&mut b, &mut iterator),
void_of(&mut y).as_ptr()
);
assert_eq!(iterator, 0);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
assert!(ffi::WMBagNext(&mut b, &mut iterator).is_null());
assert_eq!(iterator, NOT_FOUND);
}
}
#[test]
fn iterate_backward() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagLast(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
assert_eq!(
ffi::WMBagPrevious(&mut b, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagPrevious(&mut b, &mut iterator),
void_of(&mut y).as_ptr()
);
assert_eq!(iterator, 0);
assert!(ffi::WMBagPrevious(&mut b, &mut iterator).is_null());
assert_eq!(iterator, NOT_FOUND);
}
}
#[test]
fn find_iterate() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagIteratorAtIndex(&mut b, 5, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
}
}
}
+209
View File
@@ -0,0 +1,209 @@
//! 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>>);
impl Data {
/// Runs `f` on a borrow of `self`'s data, returning whatever `f` does.
///
/// This is provided because the internal structure of `Data` does not make
/// it easy to borrow its contents directly. As we migrate away from the
/// original C interfaces to WINGs data structures, we may be able to
/// provide a direct borrow, which would make this method obsolete. (That
/// would be a good thing.)
pub fn with_bytes<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
f(&self.0.borrow().bytes)
}
}
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;
}
}
}
+5
View File
@@ -0,0 +1,5 @@
#include "../../config-paths.h"
const char *get_GSUSER_SUBDIR() {
return GSUSER_SUBDIR;
}
+45
View File
@@ -0,0 +1,45 @@
//! Lookup functions for preprocessor symbols.
//!
//! Functions in this module may be called to get the value of various
//! preprocessor symbols that are available on the C side of things.
//!
//! ## Rust rewrite notes
//!
//! Until we move away from autootols entirely, we might be stuck with this as
//! along as it makes sense to keep the configure script as the main entrypoint
//! for compile-time configuration.
use std::ffi::{c_char, CStr};
// Functions defined in src/defines.c.
unsafe extern "C" {
fn get_GSUSER_SUBDIR() -> *const c_char;
}
/// Returns the value of the GSUSER_SUBDIR preprocessor symbol defined at
/// Autotools configuration time prior to compilation. This is the final
/// component of the root path where user data files are stored (usually
/// `GNUstep`, as in `$HOME/GNUstep`). Returns `None` if this value cannot be
/// determined or is empty.
pub fn gsuser_subdir() -> Option<String> {
let s = unsafe { get_GSUSER_SUBDIR() };
if s.is_null() {
return None;
}
let s = unsafe { CStr::from_ptr(s) };
if s.is_empty() {
return None;
}
String::from_utf8(s.to_bytes().iter().copied().collect()).ok()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn gsuser_subdir_is_set() {
let s = gsuser_subdir().unwrap();
assert!(!s.is_empty());
}
}
+395
View File
@@ -0,0 +1,395 @@
//! 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.
//!
//! ## Rust rewrite notes
//!
//! Many of 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. (This is a TOCTOU issue.) 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 crate::defines;
use std::{
env,
ffi::{CStr, OsStr},
fs::File,
io,
path::{Component, Path, PathBuf},
};
/// Tries to interpret `s` as a UTF-8 path. Returns `None` if decoding `s`
/// fails.
pub fn path_from_cstr(s: &CStr) -> Option<PathBuf> {
s.to_str().ok().map(PathBuf::from)
}
/// 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
}
/// Returns the root path that user data will be stored at. This is probably
/// `$HOME/GNUstep` (or some other default if a different path was specified for
/// [`defaults::gsuser_subdir`] at compilation time), unless the environment
/// variable `WMAKER_USER_ROOT` is defined, in which case it's that.
pub fn user_gnustep_path() -> Option<PathBuf> {
match env::var("WMAKER_USER_ROOT") {
Ok(path) => {
if let Some(path) = absolute(&PathBuf::from(&path)) {
return Some(path);
}
}
Err(env::VarError::NotUnicode(_)) => {
// TODO: warn.
}
Err(env::VarError::NotPresent) => (),
}
match (env::home_dir(), defines::gsuser_subdir()) {
(None, _) => {
// TODO: warn.
None
}
(Some(mut parent), Some(subdir)) => {
parent.push(subdir);
Some(parent)
}
(Some(_), None) => {
// TODO: warn.
None
}
}
}
/// Creates the directory at `p` and all of its parents, respecting the current
/// umask as much as possible. Only allows creation of paths under
/// [`user_gnustep_path`].
pub fn create_path_hierarchy<P: AsRef<Path>>(p: P) -> io::Result<()> {
let p = p.as_ref();
let Ok(canonical_p) = p.canonicalize() else {
return Err(io::Error::other("cannot canonicalize requested path"));
};
let Some(wmaker_user_root) = user_gnustep_path() else {
return Err(io::Error::other("cannot determine WMAKER_USER_ROOT. try setting the environment variable WMAKER_USER_ROOT."));
};
let Ok(wmaker_user_root) = Path::new(&wmaker_user_root).canonicalize() else {
return Err(io::Error::other(
"cannot canonicalize path for WMAKER_USER_ROOT",
));
};
if !canonical_p.starts_with(&wmaker_user_root) {
return Err(io::Error::other(
"requested path is not under WMAKER_USER_ROOT",
));
}
create_path_hierarchy_impl(&canonical_p)
}
/// Removes the file at `p` (and anything under it, if `p` is a directory). Only
/// allows deletion of files under [`user_gnustep_path`]`/Defaults` or
/// [`user_gnustep_path`]`/Library`.
pub fn remove_path_hierarchy<P: AsRef<Path>>(p: P) -> io::Result<()> {
let p = p.as_ref();
let Ok(canonical_p) = p.canonicalize() else {
return Err(io::Error::other("cannot canonicalize requested path"));
};
let Some(wmaker_user_root) = user_gnustep_path() else {
return Err(io::Error::other("cannot determine WMAKER_USER_ROOT. try setting the environment variable WMAKER_USER_ROOT."));
};
let Ok(wmaker_user_root) = Path::new(&wmaker_user_root).canonicalize() else {
return Err(io::Error::other(
"cannot canonicalize path for WMAKER_USER_ROOT",
));
};
let mut defaults = wmaker_user_root.clone();
defaults.push("Defaults");
let mut library = wmaker_user_root;
library.push("Library");
if !canonical_p.starts_with(&defaults) && !canonical_p.starts_with(&library) {
return Err(io::Error::other(
"requested path is not under WMAKER_USER_ROOT/Defaults or WMAKER_USER_ROOT/Library",
));
}
std::fs::remove_dir_all(canonical_p)
}
#[cfg(target_family = "unix")]
fn create_path_hierarchy_impl(p: &Path) -> io::Result<()> {
use std::os::unix::fs::DirBuilderExt;
std::fs::DirBuilder::new()
.recursive(true)
.mode(0o777)
.create(p)
}
#[cfg(not(target_family = "unix"))]
fn create_path_hierarchy_impl(p: &Path) -> io::Result<()> {
std::fs::DirBuilder::new().recursive(true).create(p)
}
pub mod ffi {
use super::{absolute, create_path_hierarchy, in_paths, path_from_cstr, remove_path_hierarchy, user_gnustep_path};
use crate::memory::alloc_bytes;
use std::{
env,
ffi::{c_char, c_int, CStr, OsStr},
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;
}
}
/// Delegates to [`create_path_hierarchy`].
///
/// ## Rust rewrite notes
///
/// The original C implementation of this function stripped the last path
/// component if it did not end in a `'/'` (i.e., it looked like a regular
/// file instead of a directory). This behavior does not appear to be relied
/// upon by any existing callers except for `WMWritePropListToFile`, which
/// has been rewritten, and it is not super portable (and annoying because
/// it adds weird edge cases). So each component of `path` is treated as a
/// directory to be made.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wmkdirhier(path: *const c_char) -> c_int {
if path.is_null() {
return 0;
}
let path = unsafe { CStr::from_ptr(path) };
let Some(path) = path_from_cstr(path) else {
return 0;
};
if create_path_hierarchy(path).is_ok() {
return 1;
} else {
return 0;
}
}
/// Delegates to [`remove_path_hierarchy`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wrmdirhier(path: *const c_char) -> c_int {
if path.is_null() {
return 0;
}
let path = unsafe { CStr::from_ptr(path) };
let Some(path) = path_from_cstr(path) else {
return 0;
};
if remove_path_hierarchy(path).is_ok() {
return 1;
} else {
return 0;
}
}
/// Delegates to [`user_gnustep_path`]. Returns the path, which must be The
/// returned value must be freed with [`crate::memory::free_bytes`] or
/// [`crate::memory::ffi::wfree`], path cannot be determined.
///
/// ## Rust rewrite notes
///
/// This was originally in `WINGs/userdefaults.c`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wusergnusteppath() -> *mut c_char {
if let Some(path) = user_gnustep_path() {
to_c_str(&path)
} else {
ptr::null_mut()
}
}
}
+294
View File
@@ -0,0 +1,294 @@
use hashbrown::hash_map::{self, HashMap};
use std::{
ffi::CStr,
hash::{Hash, Hasher},
mem,
};
pub enum HashTable {
PointerKeyed(HashMap<*mut u8, *mut u8>),
StringKeyed(HashMap<StringKey, *mut u8>),
}
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 u8) -> Option<*mut u8> {
match self {
HashTable::PointerKeyed(m) => {
let key = key.cast_mut();
m.get(&key).copied()
}
HashTable::StringKeyed(m) => {
let key = StringKey(key.cast_mut());
m.get(&key).copied()
}
}
}
pub unsafe fn insert(&mut self, key: *mut u8, data: *mut u8) -> Option<*mut u8> {
match self {
HashTable::PointerKeyed(m) => m.insert(key, data),
HashTable::StringKeyed(m) => m.insert(StringKey(key), data),
}
}
pub unsafe fn remove(&mut self, key: *const u8) {
match self {
HashTable::PointerKeyed(m) => {
let key = key.cast_mut();
m.remove(&key);
}
HashTable::StringKeyed(m) => {
let key = StringKey(key.cast_mut());
m.remove(&key);
mem::forget(key);
}
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct StringKey(*const u8);
impl PartialEq for StringKey {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_null(), other.0.is_null()) {
(true, true) => true,
(false, false) => unsafe { CStr::from_ptr(self.0.cast()) == CStr::from_ptr(other.0.cast()) },
_ => false,
}
}
}
impl Eq for StringKey {}
impl Hash for StringKey {
fn hash<H: Hasher>(&self, h: &mut H) {
if self.0.is_null() {
h.write_usize(0)
} else {
unsafe { CStr::from_ptr(self.0.cast()).hash(h) }
}
}
}
pub enum Enumerator<'a> {
PointerKeyed(hash_map::IterMut<'a, *mut u8, *mut u8>),
StringKeyed(hash_map::IterMut<'a, StringKey, *mut u8>),
}
pub mod ffi {
use std::{
ffi::{c_int, c_uint, c_void},
mem, ptr,
};
use super::{Enumerator, HashTable, StringKey};
#[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::<u8>();
(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 result = match m.get_key_value_mut(&key.cast::<u8>().cast_mut()) {
Some((k, v)) => {
unsafe {
*key_dest = k.cast::<c_void>();
*value_dest = v.cast::<c_void>();
}
1
}
None => 0,
};
result
}
HashTable::StringKeyed(m) => {
let key = StringKey(key.cast::<u8>().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.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::<u8>(), data.cast::<u8>()) } {
Some(v) => v.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::<u8>());
}
}
/// 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.cast::<c_void>(),
None => ptr::null_mut(),
},
Enumerator::StringKeyed(i) => match i.next() {
Some((_, v)) => v.cast::<c_void>(),
None => ptr::null_mut(),
},
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMNextHashEnumeratorKey(e: *mut Enumerator<'static>) -> *const 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.cast::<c_void>(),
None => ptr::null_mut(),
},
Enumerator::StringKeyed(i) => match i.next() {
Some((k, _)) => k.0.cast::<c_void>(),
None => ptr::null_mut(),
},
}
}
}
+11
View File
@@ -0,0 +1,11 @@
pub mod array;
pub mod bag;
pub mod data;
pub mod defines;
pub mod find_file;
pub mod hash_table;
pub mod memory;
pub mod notification;
pub mod prop_list;
pub mod string;
pub mod tree;
+283
View File
@@ -0,0 +1,283 @@
//! 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, ffi::{c_char, CStr}, mem, ptr::{self, NonNull}};
/// Tracks the layout and reference count of an allocated chunk of memory.
#[derive(Clone, Copy)]
struct Header {
ptr: NonNull<u8>,
payload_size: usize,
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 Ok(header_layout) = alloc::Layout::from_size_align(mem::size_of::<Header>(), 8) else {
return ptr::null_mut();
};
let Ok(layout) = alloc::Layout::from_size_align(size, 8) else {
return ptr::null_mut();
};
let Ok((full_layout, result_offset)) = header_layout.extend(layout) else {
return ptr::null_mut();
};
let full_segment = unsafe { alloc::alloc_zeroed(full_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),
payload_size: size,
layout: full_layout,
refcount: 0,
});
}
result
}
/// Allocates a segment with [`alloc_bytes`] and fills it with the contents of
/// `s`. The resulting string should be free'd by passing it to [`free_bytes`].
pub fn alloc_string(s: &CStr) -> *mut c_char {
let len = s.count_bytes() + 1;
let result = alloc_bytes(len).cast::<c_char>();
unsafe { ptr::copy_nonoverlapping(s.as_ptr().cast::<c_char>(), result, len); }
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 `size` is 0. Data will be
/// initialized to 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. If `newsize` is larger than `ptr`'s segment,
/// data in the new space will be initialized but have an arbitrary value.
///
/// ## Safety
///
/// If `ptr` is non-null, callers must ensure that it came from from
/// [`wmalloc`] or [`wrealloc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wrealloc(ptr: *mut c_void, newsize: usize) -> *mut c_void {
if ptr.is_null() {
unsafe {
return wmalloc(newsize);
}
}
unsafe {
let result = wmalloc(newsize);
let result_header = ptr::read_unaligned(Header::for_alloc_bytes(result.cast()));
let ptr_header = ptr::read_unaligned(Header::for_alloc_bytes(ptr.cast()));
let copy_size = usize::min(
ptr_header.payload_size,
result_header.payload_size,
);
ptr::copy_nonoverlapping(ptr, result, copy_size);
wfree(ptr);
result
}
}
/// 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, alloc_string, ffi::{wfree, wmalloc, wrealloc}, free_bytes, Header};
use std::{ffi::CStr, mem, os::raw::c_void, ptr, slice};
#[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 multiple_allocs() {
unsafe {
let x = wmalloc(mem::size_of::<i64>());
*x.cast::<i64>() = 30;
let y = wmalloc(mem::size_of::<i32>());
*y.cast::<i32>() = 5;
let z = wmalloc(48);
*z.cast::<f32>() = 1.0;
wfree(x);
wfree(y);
wfree(z);
}
}
#[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>()); }
}
#[test]
fn realloc_retains_data() {
let x: *mut u8 = unsafe { wmalloc(10).cast() };
unsafe {
let xs = slice::from_raw_parts_mut(&mut *x, 10);
// We know that xs should be zeroed.
assert_eq!(xs, &[0u8; 10]);
for i in 0u8..10 {
xs[i as usize] = i;
}
assert_eq!(xs, (0..10).collect::<Vec::<u8>>());
}
}
#[test]
fn alloc_free_string() {
let s = alloc_string(c"hello");
assert!(!s.is_null());
assert_eq!(unsafe { CStr::from_ptr(s) }, c"hello");
unsafe { free_bytes(s.cast::<u8>()); }
}
}
+360
View File
@@ -0,0 +1,360 @@
use std::{
collections::{btree_map::Entry, BTreeMap},
ffi::{c_void, CStr},
ptr::{self, NonNull},
sync::Mutex,
};
// Helper function for adding the entry `(key, (observer, action))` to `map`.
fn register<K: Eq + Ord>(
map: &mut BTreeMap<K, Vec<(Option<Sendable>, Action)>>,
key: K,
observer: Option<Sendable>,
action: Action,
) {
match map.entry(key) {
Entry::Occupied(mut o) => {
o.get_mut().push((observer, action));
}
Entry::Vacant(v) => {
v.insert(vec![(observer, action)]);
}
}
}
/// Lightweight message from object to another. When a notification is sent,
/// registered [`Action`]s are called.
///
/// Use [`ffi::WMAddNotificationObserver`] or [`NotificationCenter::register`]
/// to request notifications.
///
/// ## Safety
///
/// `Notification` encapsulates two data pointers. The Rust implementation
/// explicitly supports notifications across threads. To uphold Rust's safety
/// rules, `Notification`s must only be created with pointers that can be sent
/// across threads. The [`Sendable`] struct is provided to guarantee this.
///
/// ## Rust rewrite notes
///
/// This was originally a reference-counted structure, but it is so lightweight
/// (consisting of three pointers) that the Rust version is `Copy`. This
/// simplifies things --- each Rust recipient of a `Notification` owns it, and
/// there is no need to coordinate how it is cleaned up.
///
/// In unported C code, a notificaton's `name` may be compared against a string
/// constant using pointer equality rather than string equality. This has
/// negative implications for sending notifications across the Rust/C
/// boundary. For the time being, notifications are generated and received by
/// code written in C, but care should be taken that notification names are
/// checked properly when porting notification registration in the future. (We
/// will ideally move to a different data type for identifying notifications,
/// too.)
#[derive(Clone, Copy)]
pub struct Notification {
name: &'static CStr,
/// The object that generated the notification. This may be `None` for
/// notifications that are about global state.
source: Option<Sendable>,
/// Optional side-channel data provided to notification listeners.
client_data: Option<Sendable>,
}
/// Callback that notifies `observer` (which may be null) of `notification` (which won't be).
pub type Action = unsafe extern "C" fn(observer: *mut c_void, notification: *const Notification);
/// Wraps a type-erased pointer (which it does not own) and marks it as `Send`.
///
/// The `Send`-ability of the wrapped pointer must be guaranteed by code that
/// instantiates a `Sendable`.
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct Sendable {
ptr: NonNull<c_void>,
}
impl Sendable {
/// Creates a `Sendable` wrapping `ptr`.
///
/// ## Safety
///
/// `ptr` must be safe to send across threads.
pub unsafe fn new(ptr: NonNull<c_void>) -> Self {
Sendable { ptr }
}
}
// Guaranteed by `Sendable::new`.
unsafe impl Send for Sendable {}
pub struct NotificationCenter {
/// Notification subscriptions that match on name and source.
exact: BTreeMap<(&'static CStr, Sendable), Vec<(Option<Sendable>, Action)>>,
/// Notification subscriptions that match on name.
by_name: BTreeMap<&'static CStr, Vec<(Option<Sendable>, Action)>>,
/// Notification subscriptions that match on source.
by_source: BTreeMap<Sendable, Vec<(Option<Sendable>, Action)>>,
/// Notification subscriptions that match all notifications.
universal: Vec<(Option<Sendable>, Action)>,
}
// It is safe to send NotificationCenter across threads as long as the contract
// on Sendable is respected.
unsafe impl Send for NotificationCenter {}
impl NotificationCenter {
/// Creates a new `NotificationCenter`.
pub const fn new() -> Self {
NotificationCenter {
exact: BTreeMap::new(),
by_name: BTreeMap::new(),
by_source: BTreeMap::new(),
universal: Vec::new(),
}
}
/// Provides access to the default, process-wide notification center. The
/// FFI C API uses this notification center. This is protected behind a
/// mutex that is held while `f` is run, so panicking inside of `f` should
/// be avoided.
pub fn with_global_default<R>(f: impl FnOnce(&mut Self) -> R) -> R {
static INSTANCE: Mutex<NotificationCenter> = Mutex::new(NotificationCenter::new());
f(&mut INSTANCE
.try_lock()
.unwrap())
}
/// Registers `action` to be invoked and invoked on `observer` when
/// notifications named `name` are fired from `source`.
pub fn register_exact(
&mut self,
name: &'static CStr,
source: Sendable,
observer: Option<Sendable>,
action: Action,
) {
register(&mut self.exact, (name, source), observer, action);
}
/// Registers `action` to be invoked on `observer` when notifications are
/// fired by `source` (regardless of the notification name).
pub fn register_by_source(
&mut self,
source: Sendable,
observer: Option<Sendable>,
action: Action,
) {
register(&mut self.by_source, source, observer, action);
}
/// Registers `action` to be invoked on `observer` for all notifications
/// named `name`.
pub fn register_by_name(
&mut self,
name: &'static CStr,
observer: Option<Sendable>,
action: Action,
) {
register(&mut self.by_name, name, observer, action);
}
/// Registers `action` to be invoked on `observer` for all notifications,
/// regardless of the notification's name or source.
pub fn register_universal(&mut self, observer: Option<Sendable>, action: Action) {
self.universal.push((observer, action));
}
/// Dispatches `notification` with registered actions.
pub fn dispatch(&mut self, notification: Notification) {
if let Some(observers) = self.by_name.get_mut(notification.name) {
for (observer, action) in observers {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
if let Some(source) = notification.source {
if let Some(observers) = self.exact.get_mut(&(notification.name, source)) {
for (observer, action) in observers {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
if let Some(observers) = self.by_source.get_mut(&source) {
for (observer, action) in observers {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
}
for (observer, action) in &mut self.universal {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
/// Removes all notification subscriptions that would notify `observer` if they fired.
pub fn remove_observer(&mut self, observer: Sendable) {
self.exact.retain(|_, values| {
values.retain(|(o, _)| *o != Some(observer));
!values.is_empty()
});
self.by_name.retain(|_, values| {
values.retain(|(o, _)| *o != Some(observer));
!values.is_empty()
});
self.by_source.retain(|_, values| {
values.retain(|(o, _)| *o != Some(observer));
!values.is_empty()
});
self.universal.retain(|(o, _)| *o != Some(observer));
}
/// Clears all registered notification listeners and resets `self` to its
/// default state.
pub fn clear(&mut self) {
*self = Self::new();
}
}
pub mod ffi {
use super::{Action, Notification, NotificationCenter, Sendable};
use std::{
ffi::{c_char, c_void, CStr},
ptr::{self, NonNull},
};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetNotificationClientData(
notification: *mut Notification,
) -> *mut c_void {
if notification.is_null() {
return ptr::null_mut();
}
unsafe {
(*notification)
.client_data
.map(|x| x.ptr.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetNotificationObject(
notification: *mut Notification,
) -> *mut c_void {
if notification.is_null() {
return ptr::null_mut();
}
unsafe {
(*notification)
.source
.map(|x| x.ptr.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetNotificationName(
notification: *mut Notification,
) -> *const c_char {
if notification.is_null() {
return ptr::null_mut();
}
unsafe { (*notification).name.as_ptr() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMAddNotificationObserver(
action: Option<Action>,
observer: *mut c_void,
name: *const c_char,
object: *mut c_void,
) {
let Some(action) = action else {
return;
};
let observer = NonNull::new(observer).map(|x| unsafe { Sendable::new(x) });
let source = NonNull::new(object);
NotificationCenter::with_global_default(|c| {
if name.is_null() {
match source {
Some(source) => {
c.register_by_source(unsafe { Sendable::new(source) }, observer, action);
}
None => c.register_universal(observer, action),
}
} else {
let name = unsafe { CStr::from_ptr(name) };
match source {
Some(source) => {
c.register_exact(name, unsafe { Sendable::new(source) }, observer, action);
}
None => c.register_by_name(name, observer, action),
}
}
});
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRemoveNotificationObserver(observer: *mut c_void) {
let Some(observer) = NonNull::new(observer) else {
return;
};
NotificationCenter::with_global_default(|c| {
c.remove_observer(unsafe { Sendable::new(observer) })
});
}
/// Posts a notification from `object` with the given `name` and `client_data`.
///
/// ## Safety
///
/// `name` must be a non-null string constant or some other pointer with a
/// static lifetime.
///
/// `object` and `client_data` must be safe to send across threads (per the
/// contract of [`Sendable`]).
///
/// ## Rust rewrite notes
///
/// This originally took a heap-allocated `*mut Notification`, but now the
/// constructed `Notification` parameters are passed directly.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMPostNotificationName(
name: *const c_char,
object: *mut c_void,
client_data: *mut c_void,
) {
if name.is_null() {
return;
}
let name = unsafe { CStr::from_ptr(name) };
let source = NonNull::new(object).map(|x| unsafe { Sendable::new(x) });
let client_data = NonNull::new(client_data).map(|x| unsafe { Sendable::new(x) });
NotificationCenter::with_global_default(|c| {
c.dispatch(Notification {
name,
source,
client_data,
})
});
}
/// Resets all notifications for the global notification center. Used by
/// `WApplication` teardown code. This is a private, WINGs-internal API.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn W_ClearNotificationCenter() {
NotificationCenter::with_global_default(|c| c.clear());
}
}
+863
View File
@@ -0,0 +1,863 @@
//! Property lists: shared, tree-structured data.
//!
//! ## Rust rewrite notes
//!
//! This implementation should be good enough to facilitate migrating from C to
//! Rust and trasitioning away from using property lists everywhere instead of
//! proper structs. `PropList`s are a really cool general-purpose tool for
//! attaching data to objects and persisting it to disk. But in Rust, it is
//! easier to use proper structs with typed fields and appropriate `#[derive]`
//! declarations to generate code for serialization and deserialization
//! (presumably using Serde).
//!
//! As code that uses `PropList`s is rewritte in Rust, we should work on
//! migrating away from use of `PropList`s. Objects whose fields that can be
//! statically typed should be represented as structs. They may still be
//! persisted by cramming them into `PropList`s and writing those to disk, but
//! it would be better still to implement Serde-based serialization to and from
//! the property list format.
//!
//! The `PropList` implementation itself can also be improved substantially. See
//! [`PropList`] for thoughts on this.
use atomic_write_file::unix::OpenOptionsExt;
use std::{
cell::RefCell,
collections::{hash_map, HashMap},
ffi::{CString, OsStr, OsString},
fmt, hash,
io::{self, BufWriter, Write},
path::Path,
process::Command,
ptr,
rc::Rc,
};
use crate::find_file;
pub mod parser;
pub mod writer;
/// Payload of a [`PropList`].
#[derive(Eq, PartialEq)]
pub enum Node {
/// Text data. This is UTF-8 encoded and null-safe.
///
/// ## Rust rewrite notes
///
/// It would be better for this to be a `String`, but the C interface
/// requires borrows of C-style strings.
String(CString),
/// Array of child `PropList`s.
Array(Vec<PropList>),
/// `PropList`-keyed table of child `PropList`s. Keys should only have
/// `Node::String` or `Node::Data` payloads, although there is almost no
/// enforcement of this.
Dictionary(HashMap<PropList, PropList>),
}
impl hash::Hash for Node {
fn hash<H: hash::Hasher>(&self, h: &mut H) {
match self {
Node::String(s) => s.hash(h),
Node::Array(a) => {
for p in a {
p.hash(h);
}
}
Node::Dictionary(d) => {
for (k, v) in d {
k.hash(h);
v.hash(h);
}
}
}
}
}
impl fmt::Debug for Node {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
writer::Display {
inline: writer::Inline::Soft,
clear_left: false,
indentation: 0,
increment: 2,
node: self,
}
)
}
}
fn merge_shallow(dest: PropList, source: PropList) {
if ptr::eq(dest.0.as_ref(), source.0.as_ref()) {
return;
}
let Node::Dictionary(ref mut dest_items) = *dest.0.borrow_mut() else {
return;
};
let Node::Dictionary(ref source) = *source.0.borrow() else {
return;
};
for (k, v) in source {
if ptr::eq(dest.0.as_ptr(), k.0.as_ptr()) {
// Don't borrow k if it is already borrowed as dest.
continue;
}
dest_items.insert(k.clone(), v.clone());
}
}
fn merge_deep(dest: PropList, source: PropList) {
if ptr::eq(dest.0.as_ptr(), source.0.as_ptr()) {
return;
}
let Node::Dictionary(dest_items) = &mut *dest.0.borrow_mut() else {
return;
};
let Node::Dictionary(source_items) = &*source.0.borrow() else {
return;
};
for (key, value) in source_items {
if key.0.try_borrow().is_err() || value.0.try_borrow().is_err() {
// Something has already borrowed key or value. This may happen if
// source contains pointers that are also in dest, or if dest is
// cyclic. This is bad, but we just bail out.
continue;
}
match dest_items.entry(key.clone()) {
hash_map::Entry::Vacant(v) => {
// Dest has nothing at key. Insert value from source.
v.insert(value.clone());
}
hash_map::Entry::Occupied(mut o) => {
let recur = match *o.get().0.borrow() {
Node::Dictionary(_) => true,
_ => false,
};
if recur {
// dest[key] is a dictionary. Recur on dest[key] and value from source.
merge_deep(o.get().clone(), value.clone());
} else {
// dest[key] is not a dictionary. Overwrite with value from source.
o.insert(value.clone());
}
}
}
}
}
fn subtract_shallow(dest: PropList, source: PropList) {
if ptr::eq(dest.0.as_ptr(), source.0.as_ptr()) {
if let Node::Dictionary(ref mut items) = *dest.0.borrow_mut() {
items.clear();
}
return;
}
let Node::Dictionary(ref mut dest_items) = *dest.0.borrow_mut() else {
return;
};
let Node::Dictionary(ref source_items) = *source.0.borrow() else {
return;
};
for (k, v) in source_items.iter() {
if ptr::eq(dest.0.as_ptr(), k.0.as_ptr()) {
continue;
}
if let hash_map::Entry::Occupied(o) = dest_items.entry(k.clone()) {
if o.get() == v {
o.remove();
}
}
}
}
fn subtract_deep(dest: PropList, source: PropList) {
if ptr::eq(dest.0.as_ptr(), source.0.as_ptr()) {
if let Node::Dictionary(ref mut items) = *dest.0.borrow_mut() {
items.clear();
}
return;
}
let Node::Dictionary(ref mut dest_items) = *dest.0.borrow_mut() else {
return;
};
let Node::Dictionary(ref source_items) = *source.0.borrow() else {
return;
};
for (k, v) in source_items.iter() {
if ptr::eq(dest.0.as_ptr(), k.0.as_ptr()) {
continue;
}
if let hash_map::Entry::Occupied(o) = dest_items.entry(k.clone()) {
if o.get() == v {
o.remove();
continue;
}
let recur = match (&*o.get().0.borrow(), &*v.0.borrow()) {
(Node::Dictionary(_), Node::Dictionary(_)) => true,
_ => false,
};
if recur {
subtract_deep(o.get().clone(), v.clone());
}
}
}
}
/// Data graph with convenient (de)serialization to/from the [property
/// list](https://en.wikipedia.org/wiki/Property_list) format.
///
/// ## Rust rewrite notes
///
/// The original WUtils `PropList` was a reference-counted pointer, so it
/// supported shallow copy and shared-memory semantics that we have continued to
/// try to support in the Rust implementation. As a result, `PropList` is a thin
/// wrapper around an `Rc<RefCell<Node>>`. There are several reasons why this is
/// probably unnecessary and something we should migrate away from:
///
/// * It allows for the creation of non-tree structures, which was probably
/// never intended. (A degenerate `PropList` could even have itself as a child.)
/// * It complicates recursive operations on `PropList`s (equality checks,
/// merging, or taking differences) because a given `PropList` may occur
/// multiple times when traversing two `PropList`s, but `Rc` only allows it to be
/// mutably borrowed once.
/// * Allowing subtrees to be shared between two different `PropList`s
/// may lead to spooky action at a distance and may not actually be taken
/// advantage of by any client code.
///
/// As client code is migrated into Rust, it would be great to move away from
/// this implementation to a simpler one. As discussed in the module-level
/// rewrite notes, we may even be able to do away with `PropList` itself
/// (perhaps in favor of using Serde to write to and from property list files on
/// disk).
#[derive(Clone)]
pub struct PropList(Rc<RefCell<Node>>);
impl PropList {
pub fn new(node: Node) -> Self {
PropList(Rc::new(RefCell::new(node)))
}
/// Reads `r` to the end and tries to parse it into a `PropList`.
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<PropList, String> {
let path = path.as_ref().to_path_buf();
let buf = std::fs::read_to_string(&path).map_err(|e| format!("{}", e))?;
parser::from_str(buf.as_str())
}
// Runs `command` and tries to parse a PropList from its standard output.
pub fn from_command<S: AsRef<OsStr>>(command: S) -> Result<PropList, String> {
let command: OsString = command.as_ref().to_os_string();
let output = Command::new("/bin/sh")
.arg("-c")
.arg(command.clone())
.output()
.map_err(|e| format!("{}", e))?;
let output = str::from_utf8(&output.stdout).map_err(|e| format!("{}", e))?;
parser::from_str(&output)
}
pub fn display_indented<'s>(&'s self) -> impl fmt::Display + 's {
writer::Display {
inline: writer::Inline::Soft,
clear_left: false,
indentation: 0,
increment: 2,
node: self.0.borrow(),
}
}
pub fn display_unindented<'s>(&'s self) -> impl fmt::Display + 's {
writer::Display {
inline: writer::Inline::Hard,
clear_left: false,
indentation: 0,
increment: 0,
node: self.0.borrow(),
}
}
}
impl Eq for PropList {}
impl PartialEq for PropList {
fn eq(&self, other: &Self) -> bool {
*self.0.borrow() == *other.0.borrow()
}
}
impl hash::Hash for PropList {
fn hash<H: hash::Hasher>(&self, h: &mut H) {
self.0.borrow().hash(h)
}
}
impl fmt::Debug for PropList {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
write!(out, "{:?}", self.0.borrow())?;
Ok(())
}
}
impl PropList {
pub fn deep_clone(&self) -> Self {
match &*self.0.borrow() {
Node::String(s) => PropList::new(Node::String(s.clone())),
Node::Array(items) => {
PropList::new(Node::Array(items.iter().map(|x| x.deep_clone()).collect()))
}
Node::Dictionary(items) => PropList::new(Node::Dictionary(
items
.iter()
.map(|(k, v)| (k.deep_clone(), v.deep_clone()))
.collect(),
)),
}
}
/// Atomically serialize this `PropList` to `path`, creating any necessary
/// parent directories.
///
/// `path` is written to atomically: either the serialized `PropList` will
/// be completely written to `path`, or the operation will fail and any
/// existing file at `path` will not be modified.
///
/// ## Rust rewrite notes
///
/// As originally noted in `proplist.c`, a Coverity security bug report
/// flagged the need to preserve the permissions on the file being written
/// to. This should be respected in the rewritten code under Unix-like
/// operataing systems.
pub fn write_to_file<P: AsRef<Path>>(&self, path: &P) -> io::Result<()> {
self.write_to_file_impl(path.as_ref())
}
fn write_to_file_impl(&self, path: &Path) -> io::Result<()> {
if let Some(parent) = path.parent() {
find_file::create_path_hierarchy(parent)?;
}
let file = atomic_write_file::AtomicWriteFile::options()
.preserve_mode(true)
.open(&path)?;
let mut out = BufWriter::new(file);
writeln!(&mut out, "{}", self.display_indented())?;
out.into_inner()?.commit()
}
}
pub mod ffi {
use crate::{find_file::path_from_cstr, memory};
use super::{
merge_deep, merge_shallow, parser, subtract_deep, subtract_shallow, Node, PropList,
};
use std::{
collections::HashMap, ffi::{c_char, c_int, c_uint, CStr, CString, OsString}, ptr, str::FromStr
};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreatePLString(s: *const c_char) -> *mut PropList {
if s.is_null() {
return ptr::null_mut();
}
let s = unsafe { CStr::from_ptr(s) };
Box::leak(Box::new(PropList::new(Node::String(s.into()))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreatePLArrayFromSlice(
elems: *mut PropList,
length: c_uint,
) -> *mut PropList {
if elems.is_null() {
return ptr::null_mut();
}
let elems = unsafe { &*ptr::slice_from_raw_parts(elems, length as usize) };
Box::leak(Box::new(PropList::new(Node::Array(
elems.iter().cloned().collect(),
))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateEmptyPLArray() -> *mut PropList {
Box::leak(Box::new(PropList::new(Node::Array(Vec::new()))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreatePLDictionary(
key: *mut PropList,
value: *mut PropList,
) -> *mut PropList {
if key.is_null() || value.is_null() {
return Box::leak(Box::new(PropList::new(Node::Dictionary(HashMap::new()))));
}
let key = unsafe { (*key).clone() };
let value = unsafe { (*value).clone() };
Box::leak(Box::new(PropList::new(Node::Dictionary(
[(key, value)].into(),
))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateEmptyPLDictionary() -> *mut PropList {
Box::leak(Box::new(PropList::new(Node::Dictionary(HashMap::new()))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRetainPropList(plist: *mut PropList) -> *mut PropList {
if plist.is_null() {
return ptr::null_mut();
}
unsafe { Box::leak(Box::new((*plist).clone())) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMReleasePropList(plist: *mut PropList) {
if plist.is_null() {
return;
}
let _ = unsafe { ptr::read(plist) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMInsertInPLArray(
plist: *mut PropList,
index: c_int,
item: *mut PropList,
) {
if plist.is_null() || index < 0 || item.is_null() {
return;
}
let plist = unsafe { &mut *plist };
if let Node::Array(ref mut items) = *plist.0.borrow_mut() {
let item = unsafe { (*item).clone() };
items.insert(index as usize, item);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMAddToPLArray(plist: *mut PropList, item: *mut PropList) {
if plist.is_null() || item.is_null() {
return;
}
let plist = unsafe { &mut *plist };
if let Node::Array(ref mut items) = *plist.0.borrow_mut() {
let item = unsafe { (*item).clone() };
items.push(item);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDeleteFromPLArray(plist: *mut PropList, index: c_int) {
if plist.is_null() || index < 0 {
return;
}
let plist = unsafe { &mut *plist };
if let Node::Array(ref mut items) = *plist.0.borrow_mut() {
items.remove(index as usize);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRemoveFromPLArray(plist: *mut PropList, item: *mut PropList) {
if plist.is_null() || item.is_null() {
return;
}
let plist = unsafe { &mut *plist };
let item = unsafe { &*item };
if let Node::Array(ref mut items) = *plist.0.borrow_mut() {
if let Some((i, _)) = items.iter().enumerate().find(|(_, x)| *x == item) {
items.remove(i);
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMPutInPLDictionary(
plist: *mut PropList,
key: *mut PropList,
value: *mut PropList,
) {
if plist.is_null() || key.is_null() || value.is_null() {
return;
}
let plist = unsafe { &mut *plist };
if let Node::Dictionary(ref mut items) = *plist.0.borrow_mut() {
let key = unsafe { (*key).clone() };
let value = unsafe { (*value).clone() };
items.insert(key, value);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRemoveFromPLDictionary(plist: *mut PropList, key: *mut PropList) {
if plist.is_null() || key.is_null() {
return;
}
let plist = unsafe { &mut *plist };
let key = unsafe { &*key };
if let Node::Dictionary(ref mut items) = *plist.0.borrow_mut() {
items.remove(key);
}
}
/// If `dest` and `source` are both dictionaries, overwrites entries in
/// `dest` with corresponding entries in `source`.
///
/// If `recursive` is non-zero, this is done recursively for values in
/// `dest` and `source` that are both dictionaries.
///
/// ## Rust rewrite notes
///
/// This operation is used a few times. It may be worth keeping around
/// longer-term, although it might be hard to express if we do transition
/// away from `PropList`s to statically typed struct trees.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMMergePLDictionaries(
dest: *mut PropList,
source: *mut PropList,
recursive: c_int,
) -> *mut PropList {
if dest.is_null() || ptr::eq(dest, source) || source.is_null() {
return dest;
}
let dest = unsafe { (*dest).clone() };
let source = unsafe { (*source).clone() };
if recursive == 0 {
merge_shallow(dest.clone(), source);
} else {
merge_deep(dest.clone(), source);
}
return Box::leak(Box::new(dest));
}
/// If `dest` and `source` are both dictionaries, removes from `dest` any
/// `(k, v)` pairs where `dest[k] == source[k]`.
///
/// If `recursive` is non-zero, this is done recursively over subtrees of
/// `dest` and `source` when both `dest` and `source` are dictionaries for
/// keys of `source` that are also keys of `dest`.
///
/// ## Rust rewrite notes
///
/// This operation is only used in one place. It may be better to implement
/// this behavior as a one-off closer to where it is used, or with a
/// different API differently (e.g., as a function of a more general
/// proplist diff).
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSubtractPLDictionaries(
dest: *mut PropList,
source: *mut PropList,
recursive: c_int,
) -> *mut PropList {
if dest.is_null() {
return ptr::null_mut();
}
if source.is_null() {
return dest;
}
let dest = unsafe { (*dest).clone() };
let source = unsafe { (*source).clone() };
if recursive == 0 {
subtract_shallow(dest.clone(), source);
} else {
subtract_deep(dest.clone(), source);
}
Box::leak(Box::new(dest))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetPropListItemCount(plist: *mut PropList) -> c_int {
if plist.is_null() {
return 0;
}
let plist = unsafe { &*plist };
match &*plist.0.borrow() {
Node::Array(xs) => xs.len() as c_int,
Node::Dictionary(xs) => xs.len() as c_int,
_ => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMIsPLString(plist: *mut PropList) -> c_int {
if plist.is_null() {
return 0;
}
let plist = unsafe { &*plist };
match &*plist.0.borrow() {
Node::String(_) => 1,
_ => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMIsPLArray(plist: *mut PropList) -> c_int {
if plist.is_null() {
return 0;
}
let plist = unsafe { &*plist };
match &*plist.0.borrow() {
Node::Array(_) => 1,
_ => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMIsPLDictionary(plist: *mut PropList) -> c_int {
if plist.is_null() {
return 0;
}
let plist = unsafe { &*plist };
match &*plist.0.borrow() {
Node::Dictionary(_) => 1,
_ => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMIsPropListEqualTo(a: *mut PropList, b: *mut PropList) -> c_int {
if ptr::eq(a, b) {
return 1;
}
if a.is_null() {
return 0;
}
let a = unsafe { &*a };
let b = unsafe { &*b };
if a == b {
1
} else {
0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromPLString(plist: *mut PropList) -> *const c_char {
if plist.is_null() {
return ptr::null_mut();
}
let plist = unsafe { &*plist };
if let Node::String(ref s) = *plist.0.borrow() {
s.as_ref().as_ptr().cast::<c_char>()
} else {
ptr::null()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromPLArray(plist: *mut PropList, index: c_int) -> *mut PropList {
if plist.is_null() || index < 0 {
return ptr::null_mut();
}
let plist = unsafe { &*plist };
if let Node::Array(ref items) = *plist.0.borrow() {
if let Some(x) = items.get(index as usize) {
return Box::leak(Box::new(x.clone()));
}
}
ptr::null_mut()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromPLDictionary(plist: *mut PropList, key: *mut PropList) -> *mut PropList {
if plist.is_null() || key.is_null() {
return ptr::null_mut();
}
let plist = unsafe { &*plist };
let key = unsafe { &*key };
if let Node::Dictionary(ref items) = *plist.0.borrow() {
if let Some(item) = items.get(key) {
return Box::leak(Box::new(item.clone()));
}
}
ptr::null_mut()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetPLDictionaryKeys(plist: *mut PropList) -> *mut PropList {
if plist.is_null() {
return ptr::null_mut();
}
let plist = unsafe { &*plist };
if let Node::Dictionary(ref items) = *plist.0.borrow() {
return Box::leak(Box::new(PropList::new(Node::Array(items.keys().cloned().collect()))));
}
ptr::null_mut()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDeepCopyPropList(plist: *mut PropList) -> *mut PropList {
if plist.is_null() {
return ptr::null_mut();
}
let plist = unsafe { &*plist };
Box::leak(Box::new(plist.deep_clone()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreatePropListFromDescription(desc: *const c_char) -> *mut PropList {
if desc.is_null() {
return ptr::null_mut();
}
let desc = unsafe { CStr::from_ptr(desc) };
let Ok(desc) = desc.to_str() else {
return ptr::null_mut();
};
match parser::from_str(desc) {
Ok(plist) => Box::leak(Box::new(plist)),
Err(_) => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetPropListDescription(
plist: *mut PropList,
indented: c_int,
) -> *mut c_char {
use std::io::Write;
if plist.is_null() {
return ptr::null_mut();
}
let plist = unsafe { &*plist };
let mut buf = Vec::new();
if indented != 0 {
if let Err(_) = write!(&mut buf, "{}", plist.display_indented()) {
return ptr::null_mut();
}
} else {
if let Err(_) = write!(&mut buf, "{}", plist.display_unindented()) {
return ptr::null_mut();
}
}
match CString::new(buf) {
Ok(s) => memory::alloc_string(s.as_c_str()),
Err(_) => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMReadPropListFromFile(path: *const c_char) -> *mut PropList {
if path.is_null() {
return ptr::null_mut();
}
let path = unsafe { CStr::from_ptr(path) };
let Ok(path) = path.to_str() else {
return ptr::null_mut();
};
match PropList::from_file(path) {
Ok(plist) => Box::leak(Box::new(plist)),
Err(_) => {
// TODO: print error message.
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMReadPropListFromPipe(command: *const c_char) -> *mut PropList {
if command.is_null() {
return ptr::null_mut();
}
let command = unsafe { CStr::from_ptr(command) };
let Ok(command) = command.to_str() else {
return ptr::null_mut();
};
let command = OsString::from_str(command).unwrap();
let Ok(output) = std::process::Command::new("/bin/sh")
.arg("-c")
.arg(command)
.output()
else {
// TODO: print error message.
return ptr::null_mut();
};
let Ok(output) = String::from_utf8(output.stdout) else {
// TODO: print error message.
return ptr::null_mut();
};
match parser::from_str(&output) {
Ok(plist) => Box::leak(Box::new(plist)),
Err(_) => {
// TODO: print error message.
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMWritePropListToFile(
plist: *mut PropList,
path: *const c_char,
) -> c_int {
if plist.is_null() || path.is_null() {
return 0;
}
let plist = unsafe {
&*plist
};
let path = unsafe {
CStr::from_ptr(path)
};
let Some(path) = path_from_cstr(path) else {
// TODO: complain.
return 0;
};
match plist.write_to_file(&path) {
Ok(_) => return 1,
Err(_) => {
// TODO: complain.
return 0;
}
}
}
}
#[cfg(test)]
mod test {
use std::ffi::CString;
use crate::memory;
use super::{Node, PropList, ffi};
#[test]
fn free_proplist_description() {
let mut plist = PropList::new(Node::Array(vec![PropList::new(Node::String(CString::from(c"hello"))),
PropList::new(Node::String(CString::from(c"world!")))]));
let desc = unsafe { ffi::WMGetPropListDescription(&mut plist, 1) };
assert!(!desc.is_null());
unsafe { memory::ffi::wfree(desc.cast()); }
}
#[test]
fn oob_array_access_returns_null() {
// This is the original WMArray behavior. I don't like it, but a bunch
// of existing code relies on it.
let mut list = PropList::new(Node::Array(vec![PropList::new(Node::String(CString::from(c"hello"))),
PropList::new(Node::String(CString::from(c"world!")))]));
assert!(unsafe { ffi::WMGetFromPLArray(&mut list, 3) }.is_null());
}
}
File diff suppressed because it is too large Load Diff
+667
View File
@@ -0,0 +1,667 @@
//! Rendering of [`super::PropList`]s to text.
//!
//! To use this module, call [`super::PropList::display_indented`] or
//! [`super::PropList::display_unindented`].
//!
//! ## Rust rewrite notes
//!
//! This should work well enough for writing Window Maker state to disk, but it
//! is not a perfect reimplementation of the behavior of classic WUtils PropList
//! serialization. In particular, the WUtils hashtable has a different key
//! iteration order than Rust's `HashMap`, so this library may write dictionary
//! entries to disk in a different order.
use super::{parser, Node};
use std::{
fmt::{self, Write},
ops::Deref,
};
/// Maximum width for a chunk of text being rendered with `Inline::Soft` before
/// falling back to `Inline::No`.
const SOFT_LINEBREAK_WIDTH: usize = 77;
/// Describes the line-breaking strategy for rendering a `Node`.
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) enum Inline {
/// Do not attempt to render with no linebreaks.
No,
/// Attempt to render with no linebreaks, but fall back to `Inline::No` if
/// the result is too wide.
Soft,
/// Render with no linebreaks, regardless of output width.
Hard,
}
/// Bundles a reference to a `Node` with rendering instructions.
pub(crate) struct Display<N: Deref<Target = Node>> {
/// Line-breaking strategy.
pub(crate) inline: Inline,
/// Whether to render `node` as if it is the first item on a new line.
pub(crate) clear_left: bool,
/// Indentation level to render `node` at.
pub(crate) indentation: u32,
/// Amount by which `indentation` should increase when rendering children of
/// `node`.
pub(crate) increment: u32,
/// The `Node` to render.
pub(crate) node: N,
}
impl<N: Deref<Target = Node>> Display<N> {
/// Writes whitespace to `f` for the current level of indentation.
fn write_indent(&self, f: &mut fmt::Formatter) -> fmt::Result {
for _ in 0..self.indentation {
f.write_char(' ')?;
}
Ok(())
}
}
/// Writes `s` to `f`, backslash-escaping special characters as appropriate for
/// a quoted property list string.
fn write_escaped_string(s: &str, f: &mut fmt::Formatter) -> fmt::Result {
for c in s.chars() {
match c {
'\x07' => f.write_str(r#"\a"#)?,
'\x08' => f.write_str(r#"\b"#)?,
'\t' => f.write_str(r#"\t"#)?,
'\n' => f.write_str(r#"\n"#)?,
'\x0B' => f.write_str(r#"\v"#)?,
'\x0D' => f.write_str(r#"\f"#)?,
'\\' => f.write_str(r#"\\"#)?,
'"' => f.write_str(r#"\\""#)?,
c => f.write_char(c)?,
}
}
Ok(())
}
impl<N: Deref<Target = Node>> fmt::Display for Display<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.clear_left {
writeln!(f, "")?;
self.write_indent(f)?;
}
match &*self.node {
Node::Array(items) if self.inline != Inline::No => {
// Try to fit everything in one line.
let mut buf = String::new();
buf.push('(');
let mut items = items.iter();
if let Some(first) = items.next() {
write!(
&mut buf,
"{}",
Display {
inline: self.inline,
clear_left: false,
indentation: self.indentation,
increment: self.increment,
node: first.0.borrow(),
}
)?;
}
for next in items {
write!(
&mut buf,
", {}",
Display {
inline: self.inline,
clear_left: false,
indentation: self.indentation,
increment: self.increment,
node: next.0.borrow(),
}
)?;
}
buf.push(')');
if self.inline == Inline::Soft && buf.chars().count() > SOFT_LINEBREAK_WIDTH {
write!(
f,
"{}",
Display {
inline: Inline::No,
clear_left: false,
indentation: self.indentation,
increment: self.increment,
node: &*self.node,
}
)?;
} else {
f.write_str(&buf)?;
}
}
Node::Array(items) if items.is_empty() => f.write_str("()")?,
Node::Array(items) => {
f.write_char('(')?;
let mut items = items.iter();
if let Some(first) = items.next() {
write!(
f,
"{}",
Display {
inline: Inline::Soft,
clear_left: true,
indentation: self.indentation + self.increment,
increment: self.increment,
node: first.0.borrow(),
}
)?;
}
for next in items {
f.write_char(',')?;
write!(
f,
"{}",
Display {
inline: Inline::Soft,
clear_left: true,
indentation: self.indentation + self.increment,
increment: self.increment,
node: next.0.borrow(),
}
)?;
}
writeln!(f, "")?;
self.write_indent(f)?;
f.write_char(')')?;
}
Node::Dictionary(items) if items.is_empty() => write!(f, "{{}}")?,
Node::Dictionary(items) if self.inline != Inline::No => {
// Try to fit everything in one line.
let mut buf = String::new();
buf.push('{');
for (k, v) in items {
write!(
&mut buf,
" {} = {};",
Display {
inline: self.inline,
clear_left: false,
indentation: self.indentation,
increment: self.increment,
node: k.0.borrow(),
},
Display {
inline: self.inline,
clear_left: false,
indentation: self.indentation,
increment: self.increment,
node: v.0.borrow(),
}
)?;
}
write!(&mut buf, " }}")?;
if self.inline == Inline::Soft && buf.chars().count() > SOFT_LINEBREAK_WIDTH {
write!(
f,
"{}",
Display {
inline: Inline::No,
clear_left: false,
indentation: self.indentation,
increment: self.increment,
node: &*self.node,
}
)?;
} else {
f.write_str(&buf)?;
}
}
Node::Dictionary(items) => {
write!(f, "{{")?;
for (k, v) in items {
write!(
f,
"{} = {};",
Display {
inline: Inline::Soft,
clear_left: true,
indentation: self.indentation + self.increment,
increment: self.increment,
node: k.0.borrow(),
},
Display {
inline: Inline::Soft,
clear_left: false,
indentation: self.indentation + self.increment,
increment: self.increment,
node: v.0.borrow(),
}
)?;
}
writeln!(f, "")?;
self.write_indent(f)?;
f.write_char('}')?;
}
Node::String(s) => {
let mut unquoted = !s.is_empty();
for c in s.as_bytes() {
if !parser::is_unquoted_char(*c as char) {
unquoted = false;
break;
}
}
if unquoted {
write!(f, "{}", s.to_string_lossy())?;
} else {
f.write_char('"')?;
write_escaped_string(&s.to_string_lossy(), f)?;
f.write_char('"')?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::prop_list::{Node, PropList};
use std::ffi::CString;
fn pl_array(xs: Vec<PropList>) -> PropList {
PropList::new(Node::Array(xs))
}
fn pl_string(s: &str) -> PropList {
PropList::new(Node::String(CString::new(s.as_bytes()).unwrap()))
}
#[test]
fn write_empty_string() {
let serialized = format!("{:?}", pl_string(""));
assert_eq!(serialized, r#""""#);
}
#[test]
fn write_unquoted_string() {
let serialized = format!("{:?}", pl_string("hello"));
assert_eq!(serialized, r#"hello"#);
}
#[test]
fn write_quoted_string() {
let serialized = format!("{:?}", pl_string("hello, world!"));
assert_eq!(serialized, r#""hello, world!""#);
}
#[test]
fn write_flat_array() {
let serialized = format!(
"{:?}",
pl_array(vec![pl_string("hello"), pl_string("world")])
);
assert_eq!(serialized, r#"(hello, world)"#);
}
#[test]
fn write_empty_array() {
let serialized = format!("{:?}", pl_array(vec![]));
assert_eq!(serialized, r#"()"#);
}
#[test]
fn write_nested_array() {
let serialized = format!(
"{:?}",
pl_array(vec![
pl_array(vec![pl_string("hello"), pl_string("world")]),
pl_string("baz"),
pl_string("quux plugh")
])
);
assert_eq!(serialized, r#"((hello, world), baz, "quux plugh")"#);
}
#[test]
fn write_long_nested_array() {
let serialized = format!(
"{:?}",
pl_array(vec![
pl_array(vec![pl_string("hello"), pl_string("world")]),
pl_string("baz"),
pl_string("quux plugh"),
pl_string("etc"),
pl_array(vec![
pl_string("lots"),
pl_string("of"),
pl_string("words"),
pl_string("go"),
pl_array(vec![
pl_string("here"),
pl_string("and"),
pl_string("stuff"),
pl_string("blah"),
pl_string("blah"),
pl_string("blah"),
])
])
])
);
assert_eq!(
serialized,
r#"(
(hello, world),
baz,
"quux plugh",
etc,
(lots, of, words, go, (here, and, stuff, blah, blah, blah))
)"#
);
}
#[test]
fn roundtrip_wm_state() {
let original = r#"{
Dock = {
AutoRaiseLower = No;
Applications = (
{
Forced = No;
Name = Logo.WMDock;
BuggyApplication = No;
AutoLaunch = No;
Position = "0,0";
Lock = Yes;
Command = "/usr/bin/WPrefs";
},
{
Forced = No;
Name = wmweather.wmweather;
DropCommand = "wmweather -s KBOS %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,4";
Lock = Yes;
PasteCommand = "wmweather -s KBOS %s";
Command = "wmweather -s KBOS";
},
{
Forced = No;
Name = wmclock.WMClock;
DropCommand = "wmclock %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,3";
Lock = Yes;
PasteCommand = "wmclock %s";
Command = wmclock;
},
{
Forced = No;
Name = emacs.Emacs;
DropCommand = "emacsclient -c %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,2";
Lock = Yes;
PasteCommand = "emacsclient -c %s";
Command = "emacsclient -";
},
{
Forced = No;
Name = wmfire.wmfire;
DropCommand = "wmfire -m %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,5";
Lock = Yes;
PasteCommand = "wmfire -m %s";
Command = "wmfire -m";
},
{
Forced = No;
Name = wmnet.WMNET;
DropCommand = "wmnet -W enp0s31f6 %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,7";
Lock = Yes;
PasteCommand = "wmnet -W enp0s31f6 %s";
Command = "wmnet -W enp0s31f6";
},
{
Forced = No;
Name = wmtemp.DockApp;
DropCommand = "wmtemp -f %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,6";
Lock = Yes;
PasteCommand = "wmtemp -f %s";
Command = "wmtemp -f";
},
{
Forced = No;
Name = firefox.Firefox;
DropCommand = "firefox %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,1";
Lock = Yes;
PasteCommand = "firefox %s";
Command = firefox;
},
{
Forced = No;
Name = "org\\.gnome\\.Weather.org\\.gnome\\.Weather";
DropCommand = "org.gnome.Weather %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,8";
Lock = Yes;
PasteCommand = "org.gnome.Weather %s";
Command = "/usr/bin/gnome-weather";
},
{
Forced = No;
Name = Zotero.Zotero;
DropCommand = "/home/stu/bin/zotero %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,9";
Lock = Yes;
PasteCommand = "/home/stu/bin/zotero %s";
Command = "/home/stu/bin/zotero";
}
);
Lowered = Yes;
Position = "2496,0";
Applications1440 = (
{
Forced = No;
Name = Logo.WMDock;
BuggyApplication = No;
AutoLaunch = No;
Position = "0,0";
Lock = Yes;
Command = "/usr/bin/WPrefs";
},
{
Forced = No;
Name = wmweather.wmweather;
DropCommand = "wmweather -s KBOS %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,4";
Lock = Yes;
PasteCommand = "wmweather -s KBOS %s";
Command = "wmweather -s KBOS";
},
{
Forced = No;
Name = wmclock.WMClock;
DropCommand = "wmclock %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,3";
Lock = Yes;
PasteCommand = "wmclock %s";
Command = wmclock;
},
{
Forced = No;
Name = emacs.Emacs;
DropCommand = "emacsclient -c %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,2";
Lock = Yes;
PasteCommand = "emacsclient -c %s";
Command = "emacsclient -";
},
{
Forced = No;
Name = wmfire.wmfire;
DropCommand = "wmfire -m %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,5";
Lock = Yes;
PasteCommand = "wmfire -m %s";
Command = "wmfire -m";
},
{
Forced = No;
Name = wmnet.WMNET;
DropCommand = "wmnet -W enp0s31f6 %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,7";
Lock = Yes;
PasteCommand = "wmnet -W enp0s31f6 %s";
Command = "wmnet -W enp0s31f6";
},
{
Forced = No;
Name = wmtemp.DockApp;
DropCommand = "wmtemp -f %d";
BuggyApplication = No;
AutoLaunch = Yes;
Position = "0,6";
Lock = Yes;
PasteCommand = "wmtemp -f %s";
Command = "wmtemp -f";
},
{
Forced = No;
Name = firefox.Firefox;
DropCommand = "firefox %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,1";
Lock = Yes;
PasteCommand = "firefox %s";
Command = firefox;
},
{
Forced = No;
Name = "org\\.gnome\\.Weather.org\\.gnome\\.Weather";
DropCommand = "org.gnome.Weather %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,8";
Lock = Yes;
PasteCommand = "org.gnome.Weather %s";
Command = "/usr/bin/gnome-weather";
},
{
Forced = No;
Name = Zotero.Zotero;
DropCommand = "/home/stu/bin/zotero %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "0,9";
Lock = Yes;
PasteCommand = "/home/stu/bin/zotero %s";
Command = "/home/stu/bin/zotero";
}
);
};
Clip = {
Forced = No;
Name = Logo.WMClip;
DropCommand = "wmsetbg -u -t %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "2496,1376";
Lock = No;
Command = "-";
};
Drawers = ();
Workspaces = (
{
Clip = {
AutoRaiseLower = No;
AutoCollapse = No;
Applications = (
{
Forced = No;
Name = xsnow.Xsnow;
DropCommand = "xsnow %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "-2,0";
Lock = No;
PasteCommand = "xsnow %s";
Command = xsnow;
Omnipresent = No;
},
{
Forced = No;
Name = "thunderbird.Thunderbird-default";
DropCommand = "thunderbird %d";
BuggyApplication = No;
AutoLaunch = No;
Position = "-1,0";
Lock = No;
PasteCommand = "thunderbird %s";
Command = thunderbird;
Omnipresent = No;
}
);
Collapsed = No;
AutoAttractIcons = No;
Lowered = Yes;
};
Name = Mail;
},
{
Clip = {
AutoRaiseLower = No;
AutoCollapse = No;
Applications = ();
Collapsed = No;
AutoAttractIcons = No;
Lowered = Yes;
};
Name = Plans;
},
{
Clip = {
AutoRaiseLower = No;
AutoCollapse = No;
Applications = ();
Collapsed = No;
AutoAttractIcons = No;
Lowered = Yes;
};
Name = Misc;
},
);
}"#;
// We do (original serialized) -> plist -> (serialized) -> plist and
// compare the two plists because Rust's hashtable ordering (which is
// reflected in the serialized output) does not match the original
// WUtils hashtable ordering.
let original_plist = super::parser::from_str(original).unwrap();
let serialized = format!("{}", original_plist.display_indented());
let deserialized = super::parser::from_str(&serialized).unwrap();
assert_eq!(original_plist, deserialized);
}
}
+439
View File
@@ -0,0 +1,439 @@
//! String manipulation utilities.
//!
//! ## Rust rewrite notes
//!
//! These are more or less bug-for-bug reimplementations of the original WUtils
//! functions. Avoid using these functions in new code.
use std::{
ffi::{c_char, c_int, CStr},
iter, mem, ptr, slice,
};
use crate::memory::{alloc_bytes, alloc_string, ffi::wrealloc, free_bytes};
/// Returns a `wmalloc`-managed C-style string that duplicates `s`, which cannot
/// be null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrdup(s: *const c_char) -> *mut c_char {
assert!(!s.is_null());
alloc_string(unsafe { CStr::from_ptr(s) })
}
/// Returns a `wmalloc`-managed C-style string of the first `len` bytes of `s`,
/// which cannot be null.
///
/// If `len` exceeds the length of `s`, uses the lesser value.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrndup(s: *const c_char, len: usize) -> *mut c_char {
assert!(!s.is_null());
let len = unsafe {
slice::from_raw_parts(s, len)
.into_iter()
.position(|p| *p == 0)
.unwrap_or(len)
};
let copy: *mut c_char = alloc_bytes(len + 1).cast(); // Implicitly zeroed.
unsafe {
ptr::copy_nonoverlapping(s, copy, len);
}
copy.cast::<c_char>()
}
/// Concatenates `s1` and `s2` into a `wmalloc`-managed C-style string.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrconcat(s1: *const c_char, s2: *const c_char) -> *mut c_char {
match (s1.is_null(), s2.is_null()) {
(true, true) => ptr::null_mut(),
(true, false) => unsafe { wstrdup(s1) },
(false, true) => unsafe { wstrdup(s2) },
(false, false) => unsafe {
let s1 = CStr::from_ptr(s1);
let l1 = s1.count_bytes();
let s2 = CStr::from_ptr(s2);
let l2 = s2.count_bytes();
let s: *mut c_char = alloc_bytes(l1 + l2 + 1).cast(); // Implicitly zeroed.
ptr::copy_nonoverlapping(s1.as_ptr(), s, l1);
ptr::copy_nonoverlapping(s2.as_ptr(), s.offset(l1 as isize), l2);
s
},
}
}
/// Appends `src` to `dest`, destructively reallocating `dest` and returning a
/// new `wmalloc`-managed pointer where the result is stored. `dst` must come
/// from `wmalloc` or `wrealloc`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrappend(dst: *mut c_char, src: *const c_char) -> *mut c_char {
if src.is_null() {
return dst;
}
let src = unsafe { CStr::from_ptr(src) };
let src_len = src.count_bytes();
if src_len == 0 {
return dst;
}
if dst.is_null() {
return unsafe { wstrdup(src.as_ptr()) };
}
let dst_len = unsafe { CStr::from_ptr(dst).count_bytes() + 1 };
let len = dst_len + src_len + 1;
let result: *mut c_char = unsafe { wrealloc(dst.cast(), len).cast() };
unsafe {
ptr::copy_nonoverlapping(
src.as_ptr(),
result.offset(dst_len.try_into().unwrap()),
src_len,
);
}
result
}
/// Strips leading and trailing whitespace from `s`, returning a
/// `wmalloc`-managed C-style string holding the result.
///
/// ## Rust rewite notes
///
/// This uses a slightly different notion of "space character" than the original
/// C implementation did. `s` must be valid UTF-8.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtrimspace(s: *const c_char) -> *mut c_char {
if s.is_null() {
return ptr::null_mut();
}
let Ok(s) = (unsafe { CStr::from_ptr(s).to_str() }) else {
// TODO: complain.
return ptr::null_mut();
};
let trimmed = s.trim();
let ptr = trimmed.as_ptr();
let len = trimmed.len();
unsafe { wstrndup(ptr, len) }
}
/// Splits `command` into tokens with approximately the same rules as used in
/// the shell for splitting up a command into program arguments.
fn tokensplit(command: &[u8]) -> Vec<Vec<u8>> {
enum Mode {
Start,
Unquoted(Vec<u8>),
Escape(Context),
Quoted { token: Vec<u8>, delimiter: u8, },
}
enum Context {
Unquoted(Vec<u8>),
Quoted { token: Vec<u8>, delimiter: u8, },
}
let mut out = Vec::new();
let mut mode = Mode::Start;
for &b in command {
mode = match (mode, b) {
(Mode::Start, b'\\') => Mode::Escape(Context::Unquoted(vec![])),
(Mode::Start, b'\'' | b'"') => Mode::Quoted { token: vec![], delimiter: b },
(Mode::Start, b' ' | b'\t') => Mode::Start,
(Mode::Start, _) => Mode::Unquoted(vec![b]),
(Mode::Unquoted(token), b'\\') => Mode::Escape(Context::Unquoted(token)),
(Mode::Unquoted(token), b'\'' | b'"') => Mode::Quoted { token, delimiter: b },
(Mode::Unquoted(token), b' ' | b'\t') => {
out.push(token);
Mode::Start
}
(Mode::Unquoted(mut token), _) => {
token.push(b);
Mode::Unquoted(token)
}
(Mode::Escape(Context::Unquoted(mut token)), _) => {
token.push(b);
Mode::Unquoted(token)
}
(Mode::Escape(Context::Quoted { mut token, delimiter }), _) => {
token.push(b);
Mode::Quoted { token, delimiter }
}
(Mode::Quoted { token, delimiter }, _) if b == delimiter => {
Mode::Unquoted(token)
}
(Mode::Quoted { token, delimiter }, _) if b == b'\\' => {
Mode::Escape(Context::Quoted { token, delimiter })
}
(Mode::Quoted { mut token, delimiter }, _) => {
token.push(b);
Mode::Quoted { token, delimiter }
}
}
}
match mode {
Mode::Start => (),
Mode::Unquoted(token) => out.push(token),
Mode::Quoted { token, .. } => out.push(token),
Mode::Escape(Context::Unquoted(token)) => out.push(token),
Mode::Escape(Context::Quoted { token, .. }) => out.push(token),
}
out
}
/// Splits `command` into tokens, storing the number of tokens in `argc` and
/// `wmalloc`-managed C-style strings for each token in `argv`. Call
/// [`wtokenfree`] to free `argv`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtokensplit(
command: *const c_char,
argv: *mut *mut *mut c_char,
argc: *mut c_int,
) {
if argv.is_null() || argc.is_null() {
return;
}
unsafe {
*argv = ptr::null_mut();
*argc = 0;
}
if command.is_null() {
return;
}
let command = unsafe { CStr::from_ptr(command) };
let Ok(command) = command.to_str() else {
return;
};
let tokens = tokensplit(command.as_bytes());
if tokens.is_empty() {
return;
}
let argv = unsafe {
*argv = alloc_bytes(mem::size_of::<*mut c_char>() * tokens.len()).cast::<*mut c_char>();
*argc = tokens.len() as c_int;
slice::from_raw_parts_mut(*argv, tokens.len())
};
for (dest, mut token) in argv.iter_mut().zip(tokens.into_iter()) {
token.push(b'\0');
*dest = alloc_string(unsafe { CStr::from_bytes_with_nul_unchecked(&token) });
}
}
/// Frees an `argv` populated by [`wtokensplit`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtokenfree(tokens: *mut *mut c_char, count: c_int) {
if tokens.is_null() {
return;
}
if count > 0 {
let tokens = unsafe { slice::from_raw_parts_mut(tokens, count as usize) };
for token in tokens {
unsafe {
free_bytes(*token);
}
}
}
unsafe {
free_bytes(tokens.cast::<u8>());
}
}
/// Joins the tokens of the `count` elements of `list` into a single string,
/// enclosing them in double quotes (`"`) if necessary. Returns a
/// `wmalloc`-managed C-style string with the result, or null on failure.
///
/// ## Rust rewrite notes
///
/// `list` must consist of valid UTF-8 strings. This reimplementation does not
/// attempt to improve on the original, so it has bad failure modes (like not
/// escaping quotes in the input). Do not use this function in new code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtokenjoin(list: *const *const char, count: c_int) -> *mut c_char {
if list.is_null() || count <= 0 {
return alloc_string(c"");
}
let list = unsafe {
slice::from_raw_parts(list.cast::<*const u8>(), count as usize)
};
let mut buffer = Vec::new();
for term in list {
if term.is_null() {
continue;
}
let term = unsafe { CStr::from_ptr(*term) };
if term.is_empty() {
continue;
}
if !buffer.is_empty() {
buffer.push(b' ');
}
let term = term.to_bytes();
if term.iter().find(|&&x| x == b' ' || x == b'\t').is_some() {
buffer.push(b'"');
buffer.extend_from_slice(term);
buffer.push(b'"');
} else {
buffer.extend_from_slice(term);
}
}
buffer.push(b'\0');
if let Ok(buffer) = CStr::from_bytes_until_nul(&buffer) {
alloc_string(buffer)
} else {
return ptr::null_mut();
}
}
#[cfg(test)]
mod test {
use super::{wtokenfree, wtokensplit, wtokenjoin};
use std::{ffi::{c_char, c_int, CStr}, ptr, slice};
#[test]
fn split_empty_whitespace() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c" ".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c" \t ".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c" \t ".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c"\t\t".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
}
fn args_of(argv: *mut *mut u8, argc: usize) -> Vec<String> {
let mut v = Vec::with_capacity(argc);
for s in unsafe { slice::from_raw_parts(argv, argc) } {
if s.is_null() {
return v;
}
v.push(String::from(unsafe { CStr::from_ptr(*s) }.to_str().unwrap()));
}
v
}
#[test]
fn split_empty_quoted() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"\"\"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
unsafe { wtokenfree(argv, argc); }
argv = ptr::null_mut();
argc = 0;
unsafe { wtokensplit(c"''".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
unsafe { wtokenfree(argv, argc); }
argv = ptr::null_mut();
argc = 0;
unsafe { wtokensplit(c" ''\t".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
unsafe { wtokenfree(argv, argc); }
}
#[test]
fn split_one_unquoted() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"hello".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello")]);
unsafe { wtokensplit(c"hello\\\\".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello\\")]);
}
#[test]
fn split_one_quoted() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"\"hello\"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello")]);
unsafe { wtokensplit(c"\"hello world\"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello world")]);
}
#[test]
fn split_multi() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe {
wtokensplit(
c"\"hello world\" what\\'s happening' here it\\'s weird'".as_ptr(),
&mut argv,
&mut argc,
);
}
assert_eq!(argc, 3);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello world"),
String::from("what's"),
String::from("happening here it's weird")]);
}
/// Calls `f(wtokenjoin(list))` transparently.
fn with_list<R>(list: &[&CStr], f: impl FnOnce(Option<&CStr>) -> R) -> R {
let list: Vec<_> = list.iter().map(|s| s.as_ptr().cast::<c_char>()).collect();
let joined = unsafe { wtokenjoin(list.as_slice().as_ptr().cast(), list.len() as c_int) };
let result = f(if joined.is_null() { None } else { unsafe { Some(CStr::from_ptr(joined)) } });
unsafe { crate::memory::free_bytes(joined.cast()); }
result
}
#[test]
fn join_nothing() {
with_list(&[], |joined| {
assert_eq!(joined.unwrap(), c"");
});
with_list(&[c"", c""], |joined| {
assert_eq!(joined.unwrap(), c"");
});
}
#[test]
fn join_basic() {
with_list(&[c"hello", c"world"], |joined| {
assert_eq!(joined.unwrap(), c"hello world");
});
}
#[test]
fn join_quoted() {
with_list(&[c"hello", c"there world"], |joined| {
assert_eq!(joined.unwrap(), c"hello \"there world\"");
});
}
}
+252
View File
@@ -0,0 +1,252 @@
//! N-ary tree structure.
//!
//! ## Rust rewrite notes
//!
//! This is only used in one place (`util/wmmenugen.c`), and it should probably
//! be moved out of wutil-rs (if not deleted entirely) once we migrate that
//! utility.
//!
//! The FFI functions provided here assume that they will be used as they are in
//! `wmmenugen.c`. (For example, the C interface may allow for some callback
//! parameters to be null, but they aren't in practice, so the Rust layer
//! doesn't check for that.)
//!
//! The original C library had a larger surface area and tracked parent
//! pointers, but the Rust version only has the API used by wmmenugen. Case in
//! point: `WMTreeNode` originally had an optional destructor function pointer
//! for freeing its `data` field, but wmmenugen never actually deleted a tree
//! node, so the destructor was never called. This Rust rewrite doesn't track a
//! `data` destructor and doesn't even provide a way to delete tree nodes.
//!
//! If a generic tree really becomes necessary, [`Tree`] may be adapted, but a
//! different design is probably warranted. (At the very least, `Tree` should be
//! generic over the type of `data` so that we're not using `c_void`. It is also
//! probably better to index into an owned `Vec` or use an external
//! arena. `GhostCell` or one of its ilk may even make sense.)
use std::ffi::c_void;
pub struct Tree {
depth: u32,
children: Vec<Box<Tree>>,
data: *mut c_void,
}
pub mod ffi {
use super::Tree;
use std::{
ffi::{c_int, c_void},
ptr,
};
/// Creates the root of a new tree with the data `data`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateTreeNode(data: *mut c_void) -> *mut Tree {
Box::leak(Box::new(Tree {
depth: 0,
children: Vec::new(),
data,
}))
}
/// Creates a tree node as the `index`th child of `parent`, with the data
/// `item`. If `index` is negative, the node is added as the last child of
/// `parent`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMInsertItemInTree(
parent: *mut Tree,
index: c_int,
item: *mut c_void,
) -> *mut Tree {
if parent.is_null() {
return ptr::null_mut();
}
let parent = unsafe { &mut *parent };
let child = Tree {
depth: parent.depth + 1,
children: Vec::new(),
data: item,
};
if index < 0 {
parent.children.push(Box::new(child));
parent.children.last_mut().unwrap().as_mut() as *mut _
} else {
let index = index as usize;
parent.children.insert(index, Box::new(child));
parent.children[index].as_mut() as *mut _
}
}
/// Inserts `tree` as the `index`th child of `parent`. If `index` is
/// negative, `tree` is added as the last child of `parent`.
///
/// This eagerly updates the depth of the entire subtree rooted at `tree`,
/// so it has a O(N) cost rather than O(1).
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMInsertNodeInTree(
parent: *mut Tree,
index: c_int,
tree: *mut Tree,
) -> *mut Tree {
if parent.is_null() {
return ptr::null_mut();
}
let parent = unsafe { &mut *parent };
if tree.is_null() {
return ptr::null_mut();
}
let mut stack = vec![(unsafe { &mut *tree }, parent.depth + 1)];
while let Some((tree, depth)) = stack.pop() {
tree.depth = depth;
for child in &mut tree.children {
stack.push((child, depth + 1));
}
}
if index < 0 {
parent.children.push(unsafe { Box::from_raw(tree) });
tree
} else {
let index = index as usize;
parent
.children
.insert(index, unsafe { Box::from_raw(tree) });
tree
}
}
/// Returns the data field of `tree`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetDataForTreeNode(tree: *mut Tree) -> *mut c_void {
if tree.is_null() {
return ptr::null_mut();
}
unsafe { (*tree).data }
}
/// Returns the depth of `tree` (0 if `tree` is a root, 1 if it is an
/// immediate child of the root, etc.). This is an `O(1)` operation.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetTreeNodeDepth(tree: *mut Tree) -> c_int {
if tree.is_null() {
return 0;
}
unsafe { (*tree).depth as c_int }
}
/// Recursively sorts the children of each node of the subtree rooted at
/// `tree`, according to `comparer`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSortTree(
tree: *mut Tree,
comparer: unsafe extern "C" fn(*const Tree, *const Tree) -> c_int,
) {
use std::cmp::Ordering;
if tree.is_null() {
return;
}
let comparer = |a: &Box<Tree>, b: &Box<Tree>| {
let a = a.as_ref() as *const Tree as *const _;
let b = b.as_ref() as *const Tree as *const _;
match unsafe { comparer(a, b) }.signum() {
-1 => Ordering::Less,
0 => Ordering::Equal,
1 => Ordering::Greater,
_ => unreachable!(),
}
};
let mut stack = vec![unsafe { &mut *tree }];
while let Some(tree) = stack.pop() {
tree.children.sort_by(comparer);
stack.extend(tree.children.iter_mut().map(|c| c.as_mut()));
}
}
/// Returns the first tree node in the subtree rooted at `tree` that matches
/// `match_p`, or null if none is found. If `match_p` is `None`, returns
/// the first node whose `data` field is equal to `cdata`. Search is
/// recursive, up to `limit` depth (which must be non-negative).
///
/// ## Rust rewrite notes
///
/// This was originally a DFS with a `depthFirst` parameter which actually
/// controlled whether tree traversal was pre- or post-order (and not
/// whether the search was depth-first or breadth-first, which the name
/// might seem to suggest). Since this was only ever called with a non-zero
/// `depthFirst`, the Rust version is hard-coded as a pre-order DFS.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFindInTreeWithDepthLimit(
tree: *mut Tree,
match_p: Option<unsafe extern "C" fn(*const Tree, *const c_void) -> c_int>,
cdata: *mut c_void,
limit: c_int,
) -> *mut Tree {
if tree.is_null() {
return ptr::null_mut();
}
let safe_tree = unsafe { &mut *tree };
let match_p = |t: &Tree| -> bool {
if let Some(p) = match_p {
(unsafe { (p)(t, cdata) }) != 0
} else {
t.data == cdata
}
};
let mut stack = vec![(safe_tree, 0)];
while let Some((tree, depth)) = stack.pop() {
if depth > limit {
continue;
}
if match_p(tree) {
return tree as *mut Tree;
}
for child in tree.children.iter_mut() {
stack.push((child.as_mut(), depth + 1));
}
}
ptr::null_mut()
}
/// Traverses `tree` recursively, invoking `walk` on each tree node with
/// `cdata` passed in to provide a side channel.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMTreeWalk(
tree: *mut Tree,
walk: unsafe extern "C" fn(*mut Tree, *mut c_void),
cdata: *mut c_void,
) {
if tree.is_null() {
return;
}
let tree = unsafe { &mut *tree };
let walk_fn = |t: &mut Tree| unsafe {
walk(t as *mut Tree, cdata);
};
let mut stack = vec![tree];
while let Some(tree) = stack.pop() {
walk_fn(tree);
for child in &mut tree.children {
stack.push(child);
}
}
}
}