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;
|
||||
|
||||
/* 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.*/
|
||||
XmdMap *XmdMapNew ();
|
||||
@ -27,7 +30,7 @@ void *XmdMapGet (XmdMap *map, XmdMapKey key);
|
||||
void *XmdMapSet (XmdMap *map, XmdMapKey key, void *value);
|
||||
|
||||
/* 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. */
|
||||
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
|
||||
|
||||
#include <X11/Intrinsic.h>
|
||||
#include <Xmd/String.h>
|
||||
|
||||
/* XmdStringMap is a hash map that is keyed by a string and can store any type
|
||||
of element. */
|
||||
@ -9,13 +10,15 @@ typedef struct _XmdStringMap XmdStringMap;
|
||||
|
||||
/* XmdStringMapIterator is a function that can iterate over a string map. */
|
||||
typedef Bool (*XmdStringMapIterator) (
|
||||
XmdStringMap *map, String key, void *value);
|
||||
XmdStringMap *map,
|
||||
ConstString key, void *value,
|
||||
void *clientData);
|
||||
|
||||
/* XmdStringMapNew creates a new map.*/
|
||||
XmdStringMap *XmdStringMapNew ();
|
||||
|
||||
/* 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
|
||||
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
|
||||
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. */
|
||||
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.
|
||||
*/
|
||||
|
@ -88,12 +88,16 @@ void *XmdMapSet (XmdMap *map, XmdMapKey key, void *value) {
|
||||
return previous;
|
||||
}
|
||||
|
||||
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator) {
|
||||
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator, void *clientData) {
|
||||
for (Cardinal index = 0; index < map->capacity; index ++) {
|
||||
XmdMapElement *element = map->data[index];
|
||||
while (element != NULL) {
|
||||
if(!iterator(map, element->key, element->value)) break;
|
||||
element = element->sister;
|
||||
XmdMapElement *sister = 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 Cardinal XmdStringMapHash (XmdStringMap *map, String key);
|
||||
static Cardinal XmdStringMapHash (XmdStringMap *map, ConstString key);
|
||||
|
||||
XmdStringMap *XmdStringMapNew () {
|
||||
XmdStringMap *map = XtNew(XmdStringMap);
|
||||
@ -36,7 +36,9 @@ XmdStringMap *XmdStringMapNew () {
|
||||
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)];
|
||||
while (element != NULL) {
|
||||
if (strcmp(element->key, key) == 0) return element->value;
|
||||
@ -45,7 +47,7 @@ void *XmdStringMapGet (XmdStringMap *map, String key) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *XmdStringMapSetInternal (XmdStringMap *map, String key, void *value) {
|
||||
void *XmdStringMapSetInternal (XmdStringMap *map, ConstString key, void *value) {
|
||||
/* find bucket */
|
||||
Cardinal hash = XmdStringMapHash(map, key);
|
||||
XmdStringMapElement **destination = &map->data[hash];
|
||||
@ -78,7 +80,9 @@ void *XmdStringMapSetInternal (XmdStringMap *map, String key, void *value) {
|
||||
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);
|
||||
if (value == NULL) {
|
||||
if (previous != NULL) map->length --;
|
||||
@ -89,12 +93,20 @@ void *XmdStringMapSet (XmdStringMap *map, String key, void *value) {
|
||||
return previous;
|
||||
}
|
||||
|
||||
void XmdStringMapIterate (XmdStringMap *map, XmdStringMapIterator iterator) {
|
||||
void XmdStringMapIterate (
|
||||
XmdStringMap *map,
|
||||
XmdStringMapIterator iterator,
|
||||
void *clientData
|
||||
) {
|
||||
for (Cardinal index = 0; index < map->capacity; index ++) {
|
||||
XmdStringMapElement *element = map->data[index];
|
||||
while (element != NULL) {
|
||||
if(!iterator(map, element->key, element->value)) break;
|
||||
element = element->sister;
|
||||
XmdStringMapElement *sister = element->sister;
|
||||
if (!iterator (
|
||||
map,
|
||||
element->key, element->value,
|
||||
clientData)) break;
|
||||
element = sister;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,11 +158,11 @@ void XmdStringMapResizeIfNeeded (XmdStringMap *map) {
|
||||
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 */
|
||||
Cardinal hash = 5381;
|
||||
Cardinal ch;
|
||||
while ((ch = (Cardinal)(*key ++)) != 0) {
|
||||
while ((ch = (Cardinal)(*(key ++))) != 0) {
|
||||
hash = ((hash << 5) + hash) + ch;
|
||||
}
|
||||
return hash % map->capacity;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -fPIC"
|
||||
CFLAGS="-std=c99 -Wall -Wextra -Werror -fPIC"
|
||||
PREFIX="/usr/local"
|
||||
APP_LIBS="-lXmd -lXm -lXt -lX11"
|
||||
|
Loading…
Reference in New Issue
Block a user