Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/Makefile.in50
-rw-r--r--src/util/catalog.c253
-rw-r--r--src/util/help.c95
-rw-r--r--src/util/png.c201
-rw-r--r--src/util/thread.c221
-rw-r--r--src/util/timers.c670
-rw-r--r--src/util/util.pri12
-rw-r--r--src/util/xerror.c43
-rw-r--r--src/util/xldio.c603
-rw-r--r--src/util/xmenu.c998
-rw-r--r--src/util/xshl.c470
-rw-r--r--src/util/xstdio.c488
-rw-r--r--src/util/xstring.c141
13 files changed, 4245 insertions, 0 deletions
diff --git a/src/util/Makefile.in b/src/util/Makefile.in
new file mode 100644
index 0000000..36aad5c
--- /dev/null
+++ b/src/util/Makefile.in
@@ -0,0 +1,50 @@
+CC = @CC@
+CFLAGS = @CFLAGS@
+LIBS = @LIBS@ -lm
+LFLAGS = @LDFLAGS@
+AR = @AR@
+RANLIB = @RANLIB@
+
+SRCS = \
+ png.c \
+ catalog.c \
+ thread.c \
+ xstring.c \
+ help.c \
+ xerror.c \
+ xshl.c \
+ xldio.c \
+ xstdio.c \
+ xmenu.c \
+ timers.c
+
+OBJS = $(SRCS:.c=.o)
+
+TLIB = ../lib/libutil.a
+
+
+all: $(TLIB)
+
+$(TLIB):$(OBJS)
+ rm -f $@
+ $(AR) rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ rm -f $(TLIB)
+ rm -f *.[oas]
+ rm -f *~
+ rm -f core
+
+distclean:clean
+ rm Makefile
+
+#dep:
+# rm -f .depend
+# make .depend
+#
+#.depend:
+# echo '# Program dependencies' >.depend
+# gcc -I svgalib $(DEFINES) -MM $(patsubst %.o,%.c,$(OBJS)) >>.depend
+#
+#include .depend
diff --git a/src/util/catalog.c b/src/util/catalog.c
new file mode 100644
index 0000000..76cfa69
--- /dev/null
+++ b/src/util/catalog.c
@@ -0,0 +1,253 @@
+#include <config.h>
+#ifndef _plan9_
+#include <string.h>
+#ifndef NO_MALLOC_H
+#include <malloc.h>
+#endif
+#include <string.h>
+#else
+#include <u.h>
+#include <libc.h>
+#endif
+#include <catalog.h>
+#include <misc-f.h>
+/* Well, just simple implementation of unbalanced trees in combination
+ * of small hash table. I am lazy :) but should be OK for my purposes
+ *
+ * This function is used for both-lookups and adds into table (since most of
+ * code is the same, depends whether newvalue is NULL. If newvalue is nonNULL,
+ * new variable is added into table, if name is not present here or value is
+ * changed othewise.
+ */
+static char *find_variable(catalog_t * context, CONST char *name,
+ CONST char *newvalue)
+{
+ int r = 0;
+ int hash = (int) strlen(name);
+ struct varnames *current, *last, *newp;
+ hash =
+ ((unsigned char) (name[0]) + (unsigned char) (name[hash - 1]) +
+ hash) % (unsigned int) CHASHMAX;
+ current = last = context->root[hash];
+ while (current != NULL) {
+ last = current;
+ r = strcmp(current->name, name);
+ if (!r) {
+ if (newvalue != NULL) { /*overwrite value */
+ free(current->value);
+ current->value = mystrdup(newvalue);
+ }
+ return (current->value);
+ }
+ if (r > 0)
+ current = current->left;
+ else
+ current = current->right;
+ }
+ /*Entry is new */
+ if (newvalue == NULL)
+ return (NULL);
+ newp = (struct varnames *) calloc(1, sizeof(struct varnames));
+ newp->name = mystrdup(name);
+ newp->value = mystrdup(newvalue);
+ /*FIXME. Should take a care to full memory */
+ newp->left = NULL;
+ newp->right = NULL;
+ if (last == NULL) {
+ context->root[hash] = newp;
+ } else {
+ if (r > 0)
+ last->left = newp;
+ else
+ last->right = newp;
+ }
+ return (newp->value);
+}
+
+/*
+ * free memory used by node and its sons
+ */
+static void free_node(struct varnames *node)
+{
+ while (node != NULL) {
+ struct varnames *nextnode;
+ free_node(node->left);
+ nextnode = node->right;
+ free(node->name);
+ free(node->value);
+ free(node);
+ node = nextnode;
+ }
+}
+
+/*
+ * free catalog
+ */
+void free_catalog(catalog_t * context)
+{
+ int i;
+ for (i = 0; i < CHASHMAX; i++) {
+ free_node(context->root[i]);
+ context->root[i] = NULL;
+ }
+ free(context);
+}
+
+static catalog_t *alloc_catalog(void)
+{
+ int i;
+ catalog_t *c;
+ c = (catalog_t *) calloc(1, sizeof(catalog_t));
+ if (c == NULL)
+ return NULL;
+ for (i = 0; i < CHASHMAX; i++)
+ c->root[i] = NULL;
+ return c;
+}
+
+/*
+ * Parse an catalog file and save values into memory
+ */
+// FIXME: this macro gives a segfault if \" is used in text. kovzol, 2009-06-29
+#define seterror(text) sprintf(errort,"line %i:%s",line,text),*error=errort
+catalog_t *load_catalog(xio_file f, CONST char **error)
+{
+ int i;
+ int line = 1;
+ int size;
+ int c;
+ catalog_t *catalog = alloc_catalog();
+ static char errort[40];
+ char name[1024];
+ char value[1024];
+ if (catalog == NULL) {
+ *error = "Out of memory";
+ }
+ if (f == NULL) {
+ *error = "File could not be opened";
+ free_catalog(catalog);
+ return NULL;
+ }
+ /* Just very simple parsing loop of format
+ * [blanks]name[blanks]"value"[blanks]
+ * Blanks should be comments using # or space, newline, \r and tabulator
+ * Value shoud contain and \ seqences where \\ means \ and
+ * \[something] means something. Should be used for character "
+ */
+ while (!xio_feof(f)) {
+
+ do {
+ c = xio_getc(f);
+ if (c == '\n')
+ line++;
+ if (c == '#') {
+ while ((c = xio_getc(f)) != '\n' && c != XIO_EOF);
+ line++;
+ }
+ }
+ while (c == ' ' || c == '\n' || c == '\r' || c == '\t');
+
+ /*Skip blanks */
+ if (c == XIO_EOF) {
+ if (xio_feof(f))
+ break;
+ free_catalog(catalog);
+ seterror("read error");
+ xio_close(f);
+ return NULL;
+ }
+ i = 0;
+
+ /*read name */
+ do {
+ name[i] = c;
+ i++;
+ c = xio_getc(f);
+ if (c == '\n')
+ line++;
+ if (i == 1024) {
+ seterror("Name is too long (>=1024 characters)");
+ free_catalog(catalog);
+ xio_close(f);
+ return NULL;
+ }
+ }
+
+ while (c != '\n' && c != ' ' && c != '\t' && c != XIO_EOF);
+
+ /*Skip blanks */
+ while (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+ c = xio_getc(f);
+ if (c == '\n')
+ line++;
+ if (c == '#') {
+ while ((c = xio_getc(f)) != '\n' && c != XIO_EOF);
+ line++;
+ }
+ }
+
+ /*Skip blanks */
+ if (c == XIO_EOF) {
+ if (xio_feof(f))
+ seterror("Unexpected end of file after name field");
+ else
+ seterror("read error");
+ free_catalog(catalog);
+ xio_close(f);
+ return NULL;
+ }
+
+ name[i] = 0;
+ if (c != '"') {
+ seterror("Begin of value field expected"); // Text modified due to segfault problem, see above (kovzol)
+ free_catalog(catalog);
+ xio_close(f);
+ return NULL;
+ }
+ c = xio_getc(f);
+ if (c == '\n')
+ line++;
+ i = 0;
+
+ size = 0;
+ do {
+ if (c == '\\')
+ value[i] = xio_getc(f);
+ else
+ value[i] = c;
+ i++;
+ c = xio_getc(f);
+ if (c == '\n')
+ line++, size = 0;
+ if (size == 40 && c != '"') {
+ fprintf(stderr, "Warning - too long text at line %i\n",
+ line);
+ }
+ size++;
+ if (i == 1024) {
+ seterror("Value is too long (>=1024 characters)");
+ free_catalog(catalog);
+ xio_close(f);
+ return NULL;
+ }
+ }
+ while (c != '"' && c != XIO_EOF);
+
+ if (c == XIO_EOF) {
+ seterror("Unexpected EOF in value field");
+ free_catalog(catalog);
+ xio_close(f);
+ return NULL;
+ }
+
+ value[i] = 0;
+ find_variable(catalog, name, value);
+ } /*while */
+ xio_close(f);
+ return (catalog);
+} /*load_catalog */
+
+char *find_text(catalog_t * catalog, CONST char *name)
+{
+ return (find_variable(catalog, name, NULL));
+}
diff --git a/src/util/help.c b/src/util/help.c
new file mode 100644
index 0000000..adaabb7
--- /dev/null
+++ b/src/util/help.c
@@ -0,0 +1,95 @@
+#ifdef _plan9_
+#include <u.h>
+#include <libc.h>
+#else
+#include <stdlib.h>
+#endif
+#include <ctype.h>
+#include <config.h>
+#include <xio.h>
+#include <xshl.h>
+struct helpstatus {
+ int eol;
+ xio_file file;
+};
+static int gethelp(void *userdata)
+{
+ struct helpstatus *s = (struct helpstatus *) userdata;
+ int c = xio_getc(s->file);
+ if (c == '\r')
+ return (gethelp(userdata));
+ if (c < 0 || c > 255)
+ return 0;
+ if (c == '%' && s->eol)
+ return 0;
+ s->eol = (c == '\n');
+ return c;
+}
+
+struct xshl_line *help_make(CONST char *command,
+ int getwidth(void *, int flags,
+ CONST char *text), int width,
+ int smallheight, int bigheight)
+{
+ struct helpstatus *s = (struct helpstatus *) malloc(sizeof(*s));
+ struct xshl_line *line;
+ s->file = xio_gethelp();
+ if (s->file == XIO_FAILED) {
+ free(s);
+ return 0;
+ }
+ s->eol = 1;
+ while (1) {
+ int c;
+ c = xio_getc(s->file);
+ if (c == '%' && s->eol) {
+ c = xio_getc(s->file);
+ do {
+ int i = 0;
+ i = 0;
+ while (command[i] && c == command[i]) {
+ c = xio_getc(s->file);
+ i++;
+ }
+ if (!command[i]) {
+ if (isspace(c)) {
+ while (c != '\n' && !xio_feof(s->file))
+ c = xio_getc(s->file);
+ line =
+ xshl_interpret(s, gethelp, width, getwidth, 0,
+ smallheight, bigheight);
+ xio_close(s->file);
+ free(s);
+ return (line);
+ }
+ } else {
+ while (c != '\n' && c != ' ' && !xio_feof(s->file))
+ c = xio_getc(s->file);
+ if (c == ' ')
+ while (c == ' ')
+ c = xio_getc(s->file);
+ }
+ }
+ while (c != '\n' && !xio_feof(s->file));
+ } /*c==% */
+ s->eol = (c == '\n');
+ if (xio_feof(s->file)) {
+ xio_close(s->file);
+ return NULL;
+ }
+ } /*while 1 */
+}
+
+#if _NEVER_
+void help_print(char *name, int skip, int width)
+{
+ struct xshl_line *l;
+ l = help_make(name, xshl_textlen, width, 1, 1);
+ if (l == NULL) {
+ printf("Help not available!\n");
+ return;
+ }
+ xshl_print(skip, l);
+ xshl_free(l);
+}
+#endif
diff --git a/src/util/png.c b/src/util/png.c
new file mode 100644
index 0000000..4fb01bc
--- /dev/null
+++ b/src/util/png.c
@@ -0,0 +1,201 @@
+#include <config.h>
+#ifndef _plan9_
+#include <aconfig.h>
+#ifdef USE_PNG
+#include <png.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef NO_MALLOC_H
+#include <malloc.h>
+#endif
+#include <errno.h>
+#else
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#endif
+#include <filter.h>
+#include <version.h>
+#include <misc-f.h>
+#include <xio.h>
+#ifndef USE_PNG
+CONST char *writepng(xio_constpath filename, CONST struct image *img)
+{
+ return
+ "XaoS can not save images because it was incorrectly compiled. Please compile it with zlib and libpng";
+}
+#else
+
+CONST char *writepng(xio_constpath filename, CONST struct image *image)
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_color palette[256];
+ volatile unsigned short a = 255;
+ volatile unsigned char *b = (volatile unsigned char *) &a;
+#ifdef _undefined_
+ static char text[] =
+ "XaoS" XaoS_VERSION " - a realtime interactive fractal zoomer";
+ static png_text comments[] = {
+ {
+ -1,
+ "Software",
+ text,
+ sizeof(text)}
+ };
+#endif
+ FILE *file = fopen(filename, "wb");
+ errno = -1;
+ if (file == NULL) {
+ return strerror(errno);
+ }
+ png_ptr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, (void *) NULL,
+ (png_error_ptr) NULL,
+ (png_error_ptr) NULL);
+ if (!png_ptr)
+ return "Unable to initialize pnglib";
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+ return "No memory to create png info structure";
+ }
+ if (setjmp(png_ptr->jmpbuf)) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(file);
+ return strerror(errno);
+ }
+ png_init_io(png_ptr, file);
+ png_set_filter(png_ptr, 0,
+ PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_PAETH |
+ PNG_FILTER_UP | PNG_FILTER_AVG);
+ /* set the zlib compression level */
+ /*png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); */
+ png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
+
+ /* set other zlib parameters */
+ png_set_compression_mem_level(png_ptr, 8);
+ png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
+ png_set_compression_window_bits(png_ptr, 15);
+ png_set_compression_method(png_ptr, 8);
+
+ info_ptr->width = image->width;
+ info_ptr->height = image->height;
+ /*info_ptr->gamma=1.0; */
+ info_ptr->gamma = 0.5;
+ info_ptr->valid |= PNG_INFO_gAMA | PNG_INFO_pHYs;
+ info_ptr->x_pixels_per_unit = (png_uint_32) (100 / image->pixelwidth);
+ info_ptr->y_pixels_per_unit = (png_uint_32) (100 / image->pixelheight);
+
+
+ switch (image->palette->type) {
+ case C256:
+ {
+ int i;
+ info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+ info_ptr->bit_depth = image->bytesperpixel * 8;
+ info_ptr->palette = palette;
+ info_ptr->valid |= PNG_INFO_PLTE;
+ for (i = 0; i < image->palette->end; i++)
+ info_ptr->palette[i].red = image->palette->rgb[i][0],
+ info_ptr->palette[i].green = image->palette->rgb[i][1],
+ info_ptr->palette[i].blue = image->palette->rgb[i][2],
+ info_ptr->num_palette = image->palette->end;
+ }
+ break;
+ case SMALLITER:
+ case LARGEITER:
+ case GRAYSCALE:
+ info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
+ info_ptr->bit_depth = image->bytesperpixel * 8;
+ break;
+ case TRUECOLOR:
+ case TRUECOLOR24:
+ case TRUECOLOR16:
+ info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+ info_ptr->bit_depth = 8;
+ info_ptr->sig_bit.red = 8 - image->palette->info.truec.rprec;
+ info_ptr->sig_bit.green = 8 - image->palette->info.truec.gprec;
+ info_ptr->sig_bit.blue = 8 - image->palette->info.truec.bprec;
+ break;
+ }
+ info_ptr->interlace_type = 0;
+#ifdef _undefined_
+ png_set_text(png_ptr, info_ptr, comments,
+ sizeof(comments) / sizeof(png_text));
+#endif
+
+ png_write_info(png_ptr, info_ptr);
+ /*png_set_filler(png_ptr,0,PNG_FILLER_AFTER); */
+ png_set_packing(png_ptr);
+ if (image->palette->type & (TRUECOLOR | TRUECOLOR24 | TRUECOLOR16))
+ png_set_shift(png_ptr, &(info_ptr->sig_bit));
+ if (*b == 255)
+ png_set_swap(png_ptr);
+ png_set_bgr(png_ptr);
+ switch (image->palette->type) {
+ case C256:
+ case GRAYSCALE:
+ case SMALLITER:
+ case LARGEITER:
+#ifdef STRUECOLOR24
+ case TRUECOLOR24:
+ png_write_image(png_ptr, (png_bytepp) image->currlines);
+ break;
+#endif
+ case TRUECOLOR:
+ {
+ int i, y;
+ unsigned char *r = (unsigned char *) malloc(image->width * 3);
+ for (i = 0; i < image->height; i++) {
+ for (y = 0; y < image->width; y++)
+ r[y * 3 + 2] =
+ (((pixel32_t **) (image->
+ currlines))[i][y] &
+ image->palette->info.truec.rmask) >> image->
+ palette->info.truec.rshift, r[y * 3 + 1] =
+ (((pixel32_t **) (image->
+ currlines))[i][y] &
+ image->palette->info.truec.gmask) >> image->
+ palette->info.truec.gshift, r[y * 3] =
+ (((pixel32_t **) (image->
+ currlines))[i][y] &
+ image->palette->info.truec.bmask) >> image->
+ palette->info.truec.bshift;
+ png_write_rows(png_ptr, (png_bytepp) & r, 1);
+ }
+ }
+ break;
+#ifdef SUPPORT16
+ case TRUECOLOR16:
+ {
+ int i, y;
+ unsigned char *r = (unsigned char *) malloc(image->width * 3);
+ for (i = 0; i < image->height; i++) {
+ for (y = 0; y < image->width; y++)
+ r[y * 3 + 2] =
+ (((pixel16_t **) (image->
+ currlines))[i][y] &
+ image->palette->info.truec.rmask) >> image->
+ palette->info.truec.rshift, r[y * 3 + 1] =
+ (((pixel16_t **) (image->
+ currlines))[i][y] &
+ image->palette->info.truec.gmask) >> image->
+ palette->info.truec.gshift, r[y * 3] =
+ (((pixel16_t **) (image->
+ currlines))[i][y] &
+ image->palette->info.truec.bmask) >> image->
+ palette->info.truec.bshift;
+ png_write_rows(png_ptr, (png_bytepp) & r, 1);
+ }
+ }
+ break;
+#endif
+ }
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(file);
+ return NULL;
+}
+#endif
diff --git a/src/util/thread.c b/src/util/thread.c
new file mode 100644
index 0000000..f3ef4a7
--- /dev/null
+++ b/src/util/thread.c
@@ -0,0 +1,221 @@
+#ifndef _plan9_
+#include <signal.h>
+#include <stdio.h>
+#endif
+#include <xthread.h>
+#ifndef __BEOS__
+struct taskinfo definfo = {
+ 0,
+};
+
+#ifndef nthreads
+
+#define getrange1(range,ncpu) ((range)*(ncpu))/nthreads
+#define getrange2(range,ncpu) getrange1(range,(ncpu)+1)
+
+int ethreads = 0;
+int nthreads = 1;
+
+#ifdef USE_PTHREAD
+
+/* Well conde follows is probably very ugly, since this is
+ * my absolutely first application for threads. Please let
+ * me know how to improvie it
+ */
+
+static pthread_cond_t synccond, startcond;
+static struct taskinfo infos[MAXTHREADS];
+static pthread_mutex_t synccondmutex, startcondmutex;
+pthread_mutex_t semaphors[MAXSEMAPHORS];
+pthread_cond_t conds[MAXCONDS];
+
+/*This loop is executed whole time in slave threads.
+ Its function is following:
+ 1) wait for message
+ 2) call function from message
+ 3) syncronize
+ 4) again
+
+ To invoke this mechanizm main thread(#1) should call
+ xth_function
+ xth_synchronize forces forces thread to wait for others
+ */
+
+static int nfinished;
+static int npending;
+static int counter;
+static int bcounter;
+static int bcounter1;
+static int range;
+static void *data;
+static xfunction function;
+static void *control_routine(void *i)
+{
+ struct taskinfo *info = i;
+ int mycounter = 0;
+ int r;
+ void *d;
+ xfunction f;
+ while (1) {
+ /* quite a lot pthread calls. Please if you are
+ * reading this code and crying "OH NO!" so ugly
+ * handling! Why so much calls? Stop crying, I am
+ * newbie in threads. Please rewrite my code and
+ * send me better and faster version.
+ *
+ * This function comunicates with pth_function from main loop
+ * as follows: it uses startcond to wait for order start function!
+ * Counter is used to ensure that main function did not give
+ * order whie control_routine was busy
+ *
+ * after order is received, function adress is readed from global
+ * variables and started. Pth_function then executes its
+ * own part of calculation. After that it waits counter
+ * nfinished to reach number of thasks-1. This is done
+ * using cond synccond and synccondmutex. Quite complex
+ * but it seems to work. Looking forward for someone, who
+ * should reduce number of _lock/_unlock
+ */
+ pthread_mutex_lock(&synccondmutex);
+ nfinished++;
+ pthread_cond_signal(&synccond);
+ pthread_mutex_unlock(&synccondmutex);
+
+ pthread_mutex_lock(&startcondmutex);
+ while (mycounter >= counter) {
+ if (bcounter > bcounter1) {
+ /*Well we are already locked using start lock..should be OK */
+ mycounter--;
+ bcounter1++;
+ break;
+ }
+ pthread_cond_wait(&startcond, &startcondmutex);
+ }
+ r = range;
+ d = data;
+ f = function;
+ npending--;
+ pthread_mutex_unlock(&startcondmutex);
+ mycounter++;
+ f(d, info, getrange1(r, info->n), getrange2(r, info->n));
+ }
+ return NULL;
+}
+
+void pth_init(int nthreads1)
+{
+ int i;
+ pthread_attr_t attr;
+ if (ethreads)
+ return;
+
+ if (nthreads1 == 1 || nthreads1 == 0)
+ return; /*use nothreads_* calls */
+ if (nthreads1 > MAXTHREADS)
+ nthreads1 = MAXTHREADS;
+ nthreads = nthreads1;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if (pthread_cond_init(&synccond, NULL))
+ exit(1);
+ if (pthread_cond_init(&startcond, NULL))
+ exit(1);
+ if (pthread_mutex_init(&synccondmutex, NULL))
+ exit(1);
+ if (pthread_mutex_init(&startcondmutex, NULL))
+ exit(1);
+
+ infos[0].n = 0;
+ /*infos[0].id = pthread_self(); */
+
+ for (i = 0; i < MAXSEMAPHORS; i++) {
+ pthread_mutex_init(semaphors + i, NULL);
+ }
+ for (i = 0; i < MAXCONDS; i++) {
+ pthread_cond_init(conds + i, NULL);
+ }
+
+ nfinished = 0;
+ for (i = 0; i < nthreads - 1; i++) {
+ if (pthread_create
+ (&infos[i + 1].id, &attr, control_routine, infos + i + 1)) {
+ nthreads = i + 1;
+ break;
+ }
+ infos[i + 1].n = i + 1;
+ }
+ if (nthreads != 1)
+ ethreads = 1;
+}
+
+void pth_synchronize()
+{
+ /*Our job is done, synchronize now */
+ if (nfinished < nthreads - 1) {
+ pthread_mutex_lock(&synccondmutex);
+ while (nfinished < nthreads - 1) {
+ pthread_cond_wait(&synccond, &synccondmutex);
+ }
+ pthread_mutex_unlock(&synccondmutex);
+ }
+ /*Ok job is done, lets continue :) */
+}
+
+void pth_bgjob(xfunction f, void *d)
+{
+ pthread_mutex_lock(&startcondmutex);
+ if (npending) {
+ printf("Collision!\n"); /*FIXME:remove this..I just want to know how often this happends */
+ pthread_mutex_unlock(&startcondmutex);
+ f(d, infos, 0, 0);
+ }
+ if (bcounter < bcounter1) {
+ printf("Internal error\a\n");
+ }
+ if (!nfinished) {
+ pthread_mutex_unlock(&startcondmutex);
+ /*no more CPU available :( */
+ f(d, infos, 0, 0);
+ }
+ data = d;
+ range = 0;
+ function = f;
+ bcounter++;
+ nfinished--;
+ npending++;
+ pthread_cond_signal(&startcond);
+ pthread_mutex_unlock(&startcondmutex);
+}
+
+void pth_function(xfunction f, void *d, int r)
+{
+ pth_synchronize();
+ pthread_mutex_lock(&startcondmutex);
+ data = d;
+ range = r;
+ function = f;
+ /*And lets start it:) */
+ nfinished = 0;
+ npending = nthreads - 1;
+ counter++;
+ if (nthreads == 2)
+ pthread_cond_signal(&startcond);
+ else
+ pthread_cond_broadcast(&startcond);
+ pthread_mutex_unlock(&startcondmutex);
+
+ function(data, infos, getrange1(range, 0), getrange2(range, 0));
+}
+
+void pth_uninit()
+{
+ /*Should be empty for now since all threads will be killed after exit call */
+ /*FIXME should be added something if necessary :) */
+ nthreads = 1;
+ ethreads = 0;
+}
+#endif /*POSIX threads */
+#endif /*nthreads */
+#endif /*__BEOS__*/
diff --git a/src/util/timers.c b/src/util/timers.c
new file mode 100644
index 0000000..13e9abe
--- /dev/null
+++ b/src/util/timers.c
@@ -0,0 +1,670 @@
+/*
+ * XaoS, a fast portable realtime fractal zoomer
+ * Copyright (C) 1996,1997 by
+ *
+ * Jan Hubicka (hubicka@paru.cas.cz)
+ * Thomas Marsh (tmarsh@austin.ibm.com)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * All ugly architecture depended timing code is separated into this file..
+ */
+#ifdef _plan9_
+#include <u.h>
+#include <stdio.h>
+#include <libc.h>
+#else
+#include <config.h>
+#ifdef HAVE_GETTIMEOFDAY
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+ /*HAVE_UNISTD */
+#else
+#ifdef HAVE_FTIME
+#include <sys/timeb.h>
+#endif
+ /*HAVE_FTIME */
+#endif
+ /*HAVE_GETTIMEOFDAY */
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+ /*HAVE_TIME_H */
+#include <aconfig.h>
+#ifndef NO_MALLOC_H
+#include <malloc.h>
+#endif
+#include <stdio.h>
+#include <unistd.h>
+#ifndef _MAC
+#ifndef NO_MALLOC_H
+#include <malloc.h>
+#endif
+#endif
+#ifdef __BEOS__
+#include <OS.h>
+#endif
+#ifdef USE_ALLEGRO
+#include <allegro.h>
+#endif
+#ifdef HAVE_SETITIMER
+#include <signal.h>
+#endif
+#include <limits.h>
+#endif
+#ifndef _plan9_
+#include <assert.h>
+#endif
+#include "../include/timers.h"
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#ifdef _WIN32
+#define QuadPart(x) x.QuadPart
+#endif
+
+#ifndef SIGALRM
+#undef HAVE_SETITIMER
+#endif
+
+#define EMULDIV 1024
+struct timeemulator {
+ unsigned long int time;
+ unsigned long exact;
+};
+
+ /* Definition of structure timer. There is lots of various formats for time
+ * at variious platforms. So there is quite lots of ifdefs... same is for
+ * saving current time and function comparing them. Rest of timer lib
+ * is platform independent
+ */
+
+
+struct timer {
+#ifdef __BEOS__
+ bigtime_t lastactivated;
+#else
+#ifdef _WIN32
+ LARGE_INTEGER lastactivated;
+#else
+#ifdef USE_ALLEGRO
+ uclock_t lastactivated;
+#else
+#ifdef HAVE_UCLOCK
+ uclock_t lastactivated;
+#else
+#ifdef USE_CLOCK
+ int lastactivated;
+#else
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval lastactivated;
+#else
+#ifdef _plan9_
+ int lastactivated;
+#else
+#ifdef HAVE_FTIME
+ struct timeb lastactivated;
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+ unsigned long lastemulated;
+ struct timeemulator *emulator;
+ void (*handler) (void *);
+ void (*multihandler) (void *, int);
+ void *userdata;
+ struct timer *next, *previous, *group;
+ int interval;
+ int stopped;
+ int stoppedtime;
+ int slowdown;
+};
+
+ /*Variable for saving current time */
+#ifdef __BEOS__
+bigtime_t currenttime;
+#else
+#ifdef _WIN32
+LARGE_INTEGER currenttime, frequency;
+#else
+#ifdef USE_ALLEGRO
+int allegromode;
+#define TICKSPERSEC 100 /*must be divisor of 1000000 */
+volatile static int counter = -1;
+static int ainstalled;
+#endif
+#ifdef HAVE_UCLOCK
+static uclock_t currenttime;
+#else
+#ifdef USE_CLOCK
+static int currenttime;
+#else
+#ifdef HAVE_GETTIMEOFDAY
+static struct timeval currenttime;
+#ifdef AMIGA
+extern long __timezone;
+#define tzp __timezone
+#else
+static struct timezone tzp;
+#endif
+#else
+#ifdef HAVE_FTIME
+static struct timeb currenttime;
+#endif
+#endif
+#endif
+#endif
+#ifdef _plan9_
+static int currenttime;
+#endif
+#endif
+#endif
+
+#ifdef HAVE_SETITIMER
+static int registered = 0, reghandler = 0;
+static tl_group group2;
+#endif
+static tl_group group1;
+tl_group *syncgroup = &group1,
+#ifdef HAVE_SETITIMER
+ *asyncgroup = &group2;
+#else
+ *asyncgroup = &group1;
+#endif
+#ifdef _plan9_
+#ifdef _plan9v2_
+static int plan9_msec(void)
+{ /*this function was sent by Nigel Roles */
+ static int fd = -1;
+ char buf[20]; /* ish */
+ if (fd < 0)
+ fd = open("/dev/msec", OREAD);
+ else
+ seek(fd, 0, 0);
+ read(fd, buf, sizeof(buf));
+ return atoi(buf);
+}
+#else
+static int plan9_msec(void)
+{
+ return (int) (nsec() / 1000000);
+}
+#endif
+#endif
+
+#ifndef __BEOS__
+#ifndef _WIN32
+#ifndef HAVE_GETTIMEOFDAY
+#ifndef HAVE_FTIME
+#ifndef _plan9_
+#ifndef _MAC
+#error I am unable to get time in milisecond. Please edit timers.c and make tl_update_time and tl_lookup_timer to work for your architecture and send me then back(to hubicka@paru.cas.cz). You will need also define timers.h and change type of lasttime.
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+
+#ifdef USE_ALLEGRO
+static void timer(void)
+{
+ counter++;
+}
+
+END_OF_FUNCTION(timer);
+#endif
+/*following functions are architecture dependent */
+void tl_update_time(void)
+{
+#ifdef __BEOS__
+ currenttime = system_time();
+#else
+#ifdef _WIN32
+ QueryPerformanceCounter(&currenttime);
+#else
+#ifdef USE_ALLEGRO
+ if (allegromode) {
+ if (counter == -1) {
+ LOCK_VARIABLE(counter);
+ LOCK_FUNCTION(timer);
+ install_int(timer, 1000 / TICKSPERSEC);
+ ainstalled = 1;
+ counter = 0;
+ }
+ currenttime = counter;
+ return;
+ }
+#endif
+#ifdef HAVE_UCLOCK
+ currenttime = uclock();
+#else
+#ifdef USE_CLOCK
+ currenttime = clock();
+#else
+#ifdef HAVE_GETTIMEOFDAY
+ do {
+ gettimeofday(&currenttime, &tzp);
+ }
+ while (currenttime.tv_usec > 999999);
+#else
+#ifdef HAVE_FTIME
+ ftime(&currenttime);
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#ifdef _plan9_
+ currenttime = plan9_msec();
+#endif
+}
+
+static INLINE int __lookup_timer(tl_timer * t)
+{
+#ifdef __BEOS__
+ return (currenttime - t->lastactivated);
+#else
+#ifdef _WIN32
+ return ((QuadPart(currenttime) -
+ QuadPart(t->lastactivated)) * 1000000LL) /
+ QuadPart(frequency);
+#else
+#ifdef USE_ALLEGRO
+ if (allegromode)
+ return (((currenttime -
+ t->lastactivated) * (1000000LL / TICKSPERSEC)));
+#endif
+#ifdef HAVE_UCLOCK
+ return (((currenttime -
+ t->lastactivated) * 1000000LL) / UCLOCKS_PER_SEC);
+#else
+#ifdef USE_CLOCK
+ return ((currenttime -
+ t->lastactivated) * (1000000.0 / CLOCKS_PER_SEC));
+#else
+#ifdef HAVE_GETTIMEOFDAY
+ return ((1000000 *
+ (-(int) t->lastactivated.tv_sec + (int) currenttime.tv_sec) +
+ (-(int) t->lastactivated.tv_usec +
+ (int) currenttime.tv_usec)));
+#else
+#ifdef HAVE_FTIME
+ return ((1000000 * (-t->lastactivated.time + currenttime.time) +
+ 1000 * (-t->lastactivated.millitm + currenttime.millitm)));
+#else
+#ifdef _plan9_
+ return ((currenttime - t->lastactivated) * 1000);
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+}
+
+REGISTERS(3)
+int tl_lookup_timer(tl_timer * t)
+{
+ if (t->stopped) {
+ return (t->stoppedtime);
+ }
+ if (t->emulator != NULL) {
+ return ((int) (t->emulator->time - t->lastemulated) * EMULDIV);
+ }
+ return (__lookup_timer(t) - t->slowdown);
+}
+
+void tl_stop_timer(tl_timer * t)
+{
+ if (!t->stopped) {
+ t->stoppedtime = tl_lookup_timer(t);
+ t->stopped = 1;
+ }
+}
+
+void tl_slowdown_timer(tl_timer * t, int time)
+{
+ if (!t->stopped) {
+ t->slowdown += time;
+ } else
+ t->stoppedtime -= time;
+}
+
+void tl_resume_timer(tl_timer * t)
+{
+ if (t->stopped) {
+ t->stopped = 0;
+ t->slowdown = tl_lookup_timer(t) - t->stoppedtime;
+ }
+}
+
+void tl_sleep(int time)
+{
+#ifdef _WIN32
+ Sleep(time / 1000);
+#else
+#ifdef HAVE_USLEEP
+ usleep(time);
+#else
+#ifdef __BEOS__
+ snooze(time);
+#else
+#ifdef HAVE_SELECT
+ {
+ struct timeval tv;
+ tv.tv_sec = time / 1000000L;
+ tv.tv_usec = time % 1000000L;
+ (void) select(0, (void *) 0, (void *) 0, (void *) 0, &tv);
+ }
+#else
+#ifdef _plan9_
+ sleep(time / 1000);
+#else
+/*
+ #warning tl_sleep function not implemented. You may ignore this warning.
+ #warning xaos will work correctly. But on miltitasked enviroments it is
+ #warning HIGHLY recomended to implement this.
+ */
+#endif
+#endif
+#endif
+#endif
+#endif
+}
+
+REGISTERS(3)
+void tl_reset_timer(tl_timer * t)
+{
+ if (t->stopped)
+ t->stoppedtime = 0;
+ else {
+ if (t->emulator != NULL) {
+ t->lastemulated = t->emulator->time;
+ } else
+ t->lastactivated = currenttime, t->slowdown = 0;
+ }
+}
+
+tl_timer *tl_create_timer(void)
+{
+ tl_timer *timer;
+ timer = (tl_timer *) calloc(1, sizeof(tl_timer));
+ if (timer == NULL)
+ return NULL;
+ timer->interval = -1;
+ timer->handler = NULL;
+ timer->multihandler = NULL;
+ timer->userdata = NULL;
+ timer->next = NULL;
+ timer->previous = NULL;
+ timer->group = NULL;
+ timer->stopped = 0;
+ timer->stoppedtime = 0;
+ timer->slowdown = 0;
+ timer->emulator = NULL;
+ tl_reset_timer(timer);
+#ifdef _WIN32
+ QueryPerformanceFrequency(&frequency);
+#endif
+ return (timer);
+}
+
+tl_group *tl_create_group(void)
+{
+ tl_group *timer;
+ timer = (tl_group *) calloc(1, sizeof(tl_group));
+ if (timer == NULL)
+ return NULL;
+ timer->interval = -1;
+ timer->handler = NULL;
+ timer->multihandler = NULL;
+ timer->userdata = NULL;
+ timer->next = NULL;
+ timer->previous = NULL;
+ timer->group = timer;
+ tl_reset_timer(timer);
+ return (timer);
+}
+
+
+void tl_free_timer(tl_timer * timer)
+{
+ if (timer->group)
+ tl_remove_timer(timer);
+ free((void *) timer);
+}
+
+void tl_free_group(tl_group * timer)
+{
+ tl_timer *next;
+ do {
+ next = timer->next;
+ free((void *) timer);
+ }
+ while (next != NULL);
+}
+
+int tl_process_group(tl_group * group, int *activated)
+{
+ int again = 1;
+ tl_timer *timer, *timer1;
+ int minwait = INT_MAX;
+ tl_update_time();
+ if (activated != NULL)
+ *activated = 0;
+ while (again) {
+ group->slowdown = 0;
+ again = 0;
+ minwait = INT_MAX;
+ timer = group->next;
+ while (timer != NULL) {
+ timer1 = timer->next;
+ if (timer->handler && timer->interval >= 0) {
+ int time = timer->interval - tl_lookup_timer(timer);
+ if (time < 500) {
+ if (activated != NULL)
+ (*activated)++;
+ again = 1;
+ tl_reset_timer(timer);
+ if (time < -200 * 1000000)
+ time = 0; /*underflow? */
+ tl_slowdown_timer(timer, time);
+ time = timer->interval + time;
+ timer->handler(timer->userdata);
+ tl_update_time();
+ }
+ if (time < minwait)
+ minwait = time;
+ } else if (timer->multihandler && timer->interval > 0) {
+ int time = timer->interval - tl_lookup_timer(timer);
+ if (time < 500) {
+ int n;
+ if (activated != NULL)
+ (*activated)++;
+ tl_reset_timer(timer);
+ if (time < -200 * 1000000)
+ time = 0; /*underflow? */
+ n = -(time + 500) / timer->interval + 1;
+ time = timer->interval * n + time;
+ tl_slowdown_timer(timer,
+ time - timer->interval +
+ n * timer->interval);
+ timer->multihandler(timer->userdata, n);
+ tl_update_time();
+ }
+ if (time < minwait)
+ minwait = time;
+ }
+ if (group->slowdown) {
+ again = 1;
+ break;
+ }
+ timer = timer1;
+ }
+ }
+ if (minwait != INT_MAX) {
+ if (minwait < 0)
+ return (0);
+ return (minwait);
+ }
+ return (-1);
+}
+
+#ifdef HAVE_SETITIMER
+static void update_async(void);
+static void alarmhandler(int a)
+{
+ update_async();
+ signal(SIGALRM, alarmhandler);
+}
+
+static void update_async(void)
+{
+ int time = tl_process_group(asyncgroup, NULL);
+ if (time != -1) {
+ struct itimerval t;
+ t.it_interval.tv_sec = 0;
+ t.it_interval.tv_usec = 0;
+ t.it_value.tv_sec = time / 1000000;
+ t.it_value.tv_usec = time % 1000000;
+ if (!reghandler) {
+ signal(SIGALRM, alarmhandler), reghandler = 1;
+ }
+ setitimer(ITIMER_REAL, &t, &t);
+ registered = 1;
+ } else if (registered) {
+ struct itimerval t;
+ t.it_interval.tv_sec = 0;
+ t.it_interval.tv_usec = 0;
+ t.it_value.tv_sec = 0;
+ t.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &t, &t);
+ registered = 0;
+ }
+}
+
+#else
+#define update_async()
+#endif
+void tl_add_timer(tl_group * group, tl_timer * timer)
+{
+ if (timer->group)
+ tl_remove_timer(timer);
+ timer->previous = group;
+ timer->next = group->next;
+ timer->group = group;
+ group->next = timer;
+ if (timer->next != NULL)
+ timer->next->previous = timer;
+ if (timer->group == asyncgroup)
+ update_async();
+}
+
+void tl_set_interval(tl_timer * timer, int interval)
+{
+ if (timer->interval <= 0) {
+ tl_reset_timer(timer);
+ }
+ timer->interval = interval;
+ if (timer->group == asyncgroup)
+ update_async();
+}
+
+void tl_set_handler(tl_timer * timer, void (*handler) (void *), void *ud)
+{
+ timer->handler = handler;
+ timer->userdata = ud;
+ if (timer->group == asyncgroup)
+ update_async();
+}
+
+void
+tl_set_multihandler(tl_timer * timer, void (*handler) (void *, int),
+ void *ud)
+{
+ timer->multihandler = handler;
+ timer->userdata = ud;
+ if (timer->group == asyncgroup)
+ update_async();
+}
+
+void tl_remove_timer(tl_timer * timer)
+{
+ tl_group *g = timer->group;
+ timer->group->slowdown = 1;
+ timer->previous->next = timer->next;
+ if (timer->next != NULL)
+ timer->next->previous = timer->previous;
+ timer->group = NULL;
+ if (g == asyncgroup)
+ update_async();
+}
+
+struct timeemulator *tl_create_emulator(void)
+{
+ return ((struct timeemulator *)
+ calloc(1, sizeof(struct timeemulator)));
+}
+
+void tl_free_emulator(struct timeemulator *t)
+{
+ free(t);
+}
+
+void tl_elpased(struct timeemulator *t, int elpased)
+{
+ t->exact += elpased;
+ t->time += t->exact / EMULDIV;
+ t->exact &= (EMULDIV - 1);
+}
+
+void tl_emulate_timer(struct timer *t, struct timeemulator *e)
+{
+ int time = tl_lookup_timer(t);
+ t->emulator = e;
+ t->lastemulated = e->time;
+ tl_slowdown_timer(t, -time);
+}
+
+void tl_unemulate_timer(struct timer *t)
+{
+ int time = tl_lookup_timer(t);
+ t->emulator = NULL;
+ tl_slowdown_timer(t, tl_lookup_timer(t) - time);
+}
+
+#ifdef USE_ALLEGRO
+void tl_allegromode(int mode)
+{
+ allegromode = mode;
+ if (!allegromode && ainstalled) {
+ remove_int(timer);
+ ainstalled = 0;
+ counter = -1;
+ }
+}
+#endif
diff --git a/src/util/util.pri b/src/util/util.pri
new file mode 100644
index 0000000..7db9d98
--- /dev/null
+++ b/src/util/util.pri
@@ -0,0 +1,12 @@
+SOURCES += \
+ $$PWD/png.c \
+ $$PWD/catalog.c \
+ $$PWD/thread.c \
+ $$PWD/xstring.c \
+ $$PWD/help.c \
+ $$PWD/xerror.c \
+ $$PWD/xshl.c \
+ $$PWD/xldio.c \
+ $$PWD/xstdio.c \
+ $$PWD/xmenu.c \
+ $$PWD/timers.c
diff --git a/src/util/xerror.c b/src/util/xerror.c
new file mode 100644
index 0000000..f7a904f
--- /dev/null
+++ b/src/util/xerror.c
@@ -0,0 +1,43 @@
+#ifdef _plan9_
+#include <u.h>
+#include <libc.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "config.h"
+#include "xerror.h"
+#include "aconfig.h"
+/*BeOS driver have its own routines in the C++ code */
+#ifndef BEOS_DRIVER
+/*On windows we use message boxes done in the ui_win32.c code*/
+#ifndef WIN32_DRIVER
+void x_message(CONST char *text, ...)
+{
+ va_list ap;
+ va_start(ap, text);
+ vfprintf(stdout, text, ap);
+ fprintf(stdout, "\n");
+ va_end(ap);
+}
+
+void x_error(CONST char *text, ...)
+{
+ va_list ap;
+ va_start(ap, text);
+ vfprintf(stderr, text, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+void x_fatalerror(CONST char *text, ...)
+{
+ va_list ap;
+ va_start(ap, text);
+ vfprintf(stderr, text, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+#endif
+#endif
diff --git a/src/util/xldio.c b/src/util/xldio.c
new file mode 100644
index 0000000..ab40058
--- /dev/null
+++ b/src/util/xldio.c
@@ -0,0 +1,603 @@
+/* This file contain long double I/O routines for Windows (because Windows API
+ don't support long double at all.
+
+ They don't work on other architectures. So be curefull. */
+
+
+/* This source comes from the DJGPP runtime library. It has been hacked
+ to work with XaoS */
+/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
+#include <config.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <xio.h>
+/*#include <libc/unconst.h>*/
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <xldio.h>
+#ifdef USE_XLDIO
+
+static long double powten[] = {
+ 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, 1e64L, 1e128L, 1e256L,
+ 1e512L, 1e1024L, 1e2048L, 1e4096L
+};
+
+long double x_strtold(CONST char *s, CONST char **sret)
+{
+ long double r; /* result */
+ int e, ne; /* exponent */
+ int sign; /* +- 1.0 */
+ int esign;
+ int flags = 0;
+ int l2powm1;
+
+ r = 0.0L;
+ sign = 1;
+ e = ne = 0;
+ esign = 1;
+
+ while (*s && isspace(*s))
+ s++;
+
+ if (*s == '+')
+ s++;
+ else if (*s == '-') {
+ sign = -1;
+ s++;
+ }
+
+ while ((*s >= '0') && (*s <= '9')) {
+ flags |= 1;
+ r *= 10.0L;
+ r += *s - '0';
+ s++;
+ }
+
+ if (*s == '.') {
+ s++;
+ while ((*s >= '0') && (*s <= '9')) {
+ flags |= 2;
+ r *= 10.0L;
+ r += *s - '0';
+ s++;
+ ne++;
+ }
+ }
+ if (flags == 0) {
+ if (sret)
+ *sret = /*unconst(s, char *) */ s;
+ return 0.0L;
+ }
+
+ if ((*s == 'e') || (*s == 'E')) {
+ s++;
+ if (*s == '+')
+ s++;
+ else if (*s == '-') {
+ s++;
+ esign = -1;
+ }
+ while ((*s >= '0') && (*s <= '9')) {
+ e *= 10;
+ e += *s - '0';
+ s++;
+ }
+ }
+ if (esign < 0) {
+ esign = -esign;
+ e = -e;
+ }
+ e = e - ne;
+ if (e < -4096) {
+ /* possibly subnormal number, 10^e would overflow */
+ r *= 1.0e-2048L;
+ e += 2048;
+ }
+ if (e < 0) {
+ e = -e;
+ esign = -esign;
+ }
+ if (e >= 8192)
+ e = 8191;
+ if (e) {
+ long double d = 1.0L;
+ l2powm1 = 0;
+ while (e) {
+ if (e & 1)
+ d *= powten[l2powm1];
+ e >>= 1;
+ l2powm1++;
+ }
+ if (esign > 0)
+ r *= d;
+ else
+ r /= d;
+ }
+ if (sret)
+ *sret = /*unconst(s, char *) */ s;
+ return r * sign;
+}
+
+#if 0
+main()
+{
+ printf("%E", (float) x_strtold("1.4E15", NULL));
+}
+#endif
+
+
+#define MAXEXPLD 4952 /* this includes subnormal numbers */
+static int is_nan = 0;
+static char decimal = '.';
+static long double pten[] = {
+ 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, 1e64L, 1e128L, 1e256L,
+ 1e512L, 1e1024L, 1e2048L, 1e4096L
+};
+
+static long double ptenneg[] = {
+ 1e-1L, 1e-2L, 1e-4L, 1e-8L, 1e-16L, 1e-32L, 1e-64L, 1e-128L, 1e-256L,
+ 1e-512L, 1e-1024L, 1e-2048L, 1e-4096L
+};
+
+static inline char tochar(int n)
+{
+ if (n >= 9)
+ return '9';
+ if (n <= 0)
+ return '0';
+ return n + '0';
+}
+
+static inline int todigit(char c)
+{
+ if (c <= '0')
+ return 0;
+ if (c >= '9')
+ return 9;
+ return c - '0';
+}
+
+#define LONGINT 0x01 /* long integer */
+#define LONGDBL 0x02 /* long double */
+#define SHORTINT 0x04 /* short integer */
+#define ALT 0x08 /* alternate form */
+#define LADJUST 0x10 /* left adjustment */
+#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */
+#define HEXPREFIX 0x40 /* add 0x or 0X prefix */
+
+#define MAXP 4096
+#define NP 12
+#define P (4294967296.0L * 4294967296.0L * 2.0L) /* 2^65 */
+static long double INVPREC = P;
+static long double PREC = 1.0L / P;
+#undef P
+/*
+ * Defining FAST_LDOUBLE_CONVERSION results in a little bit faster
+ * version, which might be less accurate (about 1 bit) for long
+ * double. For 'normal' double it doesn't matter.
+ */
+/* #define FAST_LDOUBLE_CONVERSION */
+#if 1
+#define modfl mymodfl
+inline long double m_floor(long double x)
+{
+ register long double __value;
+ volatile unsigned short int __cw, __cwtmp;
+
+ asm volatile ("fnstcw %0":"=m" (__cw));
+ __cwtmp = (__cw & 0xf3ff) | 0x0400; /* rounding down */
+ asm volatile ("fldcw %0"::"m" (__cwtmp));
+ asm volatile ("frndint":"=t" (__value):"0"(x));
+ asm volatile ("fldcw %0"::"m" (__cw));
+
+ return __value;
+
+}
+
+static inline long double mymodfl(long double x, long double *pint)
+{
+ /*int p=(int) x; */
+ long double p = m_floor(x);
+ long double frac = x - p;
+ if (x < 0)
+ p = p + 1, frac = frac - 1;
+ *pint = p;
+ return frac;
+}
+#endif
+static char *exponentl(char *p, int expv, unsigned char fmtch)
+{
+ char *t;
+ char expbuf[MAXEXPLD];
+
+ *p++ = fmtch;
+ if (expv < 0) {
+ expv = -expv;
+ *p++ = '-';
+ } else
+ *p++ = '+';
+ t = expbuf + MAXEXPLD;
+ if (expv > 9) {
+ do {
+ *--t = tochar(expv % 10);
+ }
+ while ((expv /= 10) > 9);
+ *--t = tochar(expv);
+ for (; t < expbuf + MAXEXPLD; *p++ = *t++);
+ } else {
+ *p++ = '0';
+ *p++ = tochar(expv);
+ }
+ return p;
+}
+
+static int isspeciall(long double d, char *bufp)
+{
+ struct IEEExp {
+ unsigned manl:32;
+ unsigned manh:32;
+ unsigned exp:15;
+ unsigned sign:1;
+ } *ip = (struct IEEExp *) &d;
+
+ is_nan = 0; /* don't assume the static is 0 (emacs) */
+ if (ip->exp != 0x7fff)
+ return (0);
+ if ((ip->manh & 0x7fffffff) || ip->manl) {
+ strcpy(bufp, "NaN");
+ is_nan = ip->sign ? -1 : 1; /* kludge: we don't need the sign, it's not nice
+ but it should work */
+ } else
+ (void) strcpy(bufp, "Inf");
+ return (3);
+}
+
+static char *my_roundl(long double fract, int *expv, char *start,
+ char *end, char ch, char *signp)
+{
+ long double tmp;
+
+ if (fract) {
+ if (fract == 0.5L) {
+ char *e = end;
+ if (*e == '.')
+ e--;
+ if (*e == '0' || *e == '2' || *e == '4' || *e == '6'
+ || *e == '8') {
+ tmp = 3.0;
+ goto start;
+ }
+ }
+ (void) modfl(fract * 10.0L, &tmp);
+ } else
+ tmp = todigit(ch);
+ start:
+ if (tmp > 4)
+ for (;; --end) {
+ if (*end == decimal)
+ --end;
+ if (++*end <= '9')
+ break;
+ *end = '0';
+ if (end == start) {
+ if (expv) { /* e/E; increment exponent */
+ *end = '1';
+ ++*expv;
+ } else { /* f; add extra digit */
+ *--end = '1';
+ --start;
+ }
+ break;
+ }
+ }
+ /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
+ else if (*signp == '-')
+ for (;; --end) {
+ if (*end == decimal)
+ --end;
+ if (*end != '0')
+ break;
+ if (end == start)
+ *signp = 0;
+ }
+ return start;
+}
+
+
+static int
+cvtl(long double number, int prec, int flags, char *signp,
+ unsigned char fmtch, char *startp, char *endp)
+{
+ char *p, *t;
+ long double fract = 0;
+ int dotrim, expcnt, gformat;
+ int doextradps = 0; /* Do extra decimal places if the precision needs it */
+ int doingzero = 0; /* We're displaying 0.0 */
+ long double integer, tmp;
+
+ if ((expcnt = isspeciall(number, startp)))
+ return (expcnt);
+
+ dotrim = expcnt = gformat = 0;
+ /* fract = modfl(number, &integer); */
+ integer = number;
+
+ /* get an extra slot for rounding. */
+ t = ++startp;
+
+ p = endp - 1;
+ if (integer) {
+ int i, lp = NP, pt = MAXP;
+#ifndef FAST_LDOUBLE_CONVERSION
+ long double oint = integer, dd = 1.0L;
+#endif
+ if (integer > INVPREC) {
+ integer *= PREC;
+ while (lp >= 0) {
+ if (integer >= pten[lp]) {
+ expcnt += pt;
+ integer *= ptenneg[lp];
+#ifndef FAST_LDOUBLE_CONVERSION
+ dd *= pten[lp];
+#endif
+ }
+ pt >>= 1;
+ lp--;
+ }
+#ifndef FAST_LDOUBLE_CONVERSION
+ integer = oint / dd;
+#else
+ integer *= INVPREC;
+#endif
+ }
+ /*
+ * Do we really need this ?
+ */
+ for (i = 0; i < expcnt; i++)
+ *p-- = '0';
+ }
+ number = integer;
+ fract = modfl(number, &integer);
+ /* If integer is zero then we need to look at where the sig figs are */
+ if (integer < 1) {
+ /* If fract is zero the zero before the decimal point is a sig fig */
+ if (fract == 0.0)
+ doingzero = 1;
+ /* If fract is non-zero all sig figs are in fractional part */
+ else
+ doextradps = 1;
+ }
+ /*
+ * get integer portion of number; put into the end of the buffer; the
+ * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
+ */
+ for (; integer; ++expcnt) {
+ tmp = modfl(integer * 0.1L, &integer);
+ *p-- = tochar((int) ((tmp + .01L) * 10));
+ }
+ switch (fmtch) {
+ case 'f':
+ /* reverse integer into beginning of buffer */
+ if (expcnt)
+ for (; ++p < endp; *t++ = *p);
+ else
+ *t++ = '0';
+ /*
+ * if precision required or alternate flag set, add in a
+ * decimal point.
+ */
+ if (prec || flags & ALT)
+ *t++ = decimal;
+ /* if requires more precision and some fraction left */
+ if (fract) {
+ if (prec)
+ do {
+ fract = modfl(fract * 10.0L, &tmp);
+ *t++ = tochar((int) tmp);
+ }
+ while (--prec && fract);
+ if (fract)
+ startp = my_roundl(fract, (int *) NULL, startp,
+ t - 1, (char) 0, signp);
+ }
+ for (; prec--; *t++ = '0');
+ break;
+ case 'e':
+ case 'E':
+ eformat:
+ if (expcnt) {
+ *t++ = *++p;
+ if (prec || flags & ALT)
+ *t++ = decimal;
+ /* if requires more precision and some integer left */
+ for (; prec && ++p < endp; --prec)
+ *t++ = *p;
+ /*
+ * if done precision and more of the integer component,
+ * round using it; adjust fract so we don't re-round
+ * later.
+ */
+ if (!prec && ++p < endp) {
+ fract = 0;
+ startp = my_roundl((long double) 0.0L, &expcnt,
+ startp, t - 1, *p, signp);
+ }
+ /* adjust expcnt for digit in front of decimal */
+ --expcnt;
+ }
+ /* until first fractional digit, decrement exponent */
+ else if (fract) {
+ int lp = NP, pt = MAXP;
+#ifndef FAST_LDOUBLE_CONVERSION
+ long double ofract = fract, dd = 1.0L;
+#endif
+ expcnt = -1;
+ if (fract < PREC) {
+ fract *= INVPREC;
+ while (lp >= 0) {
+ if (fract <= ptenneg[lp]) {
+ expcnt -= pt;
+ fract *= pten[lp];
+#ifndef FAST_LDOUBLE_CONVERSION
+ dd *= pten[lp];
+#endif
+ }
+ pt >>= 1;
+ lp--;
+ }
+#ifndef FAST_LDOUBLE_CONVERSION
+ fract = ofract * dd;
+#else
+ fract *= PREC;
+#endif
+ }
+ /* adjust expcnt for digit in front of decimal */
+ for ( /* expcnt = -1 */ ;; --expcnt) {
+ fract = modfl(fract * 10.0L, &tmp);
+ if (tmp)
+ break;
+ }
+ *t++ = tochar((int) tmp);
+ if (prec || flags & ALT)
+ *t++ = decimal;
+ } else {
+ *t++ = '0';
+ if (prec || flags & ALT)
+ *t++ = decimal;
+ }
+ /* if requires more precision and some fraction left */
+ if (fract) {
+ if (prec)
+ do {
+ fract = modfl(fract * 10.0L, &tmp);
+ *t++ = tochar((int) tmp);
+ }
+ while (--prec && fract);
+ if (fract)
+ startp =
+ my_roundl(fract, &expcnt, startp, t - 1, (char) 0,
+ signp);
+ }
+ /* if requires more precision */
+ for (; prec--; *t++ = '0');
+
+ /* unless alternate flag, trim any g/G format trailing 0's */
+ if (gformat && !(flags & ALT)) {
+ while (t > startp && *--t == '0');
+ if (*t == decimal)
+ --t;
+ ++t;
+ }
+ t = exponentl(t, expcnt, fmtch);
+ break;
+ case 'g':
+ case 'G':
+ if (prec) {
+ /* If doing zero and precision is greater than 0 count the
+ * 0 before the decimal place */
+ if (doingzero)
+ --prec;
+ } else {
+ /* a precision of 0 is treated as precision of 1 unless doing zero */
+ if (!doingzero)
+ ++prec;
+ }
+ /*
+ * ``The style used depends on the value converted; style e
+ * will be used only if the exponent resulting from the
+ * conversion is less than -4 or greater than the precision.''
+ * -- ANSI X3J11
+ */
+ if (expcnt > prec || (!expcnt && fract && fract < .0001)) {
+ /*
+ * g/G format counts "significant digits, not digits of
+ * precision; for the e/E format, this just causes an
+ * off-by-one problem, i.e. g/G considers the digit
+ * before the decimal point significant and e/E doesn't
+ * count it as precision.
+ */
+ --prec;
+ fmtch -= 2; /* G->E, g->e */
+ gformat = 1;
+ goto eformat;
+ }
+ /*
+ * reverse integer into beginning of buffer,
+ * note, decrement precision
+ */
+ if (expcnt)
+ for (; ++p < endp; *t++ = *p, --prec);
+ else
+ *t++ = '0';
+ /*
+ * if precision required or alternate flag set, add in a
+ * decimal point. If no digits yet, add in leading 0.
+ */
+ if (prec || flags & ALT) {
+ dotrim = 1;
+ *t++ = decimal;
+ } else
+ dotrim = 0;
+ /* if requires more precision and some fraction left */
+ while (prec && fract) {
+ fract = modfl(fract * 10.0L, &tmp);
+ *t++ = tochar((int) tmp);
+ /* If we're not adding 0s
+ * or we are but they're sig figs:
+ * decrement the precision */
+ if ((doextradps != 1) || ((int) tmp != 0)) {
+ doextradps = 0;
+ prec--;
+ }
+ }
+ if (fract)
+ startp =
+ my_roundl(fract, (int *) NULL, startp, t - 1, (char) 0,
+ signp);
+ /* alternate format, adds 0's for precision, else trim 0's */
+ if (flags & ALT)
+ for (; prec--; *t++ = '0');
+ else if (dotrim) {
+ while (t > startp && *--t == '0');
+ if (*t != decimal)
+ ++t;
+ }
+ }
+ return t - startp;
+}
+
+#if 0
+main()
+{
+ static char buf[4096];
+ int i;
+ cvtl(0.00000000000000000005, 4, 0, &softsign, 'G', buf,
+ buf + sizeof(buf) - 1);
+ printf("%s\n", buf + 1);
+ printf("%.30LG\n", (long double) 234236723234234231235324.47239L);
+}
+
+#endif
+void x_ldout(long double param, int prec, xio_file stream)
+{
+ static char buf[4095];
+ char softsign = 0;
+ int l;
+ if (param < 0)
+ xio_putc('-', stream), param = -param;
+ l = cvtl(param, prec, 0, &softsign, 'G', buf, buf + sizeof(buf));
+ /*printf("a:%s %i\n",buf+1, prec); */
+ buf[l + 2] = 0;
+ l = strlen(buf + 1);
+ if (buf[l] == '.')
+ buf[l] = 0;
+ /*printf("b:%s %i\n",buf+1, prec); */
+ xio_puts(buf + 1, stream);
+}
+#endif
diff --git a/src/util/xmenu.c b/src/util/xmenu.c
new file mode 100644
index 0000000..8a70b5d
--- /dev/null
+++ b/src/util/xmenu.c
@@ -0,0 +1,998 @@
+#ifdef _plan9_
+#include <u.h>
+#include <libc.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#endif
+#include <config.h>
+#include <filter.h>
+#include <fractal.h>
+#include <ui_helper.h>
+#include <xerror.h>
+#include <xldio.h>
+#include <misc-f.h>
+#include "config.h"
+#include "xmenu.h"
+
+#define HASHBITS 8
+#define HASHSIZE (1<<HASHBITS)
+#define HASHMASK (HASHSIZE-1)
+#define HASH(c,len) (((len)*32+(c)[0]+(c)[(len)-1])&HASHMASK)
+
+static struct queuelist {
+ struct queuelist *next;
+ struct queuelist *previous;
+ CONST menuitem *item;
+ dialogparam *d;
+} *firstqueue = NULL, *lastqueue = NULL;
+static struct entry {
+ struct entry *next;
+ struct entry *previous;
+ struct entry *nextname;
+ CONST menuitem *item;
+} *firstitem = NULL, *lastitem = NULL;
+
+struct entry *namehash[HASHSIZE];
+
+static void
+x_menu_insert(CONST menuitem * item, struct entry *iitem, int n)
+{
+ int i;
+ int len;
+ int hashpos;
+ struct entry *list;
+ for (i = 0; i < n; i++) {
+ list = (struct entry *) calloc(1, sizeof(struct queuelist));
+ if (list == NULL) {
+ x_error("Warning:out of memory!");
+ return;
+ }
+ if (item->type != MENU_SEPARATOR) {
+ len = strlen(item->shortname);
+ hashpos = HASH(item->shortname, len);
+ list->nextname = namehash[hashpos];
+#ifdef DEBUG
+ {
+ struct entry *e = list->nextname;
+ while (e != NULL) {
+ if (e->item->type != MENU_SUBMENU
+ && e->item->type != MENU_SEPARATOR
+ && item->type != MENU_SEPARATOR);
+ if (!strcmp(e->item->shortname, item->shortname)
+ /*&& e->item->type != MENU_SUBMENU && item->type != MENU_SUBMENU */
+ ) {
+ x_error
+ ("Menu error:Name collision %s:'%s'(%s) and '%s'(%s)",
+ item->shortname, item->name, item->menuname,
+ e->item->name, e->item->menuname);
+ }
+ e = e->nextname;
+ }
+ }
+#endif
+ namehash[hashpos] = list;
+ }
+ list->item = item;
+ if (iitem == NULL) {
+ /*printf("ahoj\n"); */
+ list->previous = lastitem;
+ list->next = NULL;
+ if (lastitem != NULL)
+ lastitem->next = list;
+ else
+ firstitem = list;
+ lastitem = list;
+ } else {
+ list->next = iitem;
+ list->previous = iitem->previous;
+ if (iitem->previous)
+ iitem->previous->next = list;
+ else
+ firstitem = list;
+ iitem->previous = list;
+ }
+ item++;
+ }
+}
+
+void menu_add(CONST menuitem * item, int n)
+{
+ x_menu_insert(item, NULL, n);
+}
+
+void menu_insert(CONST menuitem * item, CONST char *before, int n)
+{
+ struct entry *e = firstitem;
+ while (e != NULL) {
+ if (!strcmp(e->item->shortname, before))
+ break;
+ e = e->next;
+ }
+ x_menu_insert(item, e, n);
+}
+
+void menu_delete(CONST menuitem * items, int n)
+{
+ int d = 0, i;
+ struct entry *item = firstitem;
+ struct entry *pe;
+ int hashpos;
+ for (i = 0; i < n; i++) {
+ if (items[i].type == MENU_SEPARATOR) {
+ struct entry *item = firstitem;
+ while (item && item->item != items + i)
+ item = item->next;
+ if (!item)
+ abort();
+ if (item->previous != NULL)
+ item->previous->next = item->next;
+ else
+ firstitem = item->next;
+ if (item->next != NULL)
+ item->next->previous = item->previous;
+ else
+ lastitem = item->previous;
+ free(item);
+ } else {
+ int len = strlen(items[i].shortname);
+ hashpos = HASH(items[i].shortname, len);
+ pe = NULL;
+ item = namehash[hashpos];
+ while (item != NULL) {
+ if (items + i == item->item) {
+ d++;
+ if (pe == NULL)
+ namehash[hashpos] = item->nextname;
+ else
+ pe->nextname = item->nextname;
+ if (item->previous != NULL)
+ item->previous->next = item->next;
+ else
+ firstitem = item->next;
+ if (item->next != NULL)
+ item->next->previous = item->previous;
+ else
+ lastitem = item->previous;
+ free(item);
+ break;
+ } /*if */
+ pe = item;
+ item = item->nextname;
+ } /*while */
+ }
+#ifdef DEBUG
+ if (item == NULL)
+ x_error("Item %s not found!", items[i].shortname);
+#endif
+ } /*for */
+}
+
+void menu_addqueue(CONST menuitem * item, dialogparam * d)
+{
+ struct queuelist *list;
+ list = (struct queuelist *) calloc(1, sizeof(struct queuelist));
+ if (list == NULL) {
+ x_error("Warning:out of memory!");
+ return;
+ }
+ list->previous = lastqueue;
+ list->next = NULL;
+ list->item = item;
+ list->d = d;
+ if (lastqueue != NULL)
+ lastqueue->next = list;
+ else
+ firstqueue = list;
+ lastqueue = list;
+}
+
+CONST menuitem *menu_delqueue(dialogparam ** d)
+{
+ CONST struct menuitem *item;
+ struct queuelist *list = firstqueue;
+ if (firstqueue == NULL)
+ return NULL;
+ item = firstqueue->item;
+ *d = firstqueue->d;
+ firstqueue = list->next;
+ if (list->next != NULL)
+ list->next->previous = NULL;
+ else
+ lastqueue = NULL;
+ free(list);
+ return (item);
+}
+
+CONST static void *menu_rfind(CONST void
+ *(*function) (struct entry * item),
+ CONST char *root)
+{
+ struct entry *item = firstitem;
+ CONST void *r;
+ while (item != NULL) {
+ if (!strcmp(root, item->item->menuname)) {
+ if ((r = function(item)) != NULL)
+ return r;
+ if (item->item->type == MENU_SUBMENU
+ && (r =
+ menu_rfind(function, item->item->shortname)) != NULL)
+ return r;
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+CONST static char *findkey;
+CONST static void *cmpfunction(struct entry *item)
+{
+ if (item->item->key == NULL)
+ return NULL;
+ if (!strcmp(findkey, item->item->key))
+ return item->item;
+ return NULL;
+}
+
+CONST menuitem *menu_findkey(CONST char *key, CONST char *root)
+{
+ findkey = key;
+ return ((CONST menuitem *) menu_rfind(cmpfunction, root));
+}
+
+static CONST menuitem *finditem;
+CONST static void *cmpfunction2(struct entry *item)
+{
+ if (item->item == finditem)
+ return item;
+ return NULL;
+}
+
+int menu_available(CONST menuitem * item, CONST char *root)
+{
+ finditem = item;
+ return (menu_rfind(cmpfunction2, root) != NULL);
+}
+
+CONST char *menu_fullname(CONST char *menu)
+{
+ struct entry *item = firstitem;
+ while (item != NULL) {
+ if (item->item->type == MENU_SUBMENU
+ && !strcmp(menu, item->item->shortname)) {
+ return (item->item->name);
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+CONST menuitem *menu_item(CONST char *menu, int n)
+{
+ struct entry *item = firstitem;
+ while (item != NULL) {
+ if (!strcmp(menu, item->item->menuname)) {
+ if (!(item->item->flags & MENUFLAG_NOMENU))
+ n--;
+ if (n < 0)
+ return (item->item);
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+static CONST menuitem *menu_item2(CONST char *menu, int n)
+{
+ struct entry *item = firstitem;
+ while (item != NULL) {
+ if (!strcmp(menu, item->item->menuname)) {
+ n--;
+ if (n < 0)
+ return (item->item);
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+int menu_havedialog(CONST menuitem * item, struct uih_context *c)
+{
+ if (item->type != MENU_DIALOG && item->type != MENU_CUSTOMDIALOG)
+ return 0;
+ if (!(item->type & MENUFLAG_RADIO) || c == NULL)
+ return 1;
+ if (item->flags & MENUFLAG_DIALOGATDISABLE)
+ return (menu_enabled(item, c));
+ return (!menu_enabled(item, c));
+}
+
+static void menu_freeparam(dialogparam * d, CONST struct dialog *di)
+{
+ switch (di->type) {
+ case DIALOG_STRING:
+ case DIALOG_KEYSTRING:
+ case DIALOG_IFILE:
+ case DIALOG_OFILE:
+ free(d->dstring);
+ }
+}
+
+void
+menu_destroydialog(CONST menuitem * item, dialogparam * d,
+ struct uih_context *uih)
+{
+ int i;
+ CONST struct dialog *di = menu_getdialog(uih, item);
+ for (i = 0; di[i].question; i++) {
+ menu_freeparam(d + i, di + i);
+ }
+ free(d);
+
+}
+
+void
+menu_activate(CONST menuitem * item, struct uih_context *c,
+ dialogparam * d)
+{
+ if (c == NULL
+ && (!(item->flags & MENUFLAG_ATSTARTUP) || firstqueue != NULL)) {
+ menu_addqueue(item, d);
+ return;
+ } else {
+ if (c != NULL && c->incalculation
+ && !(item->flags & MENUFLAG_INCALC)) {
+ if (c->flags & MENUFLAG_INTERRUPT)
+ c->interrupt = 1;
+ menu_addqueue(item, d);
+ return;
+ }
+ }
+ switch (item->type) {
+ case MENU_SEPARATOR:
+ x_error("separator activated!");
+ break;
+ case MENU_SUBMENU:
+ x_error("submenu activated!");
+ break;
+ case MENU_NOPARAM:
+ ((void (*)(struct uih_context *)) item->function) (c);
+ break;
+ case MENU_INT:
+ ((void (*)(struct uih_context *, int)) item->function) (c,
+ item->
+ iparam);
+ break;
+ case MENU_STRING:
+ ((void (*)(struct uih_context *, CONST char *)) item->function) (c,
+ (CONST
+ char
+ *)
+ item->pparam);
+ break;
+ case MENU_DIALOG:
+ case MENU_CUSTOMDIALOG:
+ if (!menu_havedialog(item, c)) {
+ ((void (*)(struct uih_context * c, dialogparam *)) item->
+ function)
+ (c, (dialogparam *) NULL);
+ } else {
+ CONST menudialog *di = menu_getdialog(c, item);
+ if (di[0].question == NULL) {
+ ((void (*)(struct uih_context * c, dialogparam *))
+ item->function) (c, (dialogparam *) NULL);
+ break;
+ } else if (di[1].question == NULL) {
+ /*call function with right
+ *parameter. This avoids need to write wrappers*/
+ switch (di[0].type) {
+ case DIALOG_INT:
+ case DIALOG_CHOICE:
+ case DIALOG_ONOFF:
+ ((void (*)(struct uih_context * c, int)) item->
+ function) (c, d[0].dint);
+ break;
+ case DIALOG_FLOAT:
+ ((void (*)(struct uih_context * c, number_t))
+ item->function) (c, d[0].number);
+ break;
+ case DIALOG_COORD:
+ ((void (*)(struct uih_context * c, number_t, number_t))
+ item->function) (c, d[0].dcoord[0], d[0].dcoord[1]);
+ break;
+ case DIALOG_STRING:
+ case DIALOG_KEYSTRING:
+ ((void (*)(struct uih_context * c, char *)) item->
+ function)
+ (c, d[0].dstring);
+ break;
+ case DIALOG_IFILE:
+ case DIALOG_OFILE:
+ ((void (*)(struct uih_context * c, xio_path))
+ item->function) (c, d[0].dpath);
+ break;
+ default:
+ x_error("dialog:unknown type!");
+ break;
+ }
+
+ } else
+ ((void (*)(struct uih_context * c, dialogparam *)) item->
+ function)
+ (c, d);
+ }
+ break;
+ default:
+ x_error("Menu_activate: unknown type %i!", item->type);
+ break;
+ }
+}
+
+int menu_enabled(CONST menuitem * item, struct uih_context *c)
+{
+ if (item->flags & (MENUFLAG_RADIO | MENUFLAG_CHECKBOX))
+ switch (item->type) {
+ case MENU_SEPARATOR:
+ return 0;
+ case MENU_SUBMENU:
+ case MENU_DIALOG:
+ case MENU_NOPARAM:
+ case MENU_CUSTOMDIALOG:
+ return (((int (*)(struct uih_context *)) item->control) (c));
+ case MENU_INT:
+ return (((int (*)(struct uih_context *, int)) item->
+ control) (c, item->iparam));
+ case MENU_STRING:
+ return (((int (*)(struct uih_context *, CONST char *)) item->
+ control)
+ (c, (CONST char *) item->pparam));
+ default:
+ x_error("Menu_enabled: unknown type!");
+ break;
+ }
+ return 0;
+}
+
+void menu_delnumbered(int n, CONST char *name)
+{
+ menuitem *items;
+ int i;
+ char s[256];
+ sprintf(s, "%s%i", name, 0);
+ items = (menuitem *) menu_findcommand(s);
+ menu_delete(items, n);
+ for (i = 0; i < n; i++) {
+ if (items[i].key)
+ free((char *) items[i].key);
+ free((char *) items[i].shortname);
+ }
+ free(items);
+}
+
+CONST menuitem *menu_genernumbered(int n, CONST char *menuname,
+ CONST char *CONST * CONST names,
+ CONST char *keys, int type, int flags,
+ void (*function) (struct uih_context *
+ context, int),
+ int (*control) (struct uih_context *
+ context, int),
+ CONST char *prefix)
+{
+ int l = keys != NULL ? (int) strlen(keys) : -1;
+ int i;
+ menuitem *item = (menuitem *) malloc(sizeof(menuitem) * n);
+ if (item == NULL)
+ return NULL;
+ for (i = 0; i < n; i++) {
+ item[i].menuname = menuname;
+ if (i < l) {
+ char *key = malloc(2);
+ item[i].key = key;
+ key[0] = keys[i];
+ key[1] = 0;
+ } else
+ item[i].key = 0;
+ item[i].type = type;
+ item[i].flags = flags;
+ item[i].iparam = i;
+ item[i].name = names[i];
+ item[i].shortname = names[i];
+ if (prefix != NULL) {
+ char *shortname = (char *) malloc(strlen(prefix) + 4);
+ item[i].shortname = shortname;
+ sprintf(shortname, "%s%i", prefix, i);
+ }
+ item[i].function = (void (*)(void)) function;
+ item[i].control = (int (*)(void)) control;
+ }
+ menu_add(item, n);
+ return (item);
+}
+
+number_t menu_getfloat(CONST char *s, CONST char **error)
+{
+#ifdef HAVE_LONG_DOUBLE
+ long double param = 0;
+#else
+ double param = 0;
+#endif
+#ifdef HAVE_LONG_DOUBLE
+#ifndef USE_ATOLD
+#ifdef USE_XLDIO
+ param = x_strtold(s, NULL);
+ if (0)
+#else
+ if (sscanf(s, "%LG", &param) == 0)
+#endif
+#else
+ param = _atold(s);
+ if (0)
+#endif
+ {
+#else
+ if (sscanf(s, "%lG", &param) == 0) {
+#endif
+ *error = "Floating point number expected";
+ return 0;
+ }
+ return (param);
+}
+
+int menuparse_scheme = 0;
+CONST char *menu_fillparam(struct uih_context *uih, tokenfunc f,
+ CONST menudialog * d, dialogparam * p)
+{
+ char *c = f(uih);
+ CONST char *error = NULL;
+ if (c == NULL)
+ return "Parameter expected";
+ switch (d->type) {
+ case DIALOG_INT:
+ if (sscanf(c, "%i", &p->dint) != 1) {
+ return "Integer parameter expected";
+ }
+ break;
+ case DIALOG_FLOAT:
+ p->number = menu_getfloat(c, &error);
+ if (error != NULL)
+ return (error);
+ break;
+ case DIALOG_COORD:
+ p->dcoord[0] = menu_getfloat(c, &error);
+ if (error != NULL)
+ return (error);
+ c = f(uih);
+ if (c == NULL)
+ return "Imaginary part expected";
+ p->dcoord[1] = menu_getfloat(c, &error);
+ if (error != NULL)
+ return (error);
+ break;
+ case DIALOG_KEYSTRING:
+ if (menuparse_scheme) {
+ if (c[0] != '\'')
+ return "Key string parameter expected";
+ p->dstring = mystrdup(c + 1);
+ } else
+ p->dstring = mystrdup(c);
+ break;
+ case DIALOG_STRING:
+ if (menuparse_scheme) {
+ int l = strlen(c);
+ if (l < 2 || c[0] != '"' || c[l - 1] != '"')
+ return "String parameter expected";
+ p->dstring = mystrdup(c + 1);
+ p->dstring[l - 2] = 0;
+ } else
+ p->dstring = mystrdup(c);
+ break;
+ case DIALOG_IFILE:
+ case DIALOG_OFILE:
+ if (menuparse_scheme) {
+ int l = strlen(c);
+ if (l < 2 || c[0] != '"' || c[l - 1] != '"')
+ return "String parameter expected";
+ p->dstring = mystrdup(c + 1);
+ p->dstring[l - 2] = 0;
+ } else
+ p->dstring = mystrdup(c);
+ break;
+ case DIALOG_CHOICE:
+ if (menuparse_scheme) {
+ if (c[0] != '\'')
+ return "Key string parameter expected";
+ c++;
+ }
+ {
+ int i;
+ CONST char **keys = (CONST char **) d->defstr;
+ for (i = 0;; i++) {
+ if (keys[i] == NULL)
+ return "Unknown parameter";
+ if (!strcmp(c, keys[i])) {
+ p->dint = i;
+ return NULL;
+ }
+ }
+ }
+ case DIALOG_ONOFF:
+ if (menuparse_scheme) {
+ if (!strcmp("#t", c)) {
+ p->dint = 1;
+ return NULL;
+ }
+ if (!strcmp("#f", c)) {
+ p->dint = 0;
+ return NULL;
+ }
+ } else {
+ if (!strcmp("on", c)) {
+ p->dint = 1;
+ return NULL;
+ }
+ if (!strcmp("off", c)) {
+ p->dint = 0;
+ return NULL;
+ }
+ }
+ default:
+ x_error("Unknown dialog parameter type!");
+ } /*switch */
+ return NULL;
+}
+
+static char errorstr[256];
+CONST menuitem *menu_findcommand(CONST char *name)
+{
+ struct entry *entry;
+ CONST menuitem *item;
+ int len;
+ len = strlen(name);
+ if (len > 100)
+ return NULL;
+ entry = namehash[HASH(name, len)];
+ while (entry != NULL) {
+ if (!strcmp(entry->item->shortname, name))
+ break;
+ entry = entry->nextname;
+ }
+ if (entry == NULL) {
+ return NULL;
+ }
+ item = entry->item;
+ return (item);
+}
+
+CONST char *menu_processcommand(struct uih_context *uih, tokenfunc f,
+ int scheme, int mask, CONST char *root)
+{
+ char *c = f(uih);
+ CONST menuitem *item;
+ menuparse_scheme = scheme;
+ if (c == NULL) {
+ if (!menuparse_scheme)
+ return "Command name expected";
+ return NULL;
+ }
+ item = menu_findcommand(c);
+ if (item == NULL) {
+ sprintf(errorstr, "%s:unknown function", c);
+ return errorstr;
+ }
+ if (item->flags & mask) {
+ sprintf(errorstr,
+ "function '%s' not available in this context (%i, %i)", c,
+ mask, item->flags);
+ return errorstr;
+ }
+ if ((item->flags & MENUFLAG_NOPLAY) && uih != NULL) {
+ if (root != NULL && !menu_available(item, root)) {
+ sprintf(errorstr, "function '%s' is disabled", c);
+ return errorstr;
+ }
+ }
+
+ if ((item->flags & MENUFLAG_CHECKBOX) && scheme) {
+ int w;
+ c = f(uih);
+ if (c == NULL) {
+ return ("Boolean parameter expected");
+ }
+
+ if (!strcmp("#t", c)) {
+ w = 1;
+ } else if (!strcmp("#f", c)) {
+ w = 0;
+ } else
+ return "Boolean parameter expected";
+ if (w == menu_enabled(item, uih)) {
+ if (((w != 0) ^ ((item->flags & MENUFLAG_DIALOGATDISABLE) !=
+ 0))
+ || (item->type != MENU_DIALOG
+ && item->type != MENU_CUSTOMDIALOG)) {
+ return NULL;
+ } else
+ menu_activate(item, uih, NULL); /*disable it... */
+ }
+ }
+ if (item->type != MENU_DIALOG && item->type != MENU_CUSTOMDIALOG) {
+ menu_activate(item, uih, NULL);
+ return NULL;
+ }
+ /*So we have some parameters */
+
+ {
+ dialogparam *param;
+ CONST menudialog *d = menu_getdialog(uih, item);
+ int i, n;
+ for (n = 0; d[n].question != NULL; n++);
+ param = (dialogparam *) malloc(n * sizeof(dialogparam));
+ for (i = 0; i < n; i++) {
+ CONST char *error;
+ error = menu_fillparam(uih, f, d + i, param + i);
+ if (error != NULL) {
+ sprintf(errorstr, "Function '%s' parameter %i:%s",
+ item->shortname, i, error);
+ for (n = 0; n < i; n++) {
+ menu_freeparam(param + i, d + i);
+ }
+ free(param);
+ return errorstr;
+ }
+ }
+ menu_activate(item, uih, param);
+ if (uih != NULL)
+ menu_destroydialog(item, param, uih);
+ }
+ return NULL;
+}
+
+static int argpos, margc;
+static char **margv;
+static int argposs;
+static char *gettoken(struct uih_context *c)
+{
+ if (argpos == margc)
+ return NULL;
+ if (argpos == argposs) {
+ if (!margv[argpos])
+ return NULL;
+ return (margv[argpos++] + 1);
+ } else
+ return (margv[argpos++]);
+}
+
+int menu_processargs(int n, int argc, char **argv)
+{
+ CONST char *error;
+ argpos = n;
+ argposs = n;
+ margc = argc;
+ margv = argv;
+ error =
+ menu_processcommand(NULL, gettoken, 0, MENUFLAG_NOOPTION, "root");
+ if (error) {
+ x_error("%s", error);
+ return -1;
+ }
+ return (argpos - 2);
+
+}
+
+void menu_printhelp(void)
+{
+ struct entry *e = firstitem;
+ while (e) {
+ if (e->item->type == MENU_SEPARATOR) {
+ e = e->next;
+ continue;
+ }
+ if (e->item->type == MENU_SUBMENU
+ && !(e->item->flags & MENUFLAG_NOOPTION)) {
+ struct entry *e1 = firstitem;
+ int n = 1;
+ while (e1) {
+ /*if (e->item->type == MENU_SEPARATOR) {printf ("\n"); e1=e1->next;continue;} */
+ if (e1->item->type != MENU_SUBMENU
+ && e1->item->type != MENU_SEPARATOR
+ && !(e1->item->flags & MENUFLAG_NOOPTION)
+ && !strcmp(e1->item->menuname, e->item->shortname)) {
+ if (n) {
+ printf("\n%s\n\n", e->item->name);
+ n = 0;
+ }
+ printf(" -%-15s", e1->item->shortname);
+ if (menu_havedialog(e1->item, NULL)) {
+ CONST menudialog *d =
+ menu_getdialog(NULL, e1->item);
+ while (d->question != NULL) {
+ switch (d->type) {
+ case DIALOG_INT:
+ printf("integer ");
+ break;
+ case DIALOG_FLOAT:
+ printf("real_number ");
+ break;
+ case DIALOG_COORD:
+ printf("real_number real_number ");
+ break;
+ case DIALOG_KEYSTRING:
+ case DIALOG_STRING:
+ printf("string ");
+ break;
+ case DIALOG_IFILE:
+ printf("input_file ");
+ break;
+ case DIALOG_OFILE:
+ printf("output_file ");
+ break;
+ case DIALOG_CHOICE:
+ {
+ int i;
+ CONST char **keys =
+ (CONST char **) d->defstr;
+ for (i = 0;; i++) {
+ if (keys[i] == NULL)
+ break;
+ if (i != 0)
+ putc('|', stdout);
+ printf("%s", keys[i]);
+ }
+ putc(' ', stdout);
+ }
+ break;
+ case DIALOG_ONOFF:
+ printf("on|off ");
+ }
+ d++;
+ }
+ printf("\n%14s ", "");
+ }
+ printf(" %s\n", e1->item->name);
+ }
+ e1 = e1->next;
+ }
+ }
+ e = e->next;
+ }
+}
+
+void uih_xshlprintmenu(struct uih_context *c, CONST char *name)
+{
+ int i = 0;
+ int nexti;
+ CONST menuitem *item, *nextitem, *lastitem;
+ int comma;
+ printf("%%%s\n\n", name);
+ printf("<menuhead><head>%s</head></menuhead>\n", menu_fullname(name));
+ printf("<menuitems><center>\n");
+ for (i = 0; (item = menu_item2(name, i)) != NULL; i++) {
+ if (item->type == MENU_SEPARATOR)
+ printf("<p>\n");
+ else if (item->type == MENU_SUBMENU)
+ printf("<p><submenu><a %s>%s</a>\n", item->shortname,
+ item->name);
+ else
+ printf("<p><a %s>%s</a>\n", item->shortname, item->name);
+ }
+ printf("</center></menuitems>\n");
+ lastitem = NULL;
+ for (i = 0; (item = menu_item2(name, i)) != NULL; i++) {
+ if (item->type == MENU_SEPARATOR)
+ continue;
+ if (item->type != MENU_SUBMENU) {
+ for (nexti = i + 1;
+ (nextitem = menu_item2(name, nexti)) != NULL
+ && nextitem->type == MENU_SUBMENU; nexti++);
+ printf("<node %s, %s, %s, %s>\n", item->shortname,
+ (lastitem != NULL ? lastitem->shortname : ""),
+ nextitem != NULL ? nextitem->shortname : "", name);
+ printf("%%%s\n", item->shortname);
+ printf("<head>%s</head>\n", item->name);
+ if (!(item->flags & MENUFLAG_NOPLAY)) {
+ printf("<p><emph>Syntax</emph>:(%s", item->shortname);
+ if (item->flags & MENUFLAG_CHECKBOX)
+ printf(" bool");
+ if (item->type == MENU_DIALOG
+ || item->type == MENU_CUSTOMDIALOG) {
+ int y;
+ CONST menudialog *di;
+ di = menu_getdialog(c, item);
+ if (item->flags & MENUFLAG_CHECKBOX)
+ printf(" [");
+ for (y = 0; di[y].question != NULL; y++) {
+ switch (di[y].type) {
+ case DIALOG_INT:
+ printf(" integer");
+ break;
+ case DIALOG_FLOAT:
+ printf(" float");
+ break;
+ case DIALOG_STRING:
+ printf(" string");
+ case DIALOG_KEYSTRING:
+ printf(" keyword");
+ break;
+ case DIALOG_IFILE:
+ printf(" file");
+ break;
+ case DIALOG_OFILE:
+ printf(" file");
+ break;
+ case DIALOG_ONOFF:
+ printf(" bool");
+ break;
+ case DIALOG_COORD:
+ printf(" complex");
+ break;
+ case DIALOG_CHOICE:
+ printf(" keyword");
+ break;
+ }
+ }
+ if (item->flags & MENUFLAG_CHECKBOX)
+ printf(" ]");
+ }
+ printf(")\n");
+ }
+ printf("<p>\n<emph>Available as</emph>: ");
+ comma = 0;
+ if (!(item->flags & MENUFLAG_NOMENU))
+ printf("menu item"), comma = 1;
+ if (!(item->flags & MENUFLAG_NOOPTION))
+ printf("%scommand line option", comma ? ", " : ""), comma =
+ 1;
+ if (!(item->flags & MENUFLAG_NOPLAY))
+ printf("%scommand", comma ? ", " : ""), comma = 1;
+ printf("\n");
+ printf("\n");
+ lastitem = item;
+ }
+ }
+}
+
+void uih_xshlprintmenus(struct uih_context *c)
+{
+ struct entry *e = firstitem;
+ struct entry *nexte;
+ struct entry *laste;
+ printf("%%menus\n");
+ printf("<main><head>Menus</head></main>\n");
+ printf("<menuitems><center>\n");
+ while (e != NULL) {
+ if (e->item->type == MENU_SUBMENU)
+ printf("<p><submenu><a %s>%s</a>\n", e->item->shortname,
+ e->item->name);
+ e = e->next;
+ }
+ printf("</center></menuitems>\n");
+ e = firstitem;
+ laste = NULL;
+ while (e != NULL) {
+ if (e->item->type == MENU_SUBMENU) {
+ nexte = e->next;
+ while (nexte != NULL && nexte->item->type != MENU_SUBMENU)
+ nexte = nexte->next;
+ printf("<node %s, %s, %s, %s>\n", e->item->shortname,
+ (laste != NULL ? laste->item->shortname : ""),
+ nexte != NULL ? nexte->item->shortname : "", "menus");
+ uih_xshlprintmenu(c, e->item->shortname);
+ laste = e;
+ }
+ e = e->next;
+ }
+ printf("%%endmenus");
+}
+
+void
+menu_forall(struct uih_context *c,
+ void (*callback) (struct uih_context * c,
+ CONST menuitem * item))
+{
+ struct entry *e = firstitem;
+ while (e != NULL) {
+ callback(c, e->item);
+ e = e->next;
+ }
+}
diff --git a/src/util/xshl.c b/src/util/xshl.c
new file mode 100644
index 0000000..27ab8d5
--- /dev/null
+++ b/src/util/xshl.c
@@ -0,0 +1,470 @@
+#ifndef _plan9_
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#else
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#endif
+#include <config.h>
+#include <misc-f.h>
+#include "xshl.h"
+
+#define XSHL_ENDLINE (65536)
+#define XSHL_ENDPARAGRAPH (65536*2)
+#define XSHL_NEWSTART (65536*4)
+#define XSHL_SMALL (65536*8)
+#define XSHL_SKIPBLANK (65536*16)
+#define XSHL_COMMAND (65536*32)
+#define XSHL_BLANK (65536*64)
+#define XSHL_CLEARATLINE (65536*128)
+
+#define XSHL_AUTOCLEAN (XSHL_ENDLINE|XSHL_ENDPARAGRAPH|XSHL_NEWSTART|XSHL_SKIPBLANK|XSHL_COMMAND|XSHL_BLANK)
+
+
+#define ALL (~0)
+CONST static struct tag {
+ CONST char *name;
+ int andflagenable;
+ int orflagenable;
+ int andflagdisable;
+ int orflagdisable;
+} tags[] = {
+ {
+ "p", ALL, XSHL_SKIPBLANK | XSHL_ENDPARAGRAPH, ALL,
+ XSHL_SKIPBLANK | XSHL_ENDPARAGRAPH}
+ , {
+ "head", ALL,
+ XSHL_SKIPBLANK | XSHL_BIG | XSHL_CENTERALIGN |
+ XSHL_ENDPARAGRAPH, ~(XSHL_BIG | XSHL_CENTERALIGN),
+ XSHL_SKIPBLANK | XSHL_ENDPARAGRAPH | XSHL_ENDLINE}
+ , {
+ "emph", ALL, XSHL_EMPH, ~XSHL_EMPH, 0}
+ , {
+ "br", ALL, XSHL_SKIPBLANK | XSHL_ENDLINE, ALL, 0}
+ , {
+ "white", ~XSHL_COLORMASK, XSHL_WHITE, ~XSHL_WHITE, 0}
+ , {
+ "red", ~XSHL_COLORMASK, XSHL_RED, ~XSHL_RED, 0}
+ , {
+ "black", ~XSHL_COLORMASK, XSHL_BLACK, ~XSHL_BLACK, 0}
+ , {
+ "right", ALL, XSHL_SKIPBLANK | XSHL_RIGHTALIGN | XSHL_ENDLINE,
+ ~XSHL_RIGHTALIGN, XSHL_SKIPBLANK | XSHL_ENDLINE}
+ , {
+ "center", ALL, XSHL_SKIPBLANK | XSHL_CENTERALIGN | XSHL_ENDLINE,
+ ~XSHL_CENTERALIGN, XSHL_SKIPBLANK | XSHL_ENDLINE}
+ , {
+ "tt", ALL, XSHL_MONOSPACE, ~XSHL_MONOSPACE, 0}
+ , {
+ "dl", ALL, XSHL_SKIPBLANK | XSHL_ENDLINE,
+ ~(XSHL_SMALL | XSHL_EMPH), XSHL_SKIPBLANK | XSHL_ENDLINE}
+ , {
+ "dt", ~XSHL_SMALL, XSHL_SKIPBLANK | XSHL_ENDLINE | XSHL_EMPH, ALL,
+ 0}
+ , {
+ "dd", ~XSHL_EMPH, XSHL_SKIPBLANK | XSHL_NEWSTART | XSHL_SMALL, ALL,
+ 0}
+ , {
+ NULL}
+};
+
+#define MAXINPUT 256
+
+#define SMALLSKIP (4*spacewidth)
+
+struct boxitem {
+ char *text;
+ struct xshl_context c;
+ int xpos;
+ int width;
+ int height;
+ struct boxitem *next;
+};
+static void freebox(struct boxitem *box)
+{
+ if (box->c.linktext != NULL)
+ free(box->c.linktext);
+ free(box->text);
+ free(box);
+}
+
+static struct xshl_item *pack(struct boxitem *first, struct boxitem *last,
+ int *collectedflags, int width)
+{
+ struct xshl_item *f = NULL;
+ struct xshl_item *l = NULL;
+ struct xshl_item *item;
+ struct boxitem *curr = first, *ncurr;
+ int end = 0;
+ char text[256];
+ int collected = 0;
+ while (curr != last) {
+ if (curr->text[0] == 0) {
+ ncurr = curr->next;
+ freebox(curr);
+ curr = ncurr;
+ continue;
+ }
+ strcpy(text, curr->text);
+ item = (struct xshl_item *) malloc(sizeof(struct xshl_item));
+ if (item == NULL)
+ return NULL;
+ item->x = curr->xpos;
+ item->width = curr->width;
+ item->c = curr->c;
+ if (curr->c.linktext != NULL)
+ curr->c.linktext = mystrdup(curr->c.linktext);
+ collected |= item->c.flags;
+ ncurr = curr->next;
+ freebox(curr);
+ curr = ncurr;
+ while (curr != last &&
+ curr->xpos == item->x + item->width &&
+ (curr->c.flags & 0xffff) == (item->c.flags & 0xffff)
+ &&
+ ((curr->c.linktext == NULL && item->c.linktext == NULL) ||
+ (curr->c.linktext != NULL && item->c.linktext != NULL &&
+ !strcmp(curr->c.linktext, item->c.linktext))))
+ {
+ strcat(text, curr->text);
+ item->width += curr->width;
+ collected |= item->c.flags;
+ ncurr = curr->next;
+ freebox(curr);
+ curr = ncurr;
+ }
+ item->text = mystrdup(text);
+ item->next = NULL;
+ if (l != NULL)
+ l->next = item;
+ else
+ f = item;
+ l = item;
+ end = item->x + item->width;
+ }
+ *collectedflags = collected;
+ if (collected & (XSHL_CENTERALIGN | XSHL_RIGHTALIGN)) {
+ if (collected & XSHL_RIGHTALIGN)
+ end = width - end;
+ else
+ end = (width - end) / 2;
+ item = f;
+ while (item != NULL) {
+ item->x += end;
+ item = item->next;
+ }
+ }
+ return (f);
+}
+
+#ifndef isspace
+#define isspace(c) ((c)==' '||(c)=='\t'||(c)=='\n')
+#endif
+static char xshllink[32];
+static int flags;
+static struct boxitem *xshl_readbox(void *data, int (*get) (void *))
+{
+ char inputbuf[256];
+ int c;
+ int i;
+ char command[16];
+ char parameter[32];
+ if (flags & XSHL_BLANK) {
+ struct boxitem *box = (struct boxitem *) malloc(sizeof(*box));
+ box->width = 0;
+ box->next = NULL;
+ box->height = flags & XSHL_BIG ? 0 : 1;
+ box->text = mystrdup(" ");
+ box->c.flags = flags | XSHL_CLEARATLINE;
+ box->c.linktext = NULL;
+ if (xshllink[0] != 0)
+ box->c.linktext = mystrdup(xshllink);
+ flags &= ~XSHL_AUTOCLEAN;
+ flags |= XSHL_SKIPBLANK;
+ return (box);
+ }
+ if (flags & XSHL_COMMAND) {
+ int i = 0;
+ flags &= ~XSHL_COMMAND;
+ parameter[0] = 0;
+ do {
+ c = command[i] = get(data);
+ if (i < 15)
+ i++;
+ }
+ while (c && c != '>' && !isspace(c));
+ command[i - 1] = 0;
+ if (c != '>' && c) {
+ do {
+ c = get(data);
+ }
+ while (c && c != '>' && isspace(c));
+ if (c && c != '>') {
+ i = 1;
+ parameter[0] = c;
+ do {
+ c = parameter[i] = get(data);
+ if (i < 31)
+ i++;
+ }
+ while (c && c != '>' && !isspace(c));
+ parameter[i - 1] = 0;
+ if (isspace(c)) {
+ do {
+ c = get(data);
+ }
+ while (c && c != '>' && isspace(c));
+ }
+ }
+ }
+ {
+ int i;
+ int disabled = 0;
+ if (command[0] == '/')
+ disabled = 1;
+ for (i = 0;
+ tags[i].name != NULL
+ && strcmp(tags[i].name, command + disabled); i++);
+ if (tags[i].name != NULL) {
+ if (disabled) {
+ flags &= tags[i].andflagdisable;
+ flags |= tags[i].orflagdisable;
+ } else {
+ flags &= tags[i].andflagenable;
+ flags |= tags[i].orflagenable;
+ }
+ } else {
+ if (!strcmp(command + disabled, "a")
+ || !strcmp(command + disabled, "tutor")) {
+ if (disabled)
+ xshllink[0] = 0;
+ else
+ strcpy(xshllink, parameter);
+ } else
+ while (c != '>')
+ c = get(data);
+ }
+ }
+ if (c == '>')
+ c = get(data);
+ } else
+ c = get(data);
+ if (!c) {
+ return NULL;
+ }
+ if (flags & XSHL_SKIPBLANK) {
+ while (isspace(c)) {
+ c = get(data);
+ if (!c) {
+ return NULL;
+ }
+ }
+ flags &= ~XSHL_SKIPBLANK;
+ }
+ i = 0;
+ inputbuf[i++] = c;
+ while (c && c != '<' && !isspace(c)) {
+ c = get(data);
+ inputbuf[i++] = c;
+ if (i > 255)
+ i = 255;
+ }
+ inputbuf[i - 1] = 0;
+ if (i == 1 && !c) {
+ return NULL;
+ }
+ if (i == 1 && isspace(c)) {
+ flags |= XSHL_BLANK;
+ return xshl_readbox(data, get);
+ }
+ if (i == 1 && c == '<') {
+ flags |= XSHL_COMMAND;
+ return xshl_readbox(data, get);
+ }
+ {
+ struct boxitem *box = (struct boxitem *) malloc(sizeof(*box));
+ box->width = 0;
+ box->next = NULL;
+ box->height = flags & XSHL_BIG ? 0 : 1;
+ box->text = mystrdup(inputbuf);
+ box->c.flags = flags | XSHL_CLEARATLINE;
+ box->c.linktext = NULL;
+ if (xshllink[0] != 0)
+ box->c.linktext = mystrdup(xshllink);
+ flags &= ~XSHL_AUTOCLEAN;
+ if (isspace(c))
+ flags |= XSHL_BLANK;
+ if (c == '<')
+ flags |= XSHL_COMMAND;
+ return (box);
+ }
+ /*We are at the first word */
+}
+
+struct xshl_line *xshl_interpret(void *data, int (*get) (void *),
+ int width, int getwidth(void *, int flags,
+ CONST char *text),
+ int startflags, int smallheight,
+ int bigheight)
+{
+ int spacewidth = getwidth(data, 0, " ");
+ int cflags;
+ struct boxitem *first = NULL;
+ struct boxitem *last = NULL;
+ struct boxitem *item;
+ struct boxitem *lastword = NULL;
+ int maxlines = 200;
+ struct xshl_line *lines =
+ (struct xshl_line *) malloc(maxlines * sizeof(*lines));
+ int nlines = 0;
+ int ypos = 0;
+ flags = startflags | XSHL_SKIPBLANK;
+ xshllink[0] = 0;
+ while ((item = xshl_readbox(data, get)) != NULL) {
+ if (last == NULL) {
+ if (item->text[0] == ' ' && !item->text[1]) {
+ freebox(item);
+ continue;
+ }
+ lastword = NULL;
+ first = item, item->xpos =
+ item->c.flags & XSHL_SMALL ? SMALLSKIP : 0;
+ } else {
+ if (item->text[0] == ' ' && !item->text[1]) {
+ lastword = item;
+ }
+ last->next = item, item->xpos = last->xpos + last->width;
+ }
+ last = item;
+ item->width = getwidth(data, item->c.flags, item->text);
+ if (item->c.flags & (XSHL_ENDLINE | XSHL_ENDPARAGRAPH)
+ || ((item->c.flags & XSHL_NEWSTART)
+ && item->xpos + spacewidth > SMALLSKIP)) {
+ if (nlines > maxlines - 1)
+ maxlines *= 2, lines =
+ (struct xshl_line *) realloc(lines,
+ (maxlines) *
+ sizeof(*lines));
+ if (first != NULL) {
+ lines[nlines].first = pack(first, last, &cflags, width);
+ lines[nlines].y = ypos;
+ nlines++;
+ if (ypos & XSHL_BIG)
+ ypos += bigheight;
+ else
+ ypos += smallheight;
+ }
+ if (item->c.flags & (XSHL_ENDPARAGRAPH))
+ ypos += smallheight;
+ first = last;
+ item->xpos =
+ item->c.flags & item->c.flags & XSHL_SMALL ? SMALLSKIP : 0;
+ lastword = NULL;
+ } else if (item->c.flags & XSHL_NEWSTART)
+ item->xpos = SMALLSKIP;
+ if (item->xpos + item->width > width) {
+ if (lastword == NULL) {
+ item = first;
+ while (item != NULL) {
+ struct boxitem *c = item->next;
+ freebox(item);
+ item = c;
+ }
+ first = last = NULL;
+ } else {
+ int xpos;
+ if (nlines > maxlines - 1)
+ maxlines *= 2, lines =
+ (struct xshl_line *) realloc(lines,
+ (maxlines) *
+ sizeof(*lines));
+ lines[nlines].first =
+ pack(first, lastword, &cflags, width);
+ lines[nlines].y = ypos;
+ nlines++;
+ if (ypos & XSHL_BIG)
+ ypos += bigheight;
+ else
+ ypos += smallheight;
+ if (lastword != NULL)
+ first = lastword->next;
+ else
+ first = NULL;
+ item = first;
+ if (item != NULL) {
+ xpos =
+ item->c.flags & item->c.
+ flags & XSHL_SMALL ? SMALLSKIP : 0;
+ while (item != NULL) {
+ item->xpos = xpos;
+ xpos += item->width;
+ item = item->next;
+ }
+ }
+ freebox(lastword);
+ lastword = NULL;
+ if (first == NULL)
+ last = NULL;
+ }
+ }
+ }
+ if (last != NULL) {
+ lines[nlines].y = ypos;
+ lines[nlines].first = pack(first, last->next, &cflags, width);
+ }
+ nlines++;
+ lines[nlines].y = -1;
+ lines =
+ (struct xshl_line *) realloc(lines, (nlines + 2) * sizeof(*lines));
+ return (lines);
+}
+
+
+int xshl_textlen(void *data, int flags, CONST char *text)
+{
+ return ((int) strlen(text));
+}
+
+void xshl_print(int startskip, struct xshl_line *lines)
+{
+ int i = 0;
+ int y = 0;
+ while (lines[i].y >= 0) {
+ int xpos = -startskip;
+ struct xshl_item *item;
+ while (y < lines[i].y) {
+ putc('\n', stdout), y++;
+ }
+ item = lines[i].first;
+ while (item) {
+ while (xpos < item->x) {
+ putc(' ', stdout), xpos++;
+ }
+ xpos += item->width;
+ item = item->next;
+ }
+ i++;
+ }
+}
+
+void xshl_free(struct xshl_line *lines)
+{
+ int i = 0;
+ while (lines[i].y >= 0) {
+ struct xshl_item *item, *nextitem;
+ item = lines[i].first;
+ while (item) {
+ nextitem = item->next;
+ if (item->c.linktext != NULL)
+ free(item->c.linktext);
+ free(item->text);
+ free(item);
+ item = nextitem;
+ }
+ i++;
+ }
+ free(lines);
+}
diff --git a/src/util/xstdio.c b/src/util/xstdio.c
new file mode 100644
index 0000000..fc7eb42
--- /dev/null
+++ b/src/util/xstdio.c
@@ -0,0 +1,488 @@
+#ifndef _plan9_
+#include <string.h>
+#if defined(__EMX__) || defined(__APPLE__)
+#include <sys/types.h>
+#endif
+#include <dirent.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#else
+#include <u.h>
+#include <libc.h>
+#endif
+#include <config.h>
+#include <filter.h>
+#include <fractal.h>
+#include <ui_helper.h>
+#include <misc-f.h>
+#ifdef _WIN32
+#define strcmp stricmp
+#endif
+#ifdef DJGPP
+#define strcmp stricmp
+#endif
+
+/* We reserve character 01 to application directory so we can easily refer to data files */
+char *xio_appdir;
+char *xio_homedir;
+
+char *xio_fixpath(CONST char *c)
+{
+ if (c[0] == '~') {
+ char *c1 = (char *) malloc(strlen(c) + strlen(xio_homedir) + 5);
+ sprintf(c1, "%s%s", xio_homedir, c + 1);
+ return c1;
+ }
+ if (c[0] == '\01') {
+ char *c1 = (char *) malloc(strlen(c) + strlen(xio_appdir) + 5);
+ sprintf(c1, "%s%s", xio_appdir, c + 1);
+ return c1;
+ }
+ return mystrdup(c);
+}
+
+int
+xio_getfiles(xio_constpath path1, char ***names, char ***dirs,
+ int *nnames2, int *ndirs2)
+{
+#ifdef _plan9_
+ *nnames2 = *ndirs2 = 0;
+#else
+ char *path = xio_fixpath(path1);
+ int maxnames = 200, maxdirs = 200;
+ int nnames = 0, ndirs = 0;
+ DIR *dir = opendir(path);
+ struct stat buf;
+ char buff[4096];
+ int pathlen;
+ struct dirent *e;
+ if (dir == NULL)
+ return 0;
+ *nnames2 = 0;
+ *ndirs2 = 0;
+ e = readdir(dir);
+ strcpy(buff, path);
+ pathlen = (int) strlen(path);
+ if (buff[pathlen - 1] != XIO_PATHSEP)
+ buff[pathlen] = XIO_PATHSEP;
+ else
+ pathlen--;
+ *names = (char **) malloc(maxnames * sizeof(**names));
+ *dirs = (char **) malloc(maxdirs * sizeof(**dirs));
+ free(path);
+ while (e != NULL) {
+ char *n = mystrdup(e->d_name);
+ strcpy(buff + pathlen + 1, e->d_name);
+ stat(buff, &buf);
+ if (S_ISDIR(buf.st_mode)) {
+ if (ndirs == maxdirs)
+ maxdirs *= 2, *dirs =
+ (char **) realloc(*dirs, maxdirs * sizeof(**dirs));
+ (*dirs)[ndirs] = n;
+ ndirs++;
+ } else {
+ if (nnames == maxnames)
+ maxnames *= 2, *names =
+ (char **) realloc(*names, maxnames * sizeof(**names));
+ (*names)[nnames] = n;
+ nnames++;
+ }
+ e = readdir(dir);
+ }
+ if (nnames)
+ *names = (char **) realloc(*names, nnames * sizeof(**names));
+ else
+ free(*names), *names = NULL;
+ if (ndirs)
+ *dirs = (char **) realloc(*dirs, ndirs * sizeof(**dirs));
+ else
+ free(*dirs), *dirs = NULL;
+ *nnames2 = nnames;
+ *ndirs2 = ndirs;
+ closedir(dir);
+ return 1;
+#endif
+}
+
+xio_path xio_getdirectory(xio_constpath filename)
+{
+ int i;
+ xio_pathdata directory;
+ for (i = (int) strlen(filename); i && filename[i] != '/' &&
+ filename[i] != '\\' && filename[i] != XIO_PATHSEP; i--);
+ if (filename[i] == '/' || filename[i] == '\\'
+ || filename[i] == XIO_PATHSEP)
+ i++;
+ directory[i] = 0;
+ i--;
+ for (; i >= 0; i--)
+ directory[i] = filename[i];
+ return (mystrdup(directory));
+}
+
+xio_path xio_getfilename(CONST char *basename, CONST char *extension)
+{
+ static char name[40];
+ int nimage = 0;
+#ifdef _plan9_
+#ifdef _plan9v4_
+#define DIRLEN 116
+ uchar edir[DIRLEN];
+#else
+ char edir[DIRLEN];
+#endif
+#else
+ struct stat sb;
+#endif
+ char *base = xio_fixpath(basename);
+ do {
+ sprintf(name, "%s%i%s", base, nimage++, extension);
+ }
+#ifndef _plan9_
+ while (stat(name, &sb) != -1);
+#else
+#ifdef _plan9v4_
+ while (stat(name, edir, DIRLEN) != -1);
+#else
+ while (stat(name, edir) != -1);
+#endif
+#endif
+ free(base);
+ return (name);
+}
+
+xio_file xio_getrandomexample(xio_path name)
+{
+#ifndef _plan9_
+ static CONST char *CONST paths[] = { /*Where examples should be located? */
+ EXAMPLESPATH, /*Data path when XaoS is propertly installed */
+ "\01" XIO_PATHSEPSTR "examples",
+ /*XaoS was started from root of source tree */
+ "\01" XIO_PATHSEPSTR ".." XIO_PATHSEPSTR "examples",
+ "." XIO_PATHSEPSTR "examples",
+ /*XaoS was started from root of source tree */
+ ".." XIO_PATHSEPSTR "examples",
+ /*XaoS was started from bin directory in source tree */
+ XIO_EMPTYPATH, /*Oops...it's not. Try curent directory */
+ };
+ int i = -1, p;
+ DIR *d = NULL;
+ xio_file f;
+ struct dirent *dir;
+ int n;
+ int max = 0;
+ char *realpath = NULL;
+
+ for (p = 0; p < (int) (sizeof(paths) / sizeof(char *)) && d == NULL;
+ p++) {
+ char *pp = xio_fixpath(paths[p]);
+ d = opendir(pp);
+ free(pp);
+ if (d != NULL) {
+ realpath = xio_fixpath(paths[p]);
+ max = 800 - (int) strlen(realpath);
+ for (i = 0; (dir = readdir(d)) != NULL; i++) {
+ int s = (int) strlen(dir->d_name);
+ if (s > max || s < 4
+ || strcmp(".xpf", dir->d_name + s - 4))
+ i--;
+ /*free(dir); */
+ }
+ if (!i) {
+ /*uih->errstring = "No *.xpf files found"; */
+ closedir(d);
+ free(realpath);
+ d = NULL;
+ continue;
+ }
+ break;
+ }
+ }
+ if (d == NULL) {
+ /*uih->errstring = "Can not open examples directory"; */
+ return NULL;
+ }
+
+
+
+ rewinddir(d);
+ dir = NULL;
+ n = (int) ((number_t) i * rand() / (RAND_MAX + 1.0));
+
+ for (i = 0; i <= n; i++) {
+ int s;
+ do {
+ /*if(dir!=NULL) free(dir); */
+ dir = readdir(d);
+ if (dir == NULL) {
+ /*uih->errstring = "Reading of examples directory failed"; */
+ closedir(d);
+ free(realpath);
+ return NULL;
+ }
+ s = (int) strlen(dir->d_name);
+ }
+ while (s > max || s < 4 || strcmp(".xpf", dir->d_name + s - 4));
+ }
+ if (dir == NULL) {
+ /*uih->errstring = "Reading of examples directory failed"; */
+ closedir(d);
+ free(realpath);
+ return NULL;
+ }
+ strcpy(name, realpath);
+ if (name[strlen(name) - 1] != XIO_PATHSEP)
+ strcat(name, XIO_PATHSEPSTR);
+ strcat(name, dir->d_name);
+ closedir(d);
+ /*free(dir); */
+
+ f = xio_ropen(name);
+ free(realpath);
+ return (f);
+#endif
+}
+
+xio_file xio_getcatalog(CONST char *name)
+{
+ static CONST xio_constpath paths[] = { /*Where catalogs should be located? */
+ CATALOGSPATH, /*Data path when XaoS is propertly installed */
+#ifndef _plan9_
+ "\01" XIO_PATHSEPSTR "catalogs" XIO_PATHSEPSTR,
+ "\01" XIO_PATHSEPSTR ".." XIO_PATHSEPSTR "catalogs" XIO_PATHSEPSTR,
+ "." XIO_PATHSEPSTR "catalogs" XIO_PATHSEPSTR,
+ /*XaoS was started from root of source tree */
+ ".." XIO_PATHSEPSTR "catalogs" XIO_PATHSEPSTR,
+ /*XaoS was started from bin directory in source tree */
+#else
+ "./catalogs/",
+ /*XaoS was started from root of source tree */
+ "../catalogs/",
+ /*XaoS was started from bin directory in source tree */
+#endif
+ XIO_EMPTYPATH, /*Oops...it's not. Try curent directory */
+ };
+ int i;
+ xio_file f = XIO_FAILED;
+ xio_pathdata tmp;
+ for (i = 0;
+ i < (int) (sizeof(paths) / sizeof(char *)) && f == XIO_FAILED;
+ i++) {
+ char *p = xio_fixpath(paths[i]);
+ xio_addfname(tmp, p, name);
+ free(p);
+ f = xio_ropen(tmp);
+ if (f == XIO_FAILED) {
+ xio_addextension(tmp, ".cat");
+ f = xio_ropen(tmp);
+ }
+ }
+ return (f);
+}
+
+xio_file xio_gethelp(void)
+{
+ static CONST xio_constpath paths[] = { /*Where help should be located? */
+ HELPPATH, /*Data path when XaoS is propertly installed */
+#ifndef _plan9_
+ "\01" XIO_PATHSEPSTR "help" XIO_PATHSEPSTR "xaos.hlp",
+ "\01" XIO_PATHSEPSTR ".." XIO_PATHSEPSTR "help" XIO_PATHSEPSTR
+ "xaos.hlp",
+ "." XIO_PATHSEPSTR "help" XIO_PATHSEPSTR "xaos.hlp",
+ /*XaoS was started from root of source tree */
+ ".." XIO_PATHSEPSTR "help" XIO_PATHSEPSTR "xaos.hlp",
+ /*XaoS was started from bin directory in source tree */
+ "." XIO_PATHSEPSTR "xaos.hlp",
+#else
+ "./help/xaos.hlp",
+ /*XaoS was started from root of source tree */
+ "../help/xaos.hlp",
+ /*XaoS was started from bin directory in source tree */
+ "./xaos.hlp",
+#endif
+ /*Oops...it's not. Try curent directory */
+ };
+ int i;
+ xio_file f = XIO_FAILED;
+ for (i = 0;
+ i < (int) (sizeof(paths) / sizeof(char *)) && f == XIO_FAILED;
+ i++) {
+ char *p = xio_fixpath(paths[i]);
+ f = xio_ropen(p);
+ free(p);
+ }
+ return (f);
+}
+
+xio_file xio_gettutorial(CONST char *name, xio_path tmp)
+{
+ int i;
+ xio_file f = XIO_FAILED;
+ static CONST xio_constpath paths[] = { /*Where tutorials should be located? */
+ TUTORIALPATH, /*Data path when XaoS is propertly installed */
+#ifndef _plan9_
+ "\01" XIO_PATHSEPSTR "tutorial" XIO_PATHSEPSTR,
+ "\01" XIO_PATHSEPSTR ".." XIO_PATHSEPSTR "tutorial" XIO_PATHSEPSTR,
+ "." XIO_PATHSEPSTR "tutorial" XIO_PATHSEPSTR, /*XaoS was started from root of source tree */
+ ".." XIO_PATHSEPSTR "tutorial" XIO_PATHSEPSTR, /*XaoS was started from bin directory in source tree */
+#else
+ "./tutorial/", /*XaoS was started from root of source tree */
+ "../tutorial/", /*XaoS was started from bin directory in source tree */
+#endif
+ XIO_EMPTYPATH, /*Oops...it's not. Try curent directory */
+ };
+
+ for (i = 0;
+ i < (int) (sizeof(paths) / sizeof(char *)) && f == XIO_FAILED;
+ i++) {
+ char *p = xio_fixpath(paths[i]);
+ xio_addfname(tmp, p, name);
+ f = xio_ropen(tmp);
+ free(p);
+ }
+ return (f);
+}
+
+int xio_exist(xio_constpath name)
+{
+#ifdef _plan9_
+ return (0);
+#else
+ struct stat buf;
+ return (!stat(name, &buf));
+#endif
+}
+
+static int sputc(int c, xio_file f)
+{
+ return putc(c, (FILE *) f->data);
+}
+
+static int sputs(CONST char *c, xio_file f)
+{
+ return fputs(c, (FILE *) f->data);
+}
+
+static int sungetc(int c, xio_file f)
+{
+ return ungetc(c, (FILE *) f->data);
+}
+
+static int sgetc(xio_file f)
+{
+ return getc((FILE *) f->data);
+}
+
+static int sfeof(xio_file f)
+{
+ return feof((FILE *) f->data);
+}
+
+static int sflush(xio_file f)
+{
+ return fflush((FILE *) f->data);
+}
+
+static int ssclose(xio_file f)
+{
+ int r = fclose((FILE *) f->data);
+ free(f);
+ return r;
+}
+
+xio_file xio_ropen(CONST char *name)
+{
+ xio_file f = (xio_file) calloc(1, sizeof(*f));
+ name = xio_fixpath(name);
+ f->data = (void *) fopen(name, "rt");
+ /*free (name); */
+ if (!f->data) {
+ free(f);
+ return 0;
+ }
+ f->fclose = ssclose;
+ f->xeof = sfeof;
+ f->fgetc = sgetc;
+ f->fungetc = sungetc;
+ return f;
+}
+
+xio_file xio_wopen(CONST char *name)
+{
+ xio_file f = (xio_file) calloc(1, sizeof(*f));
+ name = xio_fixpath(name);
+ f->data = (void *) fopen(name, "wt");
+ /*free (name); */
+ if (!f->data) {
+ free(f);
+ return 0;
+ }
+ f->fputc = sputc;
+ f->fputs = sputs;
+ f->fclose = ssclose;
+ f->flush = sflush;
+ return f;
+}
+
+#ifdef DJGPP
+#define DRIVES
+#endif
+#ifdef _WIN32
+#define DRIVES
+#endif
+void xio_init(CONST char *name)
+{
+ if (getenv("HOME"))
+ xio_homedir = mystrdup(getenv("HOME"));
+ else
+ xio_homedir = mystrdup("./");
+ if (
+#ifdef DRIVES
+ (((name[0] >= 'a' && name[0] <= 'z')
+ || (name[0] >= 'A' && name[0] <= 'Z')) && name[1] == ':'
+ && (name[2] == '\\' || name[2] == '/')) ||
+#endif
+ name[0] == '/' || name[0] == '\\' || name[0] == XIO_PATHSEP
+ || name[0] == '~') {
+ char *c = mystrdup(name);
+ int i;
+ int pos = 0;
+ for (i = 0; i < (int) strlen(c); i++)
+ if (name[i] == '/' || name[i] == '\\'
+ || name[i] == XIO_PATHSEP)
+ pos = i;
+ c[pos] = 0;
+ xio_appdir = xio_fixpath(c);
+ free(c);
+ } else {
+ char buf[4096];
+ buf[0] = '.';
+ buf[1] = 0;
+#ifndef _plan9_
+ getcwd(buf, sizeof(buf));
+#endif
+ xio_appdir = mystrdup(buf);
+ {
+ char *c = mystrdup(name), *c1;
+ int i;
+ int pos = 0;
+ for (i = 0; i < (int) strlen(c); i++)
+ if (name[i] == '/' || name[i] == '\\'
+ || name[i] == XIO_PATHSEP)
+ pos = i;
+ c[pos] = 0;
+ c1 = (char *) malloc(strlen(c) + strlen(xio_appdir) + 2);
+ sprintf(c1, "%s%s%s", xio_appdir, XIO_PATHSEPSTR, c);
+ free(c);
+ free(xio_appdir);
+ xio_appdir = c1;
+ }
+ }
+}
+
+void xio_uninit()
+{
+ free(xio_appdir);
+ free(xio_homedir);
+}
diff --git a/src/util/xstring.c b/src/util/xstring.c
new file mode 100644
index 0000000..02959f6
--- /dev/null
+++ b/src/util/xstring.c
@@ -0,0 +1,141 @@
+#ifndef _plan9_
+#include <string.h>
+#include <stdlib.h>
+#else
+#include <u.h>
+#include <libc.h>
+#endif
+
+#include <xio.h>
+#include <misc-f.h>
+struct fr {
+ char *string;
+ int pos;
+ int allocedsize;
+ int stringsize;
+};
+
+char *mystrdup(const char *c)
+{
+ int l = strlen(c);
+ char *d = malloc(l + 1);
+ if (!d)
+ return NULL;
+ memcpy(d, c, l + 1);
+ return d;
+}
+
+static int sputc(int c, xio_file s)
+{
+ struct fr *f = (struct fr *) s->data;
+ if (f->pos >= f->allocedsize - 1) {
+ char *c = (char *) realloc(f->string, f->allocedsize * 2);
+ if (!c)
+ return XIO_EOF;
+ f->string = c;
+ f->allocedsize *= 2;
+ }
+ f->string[f->pos++] = c;
+ if (f->pos >= f->stringsize)
+ f->string[f->pos] = 0, f->stringsize = f->pos;
+ return 0;
+}
+
+static int sputs(CONST char *c, xio_file s)
+{
+ int l = strlen(c);
+ struct fr *f = (struct fr *) s->data;
+ while (f->pos + l >= f->allocedsize - 1) {
+ char *c = (char *) realloc(f->string, f->allocedsize * 2);
+ if (!c)
+ return XIO_EOF;
+ f->string = c;
+ f->allocedsize *= 2;
+ }
+ memcpy(f->string + f->pos, c, l);
+ f->pos += l;
+ if (f->pos >= f->stringsize)
+ f->string[f->pos] = 0, f->stringsize = f->pos;
+ return 0;
+}
+
+static int sungetc(int c, xio_file s)
+{
+ struct fr *f = (struct fr *) s->data;
+ f->pos--;
+ /*f->string[f->pos]=c; */
+ return 0;
+}
+
+static int sgetc(xio_file s)
+{
+ struct fr *f = (struct fr *) s->data;
+ if (f->pos == f->stringsize)
+ return XIO_EOF;
+ return f->string[f->pos++];
+}
+
+static int sfeof(xio_file s)
+{
+ struct fr *f = (struct fr *) s->data;
+ return (f->pos == f->stringsize);
+}
+
+static int srclose(xio_file s)
+{
+ struct fr *f = (struct fr *) s->data;
+ free(f->string);
+ free(f);
+ free(s);
+ return 0;
+}
+
+static int swclose(xio_file s)
+{
+ struct fr *f = (struct fr *) s->data;
+ f->string = (char *) realloc(f->string, f->stringsize + 1);
+ /*free(s);
+ free(f); */
+ return 0;
+}
+
+char *xio_getstring(xio_file s)
+{
+ struct fr *f = (struct fr *) s->data;
+ char *c = f->string;
+ free(f);
+ free(s);
+ return c;
+}
+
+xio_file xio_strropen(CONST char *string)
+{
+ xio_file s = (xio_file) calloc(1, sizeof(*s));
+ struct fr *f = (struct fr *) calloc(1, sizeof(*f));
+ s->data = f;
+ f->pos = 0;
+ f->string = (char *) string;
+ f->stringsize = strlen(string);
+ s->fclose = srclose;
+ s->xeof = sfeof;
+ s->fgetc = sgetc;
+ s->fungetc = sungetc;
+ return s;
+}
+
+#define PAGE 4096
+xio_file xio_strwopen(void)
+{
+ xio_file s = (xio_file) calloc(1, sizeof(*s));
+ struct fr *f = (struct fr *) calloc(1, sizeof(*f));
+ s->data = f;
+ f->pos = 0;
+ f->string = (char *) malloc(PAGE);
+ f->allocedsize = PAGE;
+ f->stringsize = 0;
+ s->fputc = sputc;
+ s->fputs = sputs;
+ s->fclose = swclose;
+ s->flush = NULL;
+ return s;
+}