Add untested replicant framework
This commit is contained in:
parent
854fbf371c
commit
55dfca6341
@ -11,7 +11,10 @@ typedef long unsigned int XmdMapKey;
|
|||||||
typedef struct _XmdMap XmdMap;
|
typedef struct _XmdMap XmdMap;
|
||||||
|
|
||||||
/* XmdMapIterator is a function that can iterate over a map. */
|
/* XmdMapIterator is a function that can iterate over a map. */
|
||||||
typedef Bool (*XmdMapIterator) (XmdMap *map, XmdMapKey key, void *value);
|
typedef Bool (*XmdMapIterator) (
|
||||||
|
XmdMap *map,
|
||||||
|
XmdMapKey key, void *value,
|
||||||
|
void *clientData);
|
||||||
|
|
||||||
/* XmdMapNew creates a new map.*/
|
/* XmdMapNew creates a new map.*/
|
||||||
XmdMap *XmdMapNew ();
|
XmdMap *XmdMapNew ();
|
||||||
@ -27,7 +30,7 @@ void *XmdMapGet (XmdMap *map, XmdMapKey key);
|
|||||||
void *XmdMapSet (XmdMap *map, XmdMapKey key, void *value);
|
void *XmdMapSet (XmdMap *map, XmdMapKey key, void *value);
|
||||||
|
|
||||||
/* XmdMapIterate calls iterator for each key/value pair in a map. */
|
/* XmdMapIterate calls iterator for each key/value pair in a map. */
|
||||||
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator);
|
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator, void *clientData);
|
||||||
|
|
||||||
/* XmdMapLength returns the number of key/value entries stored in a map. */
|
/* XmdMapLength returns the number of key/value entries stored in a map. */
|
||||||
Cardinal XmdMapLength (XmdMap *map);
|
Cardinal XmdMapLength (XmdMap *map);
|
||||||
|
64
libXmd/include/Xmd/Replicant.h
Normal file
64
libXmd/include/Xmd/Replicant.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef _XmdReplicant_h
|
||||||
|
#define _XmdReplicant_h
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Intrinsic.h>
|
||||||
|
#include <Xm/Xm.h>
|
||||||
|
#include <Xmd/String.h>
|
||||||
|
|
||||||
|
#define XmdREPLICANT_VERSION 0
|
||||||
|
|
||||||
|
/* XmdReplicantState contains state information for a replicant instance. */
|
||||||
|
typedef struct _XmdReplicantState XmdReplicantState;
|
||||||
|
|
||||||
|
typedef int (*XmdReplicantVersion) ();
|
||||||
|
typedef Widget (*XmdReplicantCreate) (Widget parent, XmdReplicantState *state);
|
||||||
|
|
||||||
|
/* XmdReplicantOpen opens a replicant from the file located at path. Replicant
|
||||||
|
files hold state and point to shared object files containing their code.
|
||||||
|
Replicant shared objects are searched for in $XMD_REPLICANT_PATH. Here is an
|
||||||
|
example file that describes a calculator replicant:
|
||||||
|
|
||||||
|
[Calculator]
|
||||||
|
display=90
|
||||||
|
history=1 + 2 = 3;9 * 10 = 90;
|
||||||
|
mode=scientific
|
||||||
|
|
||||||
|
When a replicant's code is needed, it is automatically loaded by libXmd and
|
||||||
|
stays resident until all replicants using it have been freed. A system of
|
||||||
|
reference counting is used to accomplish this. =
|
||||||
|
|
||||||
|
Replicants must implement these symbols, where NAME is the base name of the
|
||||||
|
replicant's shared object file (case sensitive, and excluding the .so
|
||||||
|
extention):
|
||||||
|
|
||||||
|
int NAME_XmdReplicantVersion ();
|
||||||
|
Version must return the value of XmdREPLICANT_VERSION. This is used to detect
|
||||||
|
the API version that the replicant was compiled with, and applications with a
|
||||||
|
different API version will refuse to load it.
|
||||||
|
|
||||||
|
Widget NAME_XmdReplicantCreate (Widget parent, XmdReplicantState *state);
|
||||||
|
Create instantiates a new widget representing the replicant given by state.
|
||||||
|
*/
|
||||||
|
Widget XmdReplicantOpen (Widget parent, ConstString path);
|
||||||
|
|
||||||
|
/* XmdReplicantResolveName returns the file path of the given replicant shared
|
||||||
|
object name according to $XMD_REPLICANT_PATH. The returned string must be
|
||||||
|
freed using XtFree(). */
|
||||||
|
String XmdReplicantResolveName (ConstString name);
|
||||||
|
|
||||||
|
/* XmdReplicantStateQuery queries the replicant's state file for a named value.
|
||||||
|
The resulting string must be free'd using XtFree(). NULL is returned if there
|
||||||
|
is no value associated with the given name. This may perform a read from
|
||||||
|
disk. */
|
||||||
|
String XmdReplicantStateQuery (
|
||||||
|
XmdReplicantState *state,
|
||||||
|
ConstString name);
|
||||||
|
|
||||||
|
/* XmdReplicantStateStore stores a named value in the replicant's state file.
|
||||||
|
This may perform a write to disk. */
|
||||||
|
void XmdReplicantStateStore (
|
||||||
|
XmdReplicantState *state,
|
||||||
|
ConstString name, ConstString value);
|
||||||
|
|
||||||
|
#endif
|
6
libXmd/include/Xmd/String.h
Normal file
6
libXmd/include/Xmd/String.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _XmdString_h
|
||||||
|
#define _XmdString_h
|
||||||
|
|
||||||
|
typedef const char *ConstString;
|
||||||
|
|
||||||
|
#endif
|
@ -2,6 +2,7 @@
|
|||||||
#define _XmdStringMap_h
|
#define _XmdStringMap_h
|
||||||
|
|
||||||
#include <X11/Intrinsic.h>
|
#include <X11/Intrinsic.h>
|
||||||
|
#include <Xmd/String.h>
|
||||||
|
|
||||||
/* XmdStringMap is a hash map that is keyed by a string and can store any type
|
/* XmdStringMap is a hash map that is keyed by a string and can store any type
|
||||||
of element. */
|
of element. */
|
||||||
@ -9,13 +10,15 @@ typedef struct _XmdStringMap XmdStringMap;
|
|||||||
|
|
||||||
/* XmdStringMapIterator is a function that can iterate over a string map. */
|
/* XmdStringMapIterator is a function that can iterate over a string map. */
|
||||||
typedef Bool (*XmdStringMapIterator) (
|
typedef Bool (*XmdStringMapIterator) (
|
||||||
XmdStringMap *map, String key, void *value);
|
XmdStringMap *map,
|
||||||
|
ConstString key, void *value,
|
||||||
|
void *clientData);
|
||||||
|
|
||||||
/* XmdStringMapNew creates a new map.*/
|
/* XmdStringMapNew creates a new map.*/
|
||||||
XmdStringMap *XmdStringMapNew ();
|
XmdStringMap *XmdStringMapNew ();
|
||||||
|
|
||||||
/* XmdStringMapGet retrieves a value from a map. */
|
/* XmdStringMapGet retrieves a value from a map. */
|
||||||
void *XmdStringMapGet (XmdStringMap *map, String key);
|
void *XmdStringMapGet (XmdStringMap *map, ConstString key);
|
||||||
|
|
||||||
/* XmdStringMapSet sets a value in a map. The previous value associated with the
|
/* XmdStringMapSet sets a value in a map. The previous value associated with the
|
||||||
key, if it exists, is overwritten by the new value. If the value is set to
|
key, if it exists, is overwritten by the new value. If the value is set to
|
||||||
@ -23,10 +26,12 @@ void *XmdStringMapGet (XmdStringMap *map, String key);
|
|||||||
returns the previous value associated with the key so it can be free'd if
|
returns the previous value associated with the key so it can be free'd if
|
||||||
necessary.
|
necessary.
|
||||||
*/
|
*/
|
||||||
void *XmdStringMapSet (XmdStringMap *map, String key, void *value);
|
void *XmdStringMapSet (XmdStringMap *map, ConstString key, void *value);
|
||||||
|
|
||||||
/* XmdStringMapIterate calls iterator for each key/value pair in a map. */
|
/* XmdStringMapIterate calls iterator for each key/value pair in a map. */
|
||||||
void XmdStringMapIterate (XmdStringMap *map, XmdStringMapIterator iterator);
|
void XmdStringMapIterate (
|
||||||
|
XmdStringMap *map, XmdStringMapIterator iterator,
|
||||||
|
void *clientData);
|
||||||
|
|
||||||
/* XmdStringMapLength returns the number of key/value entries stored in a map.
|
/* XmdStringMapLength returns the number of key/value entries stored in a map.
|
||||||
*/
|
*/
|
||||||
|
@ -88,12 +88,16 @@ void *XmdMapSet (XmdMap *map, XmdMapKey key, void *value) {
|
|||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator) {
|
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator, void *clientData) {
|
||||||
for (Cardinal index = 0; index < map->capacity; index ++) {
|
for (Cardinal index = 0; index < map->capacity; index ++) {
|
||||||
XmdMapElement *element = map->data[index];
|
XmdMapElement *element = map->data[index];
|
||||||
while (element != NULL) {
|
while (element != NULL) {
|
||||||
if(!iterator(map, element->key, element->value)) break;
|
XmdMapElement *sister = element->sister;
|
||||||
element = element->sister;
|
if (!iterator (
|
||||||
|
map,
|
||||||
|
element->key, element->value,
|
||||||
|
clientData)) break;
|
||||||
|
element = sister;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
332
libXmd/src/Replicant.c
Normal file
332
libXmd/src/Replicant.c
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
#include <Xmd/Replicant.h>
|
||||||
|
#include <Xmd/StringMap.h>
|
||||||
|
#include <Xmd/Map.h>
|
||||||
|
#include <Xmd/Buffer.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *handle;
|
||||||
|
int references;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
XmdReplicantVersion version;
|
||||||
|
XmdReplicantCreate create;
|
||||||
|
} symbols;
|
||||||
|
} XmdReplicantSource;
|
||||||
|
|
||||||
|
struct _XmdReplicantState {
|
||||||
|
String file;
|
||||||
|
String sourceName;
|
||||||
|
XmdReplicantSource *source;
|
||||||
|
XmdStringMap *map;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void XmdReplicantHandleDestroy (
|
||||||
|
Widget replicant,
|
||||||
|
XtPointer clientData,
|
||||||
|
XtPointer callData);
|
||||||
|
static void XmdReplicantStateSave (XmdReplicantState *state);
|
||||||
|
static void XmdReplicantStateLoad (XmdReplicantState *state);
|
||||||
|
static XmdReplicantState *XmdReplicantStateNew ();
|
||||||
|
static void XmdReplicantStateFree (XmdReplicantState *state);
|
||||||
|
static XmdReplicantSource *XmdReplicantSourceOpen (ConstString name);
|
||||||
|
static void XmdReplicantSourceClose (XmdReplicantSource *source);
|
||||||
|
static XmdReplicantSource *XmdReplicantSourceGet (ConstString name);
|
||||||
|
static String XmdReplicantScanDir (
|
||||||
|
ConstString path,
|
||||||
|
ConstString name);
|
||||||
|
|
||||||
|
static const String defaultReplicantPath =
|
||||||
|
"/usr/lib/Xmd/replicants:"
|
||||||
|
"/usr/local/lib/Xmd/replicants";
|
||||||
|
static XmdStringMap *resident = NULL; /* source names to source structs */
|
||||||
|
|
||||||
|
Widget XmdReplicantOpen (Widget parent, ConstString path) {
|
||||||
|
/* create and load the state */
|
||||||
|
XmdReplicantState *state = XmdReplicantStateNew();
|
||||||
|
if (state == NULL) goto fail;
|
||||||
|
state->file = XtNewString(path);
|
||||||
|
if (state->file == NULL) goto fail;
|
||||||
|
XmdReplicantStateLoad(state);
|
||||||
|
|
||||||
|
/* retrieve the replicant's source */
|
||||||
|
XmdReplicantSource *source = XmdReplicantSourceGet(state->sourceName);
|
||||||
|
if (source == NULL) goto fail;
|
||||||
|
source->references ++;
|
||||||
|
state->source = source;
|
||||||
|
|
||||||
|
/* create replicant and bind its state */
|
||||||
|
Widget replicant = source->symbols.create(parent, state);
|
||||||
|
XtAddCallback (
|
||||||
|
replicant, XmNdestroyCallback,
|
||||||
|
XmdReplicantHandleDestroy, state);
|
||||||
|
return replicant;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
XmdReplicantStateFree(state);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void XmdReplicantHandleDestroy (
|
||||||
|
Widget replicant,
|
||||||
|
XtPointer clientData,
|
||||||
|
XtPointer callData
|
||||||
|
) {
|
||||||
|
(void)(replicant);
|
||||||
|
(void)(callData);
|
||||||
|
|
||||||
|
XmdReplicantState *state = clientData;
|
||||||
|
if (state == NULL) return;
|
||||||
|
|
||||||
|
state->source->references --;
|
||||||
|
if (state->source->references < 1) {
|
||||||
|
XmdReplicantSourceClose (
|
||||||
|
XmdStringMapSet(resident, state->sourceName, NULL));
|
||||||
|
}
|
||||||
|
XmdReplicantStateFree(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
String XmdReplicantResolveName (ConstString rawName) {
|
||||||
|
String list = getenv("XMD_REPLICANT_PATH");
|
||||||
|
XmdBuffer *dirBuffer = NULL;
|
||||||
|
String file = NULL;
|
||||||
|
String name = NULL;
|
||||||
|
XtAsprintf(&name, "%s.so", rawName);
|
||||||
|
if (list == NULL) list = defaultReplicantPath;
|
||||||
|
|
||||||
|
while (file == NULL) {
|
||||||
|
char ch = *list;
|
||||||
|
if (dirBuffer == NULL) dirBuffer = XmdBufferNew(char);
|
||||||
|
|
||||||
|
XmdBufferPush(dirBuffer, &ch);
|
||||||
|
if (ch == ':' || ch == 0) {
|
||||||
|
String dir = XmdBufferBreak(dirBuffer);
|
||||||
|
dirBuffer = NULL;
|
||||||
|
file = XmdReplicantScanDir(dir, name);
|
||||||
|
XtFree(dir);
|
||||||
|
}
|
||||||
|
list ++;
|
||||||
|
}
|
||||||
|
XtFree(name);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
String XmdReplicantStateQuery (XmdReplicantState *state, ConstString name) {
|
||||||
|
return XtNewString(XmdStringMapGet(state->map, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmdReplicantStateStore (
|
||||||
|
XmdReplicantState *state,
|
||||||
|
ConstString name, ConstString value
|
||||||
|
) {
|
||||||
|
String old = XmdStringMapSet(state->map, name, XtNewString(value));
|
||||||
|
XtFree(old);
|
||||||
|
XmdReplicantStateSave(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeEscapedMapString (FILE *file, ConstString string) {
|
||||||
|
while (1) {
|
||||||
|
char ch = *string ++;
|
||||||
|
switch (ch) {
|
||||||
|
case '=':
|
||||||
|
fputc('\\', file);
|
||||||
|
fputc('=', file);
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
fputc('\\', file);
|
||||||
|
fputc('[', file);
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
fputc('\\', file);
|
||||||
|
fputc(']', file);
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
fputc('\\', file);
|
||||||
|
fputc('n', file);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
fputc(ch, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool writeMapValue (
|
||||||
|
XmdStringMap *map,
|
||||||
|
ConstString key, void *value,
|
||||||
|
void *clientData
|
||||||
|
) {
|
||||||
|
(void)(map);
|
||||||
|
FILE *file = clientData;
|
||||||
|
writeEscapedMapString(file, key);
|
||||||
|
fputc('=', file);
|
||||||
|
writeEscapedMapString(file, value);
|
||||||
|
fputc('\n', file);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void XmdReplicantStateSave (XmdReplicantState *state) {
|
||||||
|
FILE *file = fopen(state->file, "w");
|
||||||
|
if (file == NULL) return;
|
||||||
|
fprintf(file, "[%s]\n", state->sourceName);
|
||||||
|
XmdStringMapIterate(state->map, writeMapValue, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String readEscapedMapString (FILE *file, char delimiter) {
|
||||||
|
int ch = fgetc(file);
|
||||||
|
if (ch == EOF) return NULL;
|
||||||
|
ungetc(ch, file);
|
||||||
|
|
||||||
|
XmdBuffer *string = XmdBufferNew(char);
|
||||||
|
while (1) {
|
||||||
|
ch = fgetc(file);
|
||||||
|
if (ch == EOF) break;
|
||||||
|
if (ch == delimiter) break;
|
||||||
|
|
||||||
|
if (ch == '\\') {
|
||||||
|
ch = fgetc(file);
|
||||||
|
if (ch == EOF) break;
|
||||||
|
if (ch == '\n') {
|
||||||
|
char newline = '\n';
|
||||||
|
XmdBufferPush(string, &newline);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XmdBufferPush(string, &ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
char null = 0;
|
||||||
|
XmdBufferPush(string, &null);
|
||||||
|
return XmdBufferBreak(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void XmdReplicantStateLoad (XmdReplicantState *state) {
|
||||||
|
FILE *file = fopen(state->file, "w");
|
||||||
|
if (file == NULL) return;
|
||||||
|
|
||||||
|
int ch = 0;
|
||||||
|
while (isspace((ch == fgetc(file))));
|
||||||
|
if (ch != '[') return;
|
||||||
|
state->sourceName = readEscapedMapString(file, ']');
|
||||||
|
ch = fgetc(file);
|
||||||
|
if (ch != '\n') return;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
String key = readEscapedMapString(file, '=');
|
||||||
|
String value = readEscapedMapString(file, '\n');
|
||||||
|
XmdStringMapSet(state->map, key, value);
|
||||||
|
XtFree(key);
|
||||||
|
if (value == NULL) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static XmdReplicantState *XmdReplicantStateNew () {
|
||||||
|
XmdReplicantState *state = XtNew(XmdReplicantState);
|
||||||
|
if (state == NULL) return NULL;
|
||||||
|
|
||||||
|
memset(state, 0, sizeof(XmdReplicantState));
|
||||||
|
state->map = XmdStringMapNew();
|
||||||
|
if (state->map == NULL) goto fail;
|
||||||
|
|
||||||
|
return state;
|
||||||
|
fail:
|
||||||
|
XmdReplicantStateFree(state);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool freeMapValue (
|
||||||
|
XmdStringMap *map,
|
||||||
|
ConstString key, void *value,
|
||||||
|
void *clientData
|
||||||
|
) {
|
||||||
|
(void)(map);
|
||||||
|
(void)(key);
|
||||||
|
(void)(clientData);
|
||||||
|
XtFree(value);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void XmdReplicantStateFree (XmdReplicantState *state) {
|
||||||
|
if (state == NULL) return;
|
||||||
|
XtFree(state->file);
|
||||||
|
XtFree(state->sourceName);
|
||||||
|
XmdStringMapIterate(state->map, freeMapValue, NULL);
|
||||||
|
XmdStringMapFree(state->map);
|
||||||
|
XtFree((char *)(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
static XmdReplicantSource *XmdReplicantSourceOpen (ConstString name) {
|
||||||
|
/* allocate */
|
||||||
|
XmdReplicantSource *source = XtNew(XmdReplicantSource);
|
||||||
|
if (source == NULL) goto fail;
|
||||||
|
memset(source, 0, sizeof(XmdReplicantSource));
|
||||||
|
|
||||||
|
/* open library */
|
||||||
|
String path = XmdReplicantResolveName(name);
|
||||||
|
if (path == NULL) goto fail;
|
||||||
|
source->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
|
||||||
|
XtFree(path);
|
||||||
|
if (source->handle == NULL) goto fail;
|
||||||
|
|
||||||
|
/* find symbols */
|
||||||
|
String versionName = NULL;
|
||||||
|
XtAsprintf(&versionName, "%s_XmdReplicantVersion", name);
|
||||||
|
source->symbols.version = (XmdReplicantVersion) (
|
||||||
|
dlsym(source->handle, versionName));
|
||||||
|
XtFree(versionName);
|
||||||
|
if (source->symbols.version == NULL) goto fail;
|
||||||
|
String createName = NULL;
|
||||||
|
XtAsprintf(&createName, "%s_XmdReplicantCreate", name);
|
||||||
|
source->symbols.create = (XmdReplicantCreate) (
|
||||||
|
dlsym(source->handle, createName));
|
||||||
|
XtFree(createName);
|
||||||
|
if (source->symbols.create == NULL) goto fail;
|
||||||
|
|
||||||
|
/* check if the version matches */
|
||||||
|
if (source->symbols.version() != XmdREPLICANT_VERSION) goto fail;
|
||||||
|
|
||||||
|
return source;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
XmdReplicantSourceClose(source);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void XmdReplicantSourceClose (XmdReplicantSource *source) {
|
||||||
|
if (source == NULL) return;
|
||||||
|
if (source->handle != NULL) dlclose(source->handle);
|
||||||
|
XtFree((char *)(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
static XmdReplicantSource *XmdReplicantSourceGet (ConstString name) {
|
||||||
|
/* check to see if it has already been loaded */
|
||||||
|
XmdReplicantSource *source = XmdStringMapGet(resident, name);
|
||||||
|
if (source != NULL) return source;
|
||||||
|
|
||||||
|
/* open the source */
|
||||||
|
return XmdReplicantSourceOpen(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String XmdReplicantScanDir (ConstString path, ConstString name) {
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
String result = NULL;
|
||||||
|
while (1) {
|
||||||
|
struct dirent *entry = readdir(dir);
|
||||||
|
if (entry == NULL) break;
|
||||||
|
|
||||||
|
if (strcmp(name, entry->d_name) == 0) {
|
||||||
|
XtAsprintf(&result, "%s/%s", path, entry->d_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return result;
|
||||||
|
}
|
@ -17,7 +17,7 @@ struct _XmdStringMap {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void XmdStringMapResizeIfNeeded (XmdStringMap *map);
|
static void XmdStringMapResizeIfNeeded (XmdStringMap *map);
|
||||||
static Cardinal XmdStringMapHash (XmdStringMap *map, String key);
|
static Cardinal XmdStringMapHash (XmdStringMap *map, ConstString key);
|
||||||
|
|
||||||
XmdStringMap *XmdStringMapNew () {
|
XmdStringMap *XmdStringMapNew () {
|
||||||
XmdStringMap *map = XtNew(XmdStringMap);
|
XmdStringMap *map = XtNew(XmdStringMap);
|
||||||
@ -36,7 +36,9 @@ XmdStringMap *XmdStringMapNew () {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *XmdStringMapGet (XmdStringMap *map, String key) {
|
void *XmdStringMapGet (XmdStringMap *map, ConstString key) {
|
||||||
|
if (key == NULL) return NULL;
|
||||||
|
|
||||||
XmdStringMapElement *element = map->data[XmdStringMapHash(map, key)];
|
XmdStringMapElement *element = map->data[XmdStringMapHash(map, key)];
|
||||||
while (element != NULL) {
|
while (element != NULL) {
|
||||||
if (strcmp(element->key, key) == 0) return element->value;
|
if (strcmp(element->key, key) == 0) return element->value;
|
||||||
@ -45,7 +47,7 @@ void *XmdStringMapGet (XmdStringMap *map, String key) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *XmdStringMapSetInternal (XmdStringMap *map, String key, void *value) {
|
void *XmdStringMapSetInternal (XmdStringMap *map, ConstString key, void *value) {
|
||||||
/* find bucket */
|
/* find bucket */
|
||||||
Cardinal hash = XmdStringMapHash(map, key);
|
Cardinal hash = XmdStringMapHash(map, key);
|
||||||
XmdStringMapElement **destination = &map->data[hash];
|
XmdStringMapElement **destination = &map->data[hash];
|
||||||
@ -78,7 +80,9 @@ void *XmdStringMapSetInternal (XmdStringMap *map, String key, void *value) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *XmdStringMapSet (XmdStringMap *map, String key, void *value) {
|
void *XmdStringMapSet (XmdStringMap *map, ConstString key, void *value) {
|
||||||
|
if (key == NULL) return NULL;
|
||||||
|
|
||||||
void *previous = XmdStringMapSetInternal(map, key, value);
|
void *previous = XmdStringMapSetInternal(map, key, value);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
if (previous != NULL) map->length --;
|
if (previous != NULL) map->length --;
|
||||||
@ -89,12 +93,20 @@ void *XmdStringMapSet (XmdStringMap *map, String key, void *value) {
|
|||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XmdStringMapIterate (XmdStringMap *map, XmdStringMapIterator iterator) {
|
void XmdStringMapIterate (
|
||||||
|
XmdStringMap *map,
|
||||||
|
XmdStringMapIterator iterator,
|
||||||
|
void *clientData
|
||||||
|
) {
|
||||||
for (Cardinal index = 0; index < map->capacity; index ++) {
|
for (Cardinal index = 0; index < map->capacity; index ++) {
|
||||||
XmdStringMapElement *element = map->data[index];
|
XmdStringMapElement *element = map->data[index];
|
||||||
while (element != NULL) {
|
while (element != NULL) {
|
||||||
if(!iterator(map, element->key, element->value)) break;
|
XmdStringMapElement *sister = element->sister;
|
||||||
element = element->sister;
|
if (!iterator (
|
||||||
|
map,
|
||||||
|
element->key, element->value,
|
||||||
|
clientData)) break;
|
||||||
|
element = sister;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,11 +158,11 @@ void XmdStringMapResizeIfNeeded (XmdStringMap *map) {
|
|||||||
XtFree((char *)(oldData));
|
XtFree((char *)(oldData));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Cardinal XmdStringMapHash (XmdStringMap *map, String key) {
|
static Cardinal XmdStringMapHash (XmdStringMap *map, ConstString key) {
|
||||||
/* http://www.cse.yorku.ca/~oz/hash.html */
|
/* http://www.cse.yorku.ca/~oz/hash.html */
|
||||||
Cardinal hash = 5381;
|
Cardinal hash = 5381;
|
||||||
Cardinal ch;
|
Cardinal ch;
|
||||||
while ((ch = (Cardinal)(*key ++)) != 0) {
|
while ((ch = (Cardinal)(*(key ++))) != 0) {
|
||||||
hash = ((hash << 5) + hash) + ch;
|
hash = ((hash << 5) + hash) + ch;
|
||||||
}
|
}
|
||||||
return hash % map->capacity;
|
return hash % map->capacity;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -fPIC"
|
CFLAGS="-std=c99 -Wall -Wextra -Werror -fPIC"
|
||||||
PREFIX="/usr/local"
|
PREFIX="/usr/local"
|
||||||
APP_LIBS="-lXmd -lXm -lXt -lX11"
|
APP_LIBS="-lXmd -lXm -lXt -lX11"
|
||||||
|
Loading…
Reference in New Issue
Block a user