Commit 35e9a274 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'kconfig' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild

Pull kconfig changes from Michal Marek:
 "kconfig in v3.7 is going to
   - initialize ncurses only once in menuconfig
   - be able to jump to a search result in menuconfig
   - change the misnomer oldnoconfig to a more meaningful name
     olddefconfig, keeping the old name as alias"

* 'kconfig' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild:
  kconfig: replace 'oldnoconfig' with 'olddefconfig', and keep the old name as an alias
  menuconfig: Assign jump keys per-page instead of globally
  menuconfig: Do not open code textbox scroll up/down
  menuconfig: Add jump keys to search results
  menuconfig: Extend dialog_textbox so that it can return to a scrolled position
  menuconfig: Extend dialog_textbox so that it can exit on arbitrary keypresses
  menuconfig: Remove superfluous conditionnal
  kconfig: document oldnoconfig to what it really does in conf.c
  kconfig/mconf.c: revision of curses initialization.
parents ae3e4628 fb16d891
......@@ -76,11 +76,17 @@ PHONY += allnoconfig allyesconfig allmodconfig alldefconfig randconfig
allnoconfig allyesconfig allmodconfig alldefconfig randconfig: $(obj)/conf
$< --$@ $(Kconfig)
PHONY += listnewconfig oldnoconfig savedefconfig defconfig
PHONY += listnewconfig olddefconfig oldnoconfig savedefconfig defconfig
listnewconfig oldnoconfig: $(obj)/conf
listnewconfig olddefconfig: $(obj)/conf
$< --$@ $(Kconfig)
# oldnoconfig is an alias of olddefconfig, because people already are dependent
# on its behavior(sets new symbols to their default value but not 'n') with the
# counter-intuitive name.
oldnoconfig: $(obj)/conf
$< --olddefconfig $(Kconfig)
savedefconfig: $(obj)/conf
$< --$@=defconfig $(Kconfig)
......@@ -114,7 +120,7 @@ help:
@echo ' alldefconfig - New config with all symbols set to default'
@echo ' randconfig - New config with random answer to all options'
@echo ' listnewconfig - List new options'
@echo ' oldnoconfig - Same as silentoldconfig but sets new symbols to their default value'
@echo ' olddefconfig - Same as silentoldconfig but sets new symbols to their default value'
# lxdialog stuff
check-lxdialog := $(srctree)/$(src)/lxdialog/check-lxdialog.sh
......
......@@ -32,7 +32,7 @@ enum input_mode {
defconfig,
savedefconfig,
listnewconfig,
oldnoconfig,
olddefconfig,
} input_mode = oldaskconfig;
static int indent = 1;
......@@ -365,7 +365,7 @@ static void conf(struct menu *menu)
case P_MENU:
if ((input_mode == silentoldconfig ||
input_mode == listnewconfig ||
input_mode == oldnoconfig) &&
input_mode == olddefconfig) &&
rootEntry != menu) {
check_conf(menu);
return;
......@@ -429,7 +429,7 @@ static void check_conf(struct menu *menu)
if (sym->name && !sym_is_choice_value(sym)) {
printf("%s%s\n", CONFIG_, sym->name);
}
} else if (input_mode != oldnoconfig) {
} else if (input_mode != olddefconfig) {
if (!conf_cnt++)
printf(_("*\n* Restart config...\n*\n"));
rootEntry = menu_get_parent_menu(menu);
......@@ -454,7 +454,13 @@ static struct option long_opts[] = {
{"alldefconfig", no_argument, NULL, alldefconfig},
{"randconfig", no_argument, NULL, randconfig},
{"listnewconfig", no_argument, NULL, listnewconfig},
{"oldnoconfig", no_argument, NULL, oldnoconfig},
{"olddefconfig", no_argument, NULL, olddefconfig},
/*
* oldnoconfig is an alias of olddefconfig, because people already
* are dependent on its behavior(sets new symbols to their default
* value but not 'n') with the counter-intuitive name.
*/
{"oldnoconfig", no_argument, NULL, olddefconfig},
{NULL, 0, NULL, 0}
};
......@@ -467,7 +473,8 @@ static void conf_usage(const char *progname)
printf(" --oldaskconfig Start a new configuration using a line-oriented program\n");
printf(" --oldconfig Update a configuration using a provided .config as base\n");
printf(" --silentoldconfig Same as oldconfig, but quietly, additionally update deps\n");
printf(" --oldnoconfig Same as silentoldconfig but set new symbols to no\n");
printf(" --olddefconfig Same as silentoldconfig but sets new symbols to their default value\n");
printf(" --oldnoconfig An alias of olddefconfig\n");
printf(" --defconfig <file> New config with default defined in <file>\n");
printf(" --savedefconfig <file> Save the minimal current configuration to <file>\n");
printf(" --allnoconfig New config where all options are answered with no\n");
......@@ -520,7 +527,7 @@ int main(int ac, char **av)
case allmodconfig:
case alldefconfig:
case listnewconfig:
case oldnoconfig:
case olddefconfig:
break;
case '?':
conf_usage(progname);
......@@ -565,7 +572,7 @@ int main(int ac, char **av)
case oldaskconfig:
case oldconfig:
case listnewconfig:
case oldnoconfig:
case olddefconfig:
conf_read(NULL);
break;
case allnoconfig:
......@@ -645,7 +652,7 @@ int main(int ac, char **av)
/* fall through */
case oldconfig:
case listnewconfig:
case oldnoconfig:
case olddefconfig:
case silentoldconfig:
/* Update until a loop caused no more changes */
do {
......@@ -653,7 +660,7 @@ int main(int ac, char **av)
check_conf(&rootmenu);
} while (conf_cnt &&
(input_mode != listnewconfig &&
input_mode != oldnoconfig));
input_mode != olddefconfig));
break;
}
......
......@@ -12,6 +12,7 @@ extern "C" {
#include <assert.h>
#include <stdio.h>
#include <sys/queue.h>
#ifndef __cplusplus
#include <stdbool.h>
#endif
......@@ -173,6 +174,16 @@ struct menu {
#define MENU_CHANGED 0x0001
#define MENU_ROOT 0x0002
struct jump_key {
CIRCLEQ_ENTRY(jump_key) entries;
size_t offset;
struct menu *target;
int index;
};
CIRCLEQ_HEAD(jk_head, jump_key);
#define JUMP_NB 9
extern struct file *file_list;
extern struct file *current_file;
struct file *lookup_file(const char *name);
......
......@@ -21,8 +21,10 @@ P(menu_get_root_menu,struct menu *,(struct menu *menu));
P(menu_get_parent_menu,struct menu *,(struct menu *menu));
P(menu_has_help,bool,(struct menu *menu));
P(menu_get_help,const char *,(struct menu *menu));
P(get_symbol_str, void, (struct gstr *r, struct symbol *sym));
P(get_relations_str, struct gstr, (struct symbol **sym_arr));
P(get_symbol_str, void, (struct gstr *r, struct symbol *sym, struct jk_head
*head));
P(get_relations_str, struct gstr, (struct symbol **sym_arr, struct jk_head
*head));
P(menu_get_ext_help,void,(struct menu *menu, struct gstr *help));
/* symbol.c */
......
......@@ -144,6 +144,7 @@ struct dialog_info {
*/
extern struct dialog_info dlg;
extern char dialog_input_result[];
extern int saved_x, saved_y; /* Needed in signal handler in mconf.c */
/*
* Function prototypes
......@@ -209,7 +210,13 @@ int first_alpha(const char *string, const char *exempt);
int dialog_yesno(const char *title, const char *prompt, int height, int width);
int dialog_msgbox(const char *title, const char *prompt, int height,
int width, int pause);
int dialog_textbox(const char *title, const char *file, int height, int width);
typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void
*_data);
int dialog_textbox(const char *title, char *tbuf, int initial_height,
int initial_width, int *keys, int *_vscroll, int *_hscroll,
update_text_fn update_text, void *data);
int dialog_menu(const char *title, const char *prompt,
const void *selected, int *s_scroll);
int dialog_checklist(const char *title, const char *prompt, int height,
......
......@@ -22,23 +22,25 @@
#include "dialog.h"
static void back_lines(int n);
static void print_page(WINDOW * win, int height, int width);
static void print_line(WINDOW * win, int row, int width);
static void print_page(WINDOW *win, int height, int width, update_text_fn
update_text, void *data);
static void print_line(WINDOW *win, int row, int width);
static char *get_line(void);
static void print_position(WINDOW * win);
static int hscroll;
static int begin_reached, end_reached, page_length;
static const char *buf;
static const char *page;
static char *buf;
static char *page;
/*
* refresh window content
*/
static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
int cur_y, int cur_x)
int cur_y, int cur_x, update_text_fn update_text,
void *data)
{
print_page(box, boxh, boxw);
print_page(box, boxh, boxw, update_text, data);
print_position(dialog);
wmove(dialog, cur_y, cur_x); /* Restore cursor position */
wrefresh(dialog);
......@@ -47,14 +49,18 @@ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
/*
* Display text from a file in a dialog box.
*
* keys is a null-terminated array
* update_text() may not add or remove any '\n' or '\0' in tbuf
*/
int dialog_textbox(const char *title, const char *tbuf,
int initial_height, int initial_width)
int dialog_textbox(const char *title, char *tbuf, int initial_height,
int initial_width, int *keys, int *_vscroll, int *_hscroll,
update_text_fn update_text, void *data)
{
int i, x, y, cur_x, cur_y, key = 0;
int height, width, boxh, boxw;
int passed_end;
WINDOW *dialog, *box;
bool done = false;
begin_reached = 1;
end_reached = 0;
......@@ -63,6 +69,15 @@ int dialog_textbox(const char *title, const char *tbuf,
buf = tbuf;
page = buf; /* page is pointer to start of page to be displayed */
if (_vscroll && *_vscroll) {
begin_reached = 0;
for (i = 0; i < *_vscroll; i++)
get_line();
}
if (_hscroll)
hscroll = *_hscroll;
do_resize:
getmaxyx(stdscr, height, width);
if (height < 8 || width < 8)
......@@ -120,9 +135,10 @@ do_resize:
/* Print first page of text */
attr_clear(box, boxh, boxw, dlg.dialog.atr);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text,
data);
while ((key != KEY_ESC) && (key != '\n')) {
while (!done) {
key = wgetch(dialog);
switch (key) {
case 'E': /* Exit */
......@@ -130,16 +146,17 @@ do_resize:
case 'X':
case 'x':
case 'q':
delwin(box);
delwin(dialog);
return 0;
case '\n':
done = true;
break;
case 'g': /* First page */
case KEY_HOME:
if (!begin_reached) {
begin_reached = 1;
page = buf;
refresh_text_box(dialog, box, boxh, boxw,
cur_y, cur_x);
cur_y, cur_x, update_text,
data);
}
break;
case 'G': /* Last page */
......@@ -149,45 +166,18 @@ do_resize:
/* point to last char in buf */
page = buf + strlen(buf);
back_lines(boxh);
refresh_text_box(dialog, box, boxh, boxw,
cur_y, cur_x);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
cur_x, update_text, data);
break;
case 'K': /* Previous line */
case 'k':
case KEY_UP:
if (!begin_reached) {
back_lines(page_length + 1);
/* We don't call print_page() here but use
* scrolling to ensure faster screen update.
* However, 'end_reached' and 'page_length'
* should still be updated, and 'page' should
* point to start of next page. This is done
* by calling get_line() in the following
* 'for' loop. */
scrollok(box, TRUE);
wscrl(box, -1); /* Scroll box region down one line */
scrollok(box, FALSE);
page_length = 0;
passed_end = 0;
for (i = 0; i < boxh; i++) {
if (!i) {
/* print first line of page */
print_line(box, 0, boxw);
wnoutrefresh(box);
} else
/* Called to update 'end_reached' and 'page' */
get_line();
if (!passed_end)
page_length++;
if (end_reached && !passed_end)
passed_end = 1;
}
if (begin_reached)
break;
print_position(dialog);
wmove(dialog, cur_y, cur_x); /* Restore cursor position */
wrefresh(dialog);
}
back_lines(page_length + 1);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
cur_x, update_text, data);
break;
case 'B': /* Previous page */
case 'b':
......@@ -196,23 +186,18 @@ do_resize:
if (begin_reached)
break;
back_lines(page_length + boxh);
refresh_text_box(dialog, box, boxh, boxw,
cur_y, cur_x);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
cur_x, update_text, data);
break;
case 'J': /* Next line */
case 'j':
case KEY_DOWN:
if (!end_reached) {
begin_reached = 0;
scrollok(box, TRUE);
scroll(box); /* Scroll box region up one line */
scrollok(box, FALSE);
print_line(box, boxh - 1, boxw);
wnoutrefresh(box);
print_position(dialog);
wmove(dialog, cur_y, cur_x); /* Restore cursor position */
wrefresh(dialog);
}
if (end_reached)
break;
back_lines(page_length - 1);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
cur_x, update_text, data);
break;
case KEY_NPAGE: /* Next page */
case ' ':
......@@ -221,8 +206,8 @@ do_resize:
break;
begin_reached = 0;
refresh_text_box(dialog, box, boxh, boxw,
cur_y, cur_x);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
cur_x, update_text, data);
break;
case '0': /* Beginning of line */
case 'H': /* Scroll left */
......@@ -237,8 +222,8 @@ do_resize:
hscroll--;
/* Reprint current page to scroll horizontally */
back_lines(page_length);
refresh_text_box(dialog, box, boxh, boxw,
cur_y, cur_x);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
cur_x, update_text, data);
break;
case 'L': /* Scroll right */
case 'l':
......@@ -248,11 +233,12 @@ do_resize:
hscroll++;
/* Reprint current page to scroll horizontally */
back_lines(page_length);
refresh_text_box(dialog, box, boxh, boxw,
cur_y, cur_x);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
cur_x, update_text, data);
break;
case KEY_ESC:
key = on_key_esc(dialog);
if (on_key_esc(dialog) == KEY_ESC)
done = true;
break;
case KEY_RESIZE:
back_lines(height);
......@@ -260,11 +246,31 @@ do_resize:
delwin(dialog);
on_key_resize();
goto do_resize;
default:
for (i = 0; keys[i]; i++) {
if (key == keys[i]) {
done = true;
break;
}
}
}
}
delwin(box);
delwin(dialog);
return key; /* ESC pressed */
if (_vscroll) {
const char *s;
s = buf;
*_vscroll = 0;
back_lines(page_length);
while (s < page && (s = strchr(s, '\n'))) {
(*_vscroll)++;
s++;
}
}
if (_hscroll)
*_hscroll = hscroll;
return key;
}
/*
......@@ -301,12 +307,23 @@ static void back_lines(int n)
}
/*
* Print a new page of text. Called by dialog_textbox().
* Print a new page of text.
*/
static void print_page(WINDOW * win, int height, int width)
static void print_page(WINDOW *win, int height, int width, update_text_fn
update_text, void *data)
{
int i, passed_end = 0;
if (update_text) {
char *end;
for (i = 0; i < height; i++)
get_line();
end = page;
back_lines(height);
update_text(buf, page - buf, end - buf, data);
}
page_length = 0;
for (i = 0; i < height; i++) {
print_line(win, i, width);
......@@ -319,7 +336,7 @@ static void print_page(WINDOW * win, int height, int width)
}
/*
* Print a new line of text. Called by dialog_textbox() and print_page().
* Print a new line of text.
*/
static void print_line(WINDOW * win, int row, int width)
{
......@@ -357,10 +374,8 @@ static char *get_line(void)
end_reached = 0;
while (*page != '\n') {
if (*page == '\0') {
if (!end_reached) {
end_reached = 1;
break;
}
end_reached = 1;
break;
} else if (i < MAX_LEN)
line[i++] = *(page++);
else {
......@@ -373,7 +388,7 @@ static char *get_line(void)
if (i <= MAX_LEN)
line[i] = '\0';
if (!end_reached)
page++; /* move pass '\n' */
page++; /* move past '\n' */
return line;
}
......
......@@ -23,6 +23,9 @@
#include "dialog.h"
/* Needed in signal handler in mconf.c */
int saved_x, saved_y;
struct dialog_info dlg;
static void set_mono_theme(void)
......@@ -273,6 +276,10 @@ int init_dialog(const char *backtitle)
int height, width;
initscr(); /* Init curses */
/* Get current cursor position for signal handler in mconf.c */
getyx(stdscr, saved_y, saved_x);
getmaxyx(stdscr, height, width);
if (height < 19 || width < 80) {
endwin();
......
......@@ -236,16 +236,19 @@ search_help[] = N_(
"Result:\n"
"-----------------------------------------------------------------\n"
"Symbol: FOO [=m]\n"
"Type : tristate\n"
"Prompt: Foo bus is used to drive the bar HW\n"
"Defined at drivers/pci/Kconfig:47\n"
"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
"Location:\n"
" -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
" -> PCI support (PCI [=y])\n"
" -> PCI access mode (<choice> [=y])\n"
"Selects: LIBCRC32\n"
"Selected by: BAR\n"
" Defined at drivers/pci/Kconfig:47\n"
" Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
" Location:\n"
" -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
" -> PCI support (PCI [=y])\n"
"(1) -> PCI access mode (<choice> [=y])\n"
" Selects: LIBCRC32\n"
" Selected by: BAR\n"
"-----------------------------------------------------------------\n"
"o The line 'Type:' shows the type of the configuration option for\n"
" this symbol (boolean, tristate, string, ...)\n"
"o The line 'Prompt:' shows the text used in the menu structure for\n"
" this symbol\n"
"o The 'Defined at' line tell at what file / line number the symbol\n"
......@@ -254,8 +257,12 @@ search_help[] = N_(
" this symbol to be visible in the menu (selectable)\n"
"o The 'Location:' lines tell where in the menu structure this symbol\n"
" is located\n"
" A location followed by a [=y] indicate that this is a selectable\n"
" menu item - and current value is displayed inside brackets.\n"
" A location followed by a [=y] indicates that this is a\n"
" selectable menu item - and the current value is displayed inside\n"
" brackets.\n"
" Press the key in the (#) prefix to jump directly to that\n"
" location. You will be returned to the current search results\n"
" after exiting this new menu.\n"
"o The 'Selects:' line tell what symbol will be automatically\n"
" selected if this symbol is selected (y or m)\n"
"o The 'Selected by' line tell what symbol has selected this symbol\n"
......@@ -273,13 +280,15 @@ static struct menu *current_menu;
static int child_count;
static int single_menu_mode;
static int show_all_options;
static int saved_x, saved_y;
static void conf(struct menu *menu);
static void conf(struct menu *menu, struct menu *active_menu);
static void conf_choice(struct menu *menu);
static void conf_string(struct menu *menu);
static void conf_load(void);
static void conf_save(void);
static int show_textbox_ext(const char *title, char *text, int r, int c,
int *keys, int *vscroll, int *hscroll,
update_text_fn update_text, void *data);
static void show_textbox(const char *title, const char *text, int r, int c);
static void show_helptext(const char *title, const char *text);
static void show_help(struct menu *menu);
......@@ -302,12 +311,47 @@ static void set_config_filename(const char *config_filename)
}
struct search_data {
struct jk_head *head;
struct menu **targets;
int *keys;
};
static void update_text(char *buf, size_t start, size_t end, void *_data)
{
struct search_data *data = _data;
struct jump_key *pos;
int k = 0;
CIRCLEQ_FOREACH(pos, data->head, entries) {
if (pos->offset >= start && pos->offset < end) {
char header[4];
if (k < JUMP_NB) {
int key = '0' + (pos->index % JUMP_NB) + 1;
sprintf(header, "(%c)", key);
data->keys[k] = key;
data->targets[k] = pos->target;
k++;
} else {
sprintf(header, " ");
}
memcpy(buf + pos->offset, header, sizeof(header) - 1);
}
}
data->keys[k] = 0;
}
static void search_conf(void)
{
struct symbol **sym_arr;
struct gstr res;
char *dialog_input;
int dres;
int dres, vscroll = 0, hscroll = 0;
bool again;
again:
dialog_clear();
dres = dialog_inputbox(_("Search Configuration Parameter"),
......@@ -330,10 +374,30 @@ again:
dialog_input += strlen(CONFIG_);
sym_arr = sym_re_search(dialog_input);
res = get_relations_str(sym_arr);
do {
struct jk_head head = CIRCLEQ_HEAD_INITIALIZER(head);
struct menu *targets[JUMP_NB];
int keys[JUMP_NB + 1], i;
struct search_data data = {
.head = &head,
.targets = targets,
.keys = keys,
};
res = get_relations_str(sym_arr, &head);
dres = show_textbox_ext(_("Search Results"), (char *)
str_get(&res), 0, 0, keys, &vscroll,
&hscroll, &update_text, (void *)
&data);
again = false;
for (i = 0; i < JUMP_NB && keys[i]; i++)
if (dres == keys[i]) {
conf(targets[i]->parent, targets[i]);
again = true;
}
str_free(&res);
} while (again);
free(sym_arr);
show_textbox(_("Search Results"), str_get(&res), 0, 0);
str_free(&res);
}
static void build_conf(struct menu *menu)
......@@ -514,12 +578,11 @@ conf_childs:
indent -= doint;
}
static void conf(struct menu *menu)
static void conf(struct menu *menu, struct menu *active_menu)
{
struct menu *submenu;
const char *prompt = menu_get_prompt(menu);
struct symbol *sym;
struct menu *active_menu = NULL;
int res;
int s_scroll = 0;
......@@ -562,13 +625,13 @@ static void conf(struct menu *menu)
if (single_menu_mode)
submenu->data = (void *) (long) !submenu->data;
else
conf(submenu);
conf(submenu, NULL);
break;
case 't':
if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
conf_choice(submenu);
else if (submenu->prompt->type == P_MENU)
conf(submenu);
conf(submenu, NULL);
break;
case 's':
conf_string(submenu);
......@@ -607,7 +670,7 @@ static void conf(struct menu *menu)
if (item_is_tag('t'))
sym_toggle_tristate_value(sym);
else if (item_is_tag('m'))
conf(submenu);
conf(submenu, NULL);
break;
case 7:
search_conf();
......@@ -619,10 +682,19 @@ static void conf(struct menu *menu)
}
}
static void show_textbox(const char *title, const char *text, int r, int c)
static int show_textbox_ext(const char *title, char *text, int r, int c, int
*keys, int *vscroll, int *hscroll, update_text_fn
update_text, void *data)
{
dialog_clear();
dialog_textbox(title, text, r, c);
return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
update_text, data);
}
static void show_textbox(const char *title, const char *text, int r, int c)
{
show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
NULL, NULL);
}
static void show_helptext(const char *title, const char *text)
......@@ -862,9 +934,6 @@ int main(int ac, char **av)
single_menu_mode = 1;
}
initscr();
getyx(stdscr, saved_y, saved_x);
if (init_dialog(NULL)) {
fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
......@@ -873,7 +942,7 @@ int main(int ac, char **av)
set_config_filename(conf_get_configname());
do {
conf(&rootmenu);
conf(&rootmenu, NULL);
res = handle_exit();
} while (res == KEY_ESC);
......
......@@ -507,10 +507,12 @@ const char *menu_get_help(struct menu *menu)
return "";
}
static void get_prompt_str(struct gstr *r, struct property *prop)