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