mirror of
https://github.com/rkd77/elinks.git
synced 2024-11-04 08:17:17 -05:00
7645a836fc
straconcat reads the args with va_arg(ap, const unsigned char *), and the NULL macro may have the wrong type (e.g. int). Many places pass string literals of type char * to straconcat. This is in principle also a violation, but I'm ignoring it for now because if it becomes a problem with some C implementation, then so will the use of unsigned char * with printf "%s", which is so widespread in ELinks that I'm not going to try fixing it now.
222 lines
4.5 KiB
C
222 lines
4.5 KiB
C
/* List menus functions */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "bfu/listmenu.h"
|
|
#include "bfu/menu.h"
|
|
#include "session/session.h"
|
|
#include "util/conv.h"
|
|
#include "util/error.h"
|
|
#include "util/string.h"
|
|
#include "viewer/text/form.h" /* selected_item() and get_current_state() */
|
|
|
|
/* TODO: massive cleanup, merging, code redundancy tracking between this file
|
|
* and bfu/menu.c (and perhaps others.)
|
|
* We should unify and clarify menu-related code. */
|
|
|
|
static int
|
|
menu_contains(struct menu_item *m, int f)
|
|
{
|
|
if (m->func != do_select_submenu)
|
|
return (long) m->data == f;
|
|
|
|
foreach_menu_item (m, m->data) {
|
|
if (menu_contains(m, f))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
do_select_submenu(struct terminal *term, void *menu_, void *ses_)
|
|
{
|
|
struct menu_item *menu = menu_;
|
|
struct session *ses = ses_;
|
|
struct menu_item *m;
|
|
int def = int_max(0, get_current_state(ses));
|
|
int sel = 0;
|
|
|
|
foreach_menu_item (m, menu) {
|
|
if (menu_contains(m, def)) {
|
|
sel = m - menu;
|
|
break;
|
|
}
|
|
}
|
|
|
|
do_menu_selected(term, menu, ses, sel, 0);
|
|
}
|
|
|
|
void
|
|
new_menu_item(struct list_menu *menu, unsigned char *name, int data, int fullname)
|
|
/* name == NULL - up; data == -1 - down */
|
|
{
|
|
struct menu_item *new_menu_item = NULL; /* no uninitialized warnings */
|
|
struct menu_item **items;
|
|
size_t stack_size = menu->stack_size;
|
|
|
|
if (!name) {
|
|
menu->stack_size--;
|
|
return;
|
|
|
|
} else if (data != -1 && menu->stack_size == 0) {
|
|
mem_free(name);
|
|
return;
|
|
}
|
|
|
|
clr_spaces(name);
|
|
if (!name[0]) {
|
|
mem_free(name);
|
|
name = stracpy(" ");
|
|
if (!name) return;
|
|
}
|
|
|
|
if (data == -1) {
|
|
int size = (menu->stack_size + 1) * sizeof(*menu->stack);
|
|
struct menu_item **stack = mem_realloc(menu->stack, size);
|
|
|
|
if (stack) {
|
|
menu->stack = stack;
|
|
new_menu_item = new_menu(NO_INTL);
|
|
}
|
|
|
|
if (!stack || !new_menu_item) {
|
|
if (new_menu_item) mem_free(new_menu_item);
|
|
mem_free(name);
|
|
return;
|
|
}
|
|
|
|
/* Since we increment @stack_size use cached value */
|
|
menu->stack[menu->stack_size++] = new_menu_item;
|
|
|
|
if (menu->stack_size == 1) {
|
|
mem_free(name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
items = &menu->stack[stack_size - 1];
|
|
|
|
if (data == -1) {
|
|
add_to_menu(items, name, NULL, ACT_MAIN_NONE,
|
|
do_select_submenu,
|
|
new_menu_item, SUBMENU);
|
|
} else {
|
|
add_to_menu(items, name, NULL, ACT_MAIN_NONE,
|
|
selected_item,
|
|
(void *) (long) data, (fullname ? MENU_FULLNAME : 0));
|
|
}
|
|
|
|
if (stack_size >= 2) {
|
|
struct menu_item *below = menu->stack[stack_size - 2];
|
|
|
|
while (below->text) below++;
|
|
below[-1].data = *items;
|
|
}
|
|
}
|
|
|
|
void
|
|
init_menu(struct list_menu *menu)
|
|
{
|
|
menu->stack_size = 0;
|
|
menu->stack = NULL;
|
|
new_menu_item(menu, stracpy(""), -1, 0);
|
|
}
|
|
|
|
/* TODO: merge with free_menu_items() in bfu/menu.h --Zas */
|
|
void
|
|
free_menu(struct menu_item *m) /* Grrr. Recursion */
|
|
{
|
|
struct menu_item *mm;
|
|
|
|
if (!m) return; /* XXX: Who knows... need to be verified */
|
|
|
|
foreach_menu_item (mm, m) {
|
|
mem_free_if(mm->text);
|
|
if (mm->func == do_select_submenu) free_menu(mm->data);
|
|
}
|
|
|
|
mem_free(m);
|
|
}
|
|
|
|
struct menu_item *
|
|
detach_menu(struct list_menu *menu)
|
|
{
|
|
struct menu_item *i = NULL;
|
|
|
|
if (menu->stack) {
|
|
if (menu->stack_size) i = menu->stack[0];
|
|
mem_free(menu->stack);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void
|
|
destroy_menu(struct list_menu *menu)
|
|
{
|
|
if (menu->stack) free_menu(menu->stack[0]);
|
|
detach_menu(menu);
|
|
}
|
|
|
|
void
|
|
menu_labels(struct menu_item *items, unsigned char *base, unsigned char **lbls)
|
|
{
|
|
struct menu_item *item;
|
|
unsigned char *bs;
|
|
|
|
foreach_menu_item (item, items) {
|
|
bs = (item->flags & MENU_FULLNAME) ? (unsigned char *) ""
|
|
: base;
|
|
bs = straconcat(bs, item->text, (unsigned char *) NULL);
|
|
if (!bs) continue;
|
|
|
|
if (item->func == do_select_submenu) {
|
|
add_to_strn(&bs, " ");
|
|
menu_labels(item->data, bs, lbls);
|
|
mem_free(bs);
|
|
} else {
|
|
assert(item->func == selected_item);
|
|
lbls[(long) item->data] = bs;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
add_select_item(struct list_menu *menu, struct string *string,
|
|
struct string *orig_string, unsigned char **value,
|
|
int order, int dont_add)
|
|
{
|
|
int pos = order - 1;
|
|
|
|
assert(menu && string);
|
|
|
|
if (!string->source) return;
|
|
|
|
assert(value && pos >= 0);
|
|
|
|
if (!value[pos])
|
|
/* <select> values are not mangled by various encode_*()
|
|
* functions, therefore we need to store them in the
|
|
* original document encoding. */
|
|
value[pos] = memacpy(orig_string->source, orig_string->length);
|
|
|
|
if (dont_add) {
|
|
done_string(string);
|
|
} else {
|
|
new_menu_item(menu, string->source, pos, 1);
|
|
string->source = NULL;
|
|
string->length = 0;
|
|
}
|
|
done_string(orig_string);
|
|
}
|