/* Unconditionally redraw the entire screen, and then refresh it using * the current file. */ void total_refresh(void) { total_redraw(); titlebar(NULL); edit_refresh(); bottombars(currmenu); }
/* Unconditionally redraw the entire screen, and then refresh it using * refresh_func(). */ void total_statusbar_refresh(void (*refresh_func)(void)) { total_redraw(); refresh_func(); }
int do_dictionary_prompt(bool all, const char *msg, char *str) { s_list l; s_node *p; int ok = -2, width = 16; const char *yesstr; /* String of Yes characters accepted. */ const char *nostr; /* Same for No. */ const char *allstr; /* And All, surprise! */ int u; char **thisarray = (char **)malloc(sizeof(char)*5); for(u = 0; u < 5; u++) thisarray[u] = (char *)malloc(sizeof(char)*16); thisarray = thisfunc(str); int oldmenu = currmenu; char but[16]; int i, j = 0; int count = 0; int t = 0; int bs = 0; char buf[16]; strcpy(buf, str); int tp; int we = 0, wc = 0; tp = typepos(); assert(msg != NULL); s_init(&l); u = 0; while(u < 5) { s_append(&l, thisarray[u]); u++; } /*Now partial matched list is prepared*/ p = l.head; if(str == NULL){ count = 0; while(p){ count++; p = p->next; } } //count=5; p = l.head; do { int kbinput; functionptrtype func; i = 0; #ifndef DISABLE_MOUSE int mouse_x, mouse_y; #endif if (!ISSET(NO_HELP)) { char shortstr[3]; /* Temporary string for (translated) " Y", " N" and " A". */ if (COLS < 32) width = COLS / 2; /* Clear the shortcut list from the bottom of the screen. */ blank_bottombars(); /* Now show the ones for "Yes", "No", "Cancel" and maybe "All". */ if (all) { shortstr[1] = allstr[0]; wmove(bottomwin, 1, width); onekey(shortstr, _("All"), width); } for(i = 0; i < count && i < 5; i++){ sprintf(but, "%d", i + 1); if(i == 0) wmove(bottomwin, 1, 0); else wmove(bottomwin, 1, i * width); if(p){ onekey(but, p->word, width); p = p->next; } j++; } wmove(bottomwin, 1, i * width); onekey("^C", _("Cancel"), width); } if (interface_color_pair[TITLE_BAR].bright) wattron(bottomwin, A_BOLD); wattron(bottomwin, interface_color_pair[TITLE_BAR].pairnum); blank_statusbar(); mvwaddnstr(bottomwin, 0, 0, msg, actual_x(msg, COLS - 1)); wattroff(bottomwin, A_BOLD); wattroff(bottomwin, interface_color_pair[TITLE_BAR].pairnum); /* Refresh edit window and statusbar before getting input. */ wnoutrefresh(edit); wnoutrefresh(bottomwin); currmenu = MYESNO; kbinput = get_kbinput(bottomwin); #ifndef NANO_TINY if (kbinput == KEY_WINCH) continue; #endif /*49 is 1, 50 is 2 and so on*/ func = func_from_key(&kbinput); if (func == do_cancel){ ok = 0; return ok; } if(kbinput == 's'){ if(i == 5) { continue; } } if(kbinput <= '0' || kbinput > '0' + count) return -1; if(kbinput > '0' && kbinput <= '0' + count){ p = l.head; i = kbinput - '0' - 1; if(j > 5) t = j - 5 + i; else t = i; while(t--) if(p){ p = p->next; } if(tp == 1 || tp == 2){ bs = strlen(str); while(bs--) do_backspace(); } if(tp == 6 || tp == 7){ if(str != NULL){ we = openfile->current_x; while(openfile->current->data[we] != ' ' && openfile->current->data[we] != '\00'){ we++; wc++; do_right(); } we--; while(openfile->current->data[we] != ' ' && openfile->current->data[we] != '\00'){ do_backspace(); we--; } } } do_output(p->word, strlen(p->word), FALSE); return tp; } else if (func == total_refresh) { total_redraw(); continue; } else { /* Look for the kbinput in the Yes, No and (optionally) * All strings. */ if (strchr(yesstr, kbinput) != NULL) ok = 1; else if (strchr(nostr, kbinput) != NULL) ok = 0; else if (all && strchr(allstr, kbinput) != NULL) ok = 2; } i = 0; p = l.head; } while (ok == -2); currmenu = oldmenu; return ok; }
/* Ask a simple Yes/No (and optionally All) question, specified in msg, * on the statusbar. Return 1 for Yes, 0 for No, 2 for All (if all is * TRUE when passed in), and -1 for Cancel. */ int do_yesno_prompt(bool all, const char *msg) { int ok = -2, width = 16; const char *yesstr; /* String of Yes characters accepted. */ const char *nostr; /* Same for No. */ const char *allstr; /* And All, surprise! */ int oldmenu = currmenu; assert(msg != NULL); /* yesstr, nostr, and allstr are strings of any length. Each string * consists of all single-byte characters accepted as valid * characters for that value. The first value will be the one * displayed in the shortcuts. */ /* TRANSLATORS: For the next three strings, if possible, specify * the single-byte shortcuts for both your language and English. * For example, in French: "OoYy" for "Oui". */ yesstr = _("Yy"); nostr = _("Nn"); allstr = _("Aa"); do { int kbinput; functionptrtype func; #ifndef DISABLE_MOUSE int mouse_x, mouse_y; #endif if (!ISSET(NO_HELP)) { char shortstr[3]; /* Temporary string for (translated) " Y", " N" and " A". */ if (COLS < 32) width = COLS / 2; /* Clear the shortcut list from the bottom of the screen. */ blank_bottombars(); /* Now show the ones for "Yes", "No", "Cancel" and maybe "All". */ sprintf(shortstr, " %c", yesstr[0]); wmove(bottomwin, 1, 0); onekey(shortstr, _("Yes"), width); if (all) { shortstr[1] = allstr[0]; wmove(bottomwin, 1, width); onekey(shortstr, _("All"), width); } shortstr[1] = nostr[0]; wmove(bottomwin, 2, 0); onekey(shortstr, _("No"), width); wmove(bottomwin, 2, width); onekey("^C", _("Cancel"), width); } if (interface_color_pair[TITLE_BAR].bright) wattron(bottomwin, A_BOLD); wattron(bottomwin, interface_color_pair[TITLE_BAR].pairnum); blank_statusbar(); mvwaddnstr(bottomwin, 0, 0, msg, actual_x(msg, COLS - 1)); wattroff(bottomwin, A_BOLD); wattroff(bottomwin, interface_color_pair[TITLE_BAR].pairnum); /* Refresh edit window and statusbar before getting input. */ wnoutrefresh(edit); wnoutrefresh(bottomwin); currmenu = MYESNO; kbinput = get_kbinput(bottomwin); #ifndef NANO_TINY if (kbinput == KEY_WINCH) continue; #endif func = func_from_key(&kbinput); if (func == do_cancel) ok = -1; #ifndef DISABLE_MOUSE else if (kbinput == KEY_MOUSE) { /* We can click on the Yes/No/All shortcut list to * select an answer. */ if (get_mouseinput(&mouse_x, &mouse_y, FALSE) == 0 && wmouse_trafo(bottomwin, &mouse_y, &mouse_x, FALSE) && mouse_x < (width * 2) && mouse_y > 0) { int x = mouse_x / width; /* Calculate the x-coordinate relative to the * two columns of the Yes/No/All shortcuts in * bottomwin. */ int y = mouse_y - 1; /* Calculate the y-coordinate relative to the * beginning of the Yes/No/All shortcuts in * bottomwin, i.e. with the sizes of topwin, * edit, and the first line of bottomwin * subtracted out. */ assert(0 <= x && x <= 1 && 0 <= y && y <= 1); /* x == 0 means they clicked Yes or No. y == 0 * means Yes or All. */ ok = -2 * x * y + x - y + 1; if (ok == 2 && !all) ok = -2; } } #endif /* !DISABLE_MOUSE */ else if (func == total_refresh) { total_redraw(); continue; } else { /* Look for the kbinput in the Yes, No and (optionally) * All strings. */ if (strchr(yesstr, kbinput) != NULL) ok = 1; else if (strchr(nostr, kbinput) != NULL) ok = 0; else if (all && strchr(allstr, kbinput) != NULL) ok = 2; } } while (ok == -2); currmenu = oldmenu; return ok; }
/* Our main file browser function. path is the tilde-expanded path we * start browsing from. */ char *do_browser(char *path, DIR *dir) { char *retval = NULL; int kbinput; bool old_const_update = ISSET(CONST_UPDATE); char *prev_dir = NULL; /* The directory we were in before backing up to "..". */ char *ans = NULL; /* The last answer the user typed at the statusbar prompt. */ size_t old_selected; /* The selected file we had before the current selected file. */ functionptrtype func; /* The function of the key the user typed in. */ curs_set(0); blank_statusbar(); bottombars(MBROWSER); wnoutrefresh(bottomwin); UNSET(CONST_UPDATE); ans = mallocstrcpy(NULL, ""); change_browser_directory: /* We go here after we select a new directory. */ /* Start with no key pressed. */ kbinput = ERR; path = mallocstrassn(path, get_full_path(path)); /* Save the current path in order to be used later. */ path_save = path; assert(path != NULL && path[strlen(path) - 1] == '/'); /* Get the file list, and set longest and width in the process. */ browser_init(path, dir); assert(filelist != NULL); /* Sort the file list. */ qsort(filelist, filelist_len, sizeof(char *), diralphasort); /* If prev_dir isn't NULL, select the directory saved in it, and * then blow it away. */ if (prev_dir != NULL) { browser_select_dirname(prev_dir); free(prev_dir); prev_dir = NULL; /* Otherwise, select the first file or directory in the list. */ } else selected = 0; old_selected = (size_t)-1; titlebar(path); while (TRUE) { struct stat st; int i; size_t fileline = selected / width; /* The line number the selected file is on. */ char *new_path; /* The path we switch to at the "Go to Directory" * prompt. */ /* Display the file list if we don't have a key, or if the * selected file has changed, and set width in the process. */ if (kbinput == ERR || old_selected != selected) browser_refresh(); old_selected = selected; kbinput = get_kbinput(edit); #ifndef NANO_TINY if (kbinput == KEY_WINCH) { kbinput = ERR; curs_set(0); continue; } #endif #ifndef DISABLE_MOUSE if (kbinput == KEY_MOUSE) { int mouse_x, mouse_y; /* We can click on the edit window to select a * filename. */ if (get_mouseinput(&mouse_x, &mouse_y, TRUE) == 0 && wmouse_trafo(edit, &mouse_y, &mouse_x, FALSE)) { /* longest is the width of each column. There * are two spaces between each column. */ selected = (fileline / editwinrows) * (editwinrows * width) + (mouse_y * width) + (mouse_x / (longest + 2)); /* If they clicked beyond the end of a row, * select the last filename in that row. */ if (mouse_x > width * (longest + 2)) selected--; /* If we're off the screen, select the last filename. */ if (selected > filelist_len - 1) selected = filelist_len - 1; /* If we selected the same filename as last time, * put back the Enter key so that it's read in. */ if (old_selected == selected) unget_kbinput(sc_seq_or(do_enter_void, 0), FALSE, FALSE); } } #endif /* !DISABLE_MOUSE */ func = parse_browser_input(&kbinput); if (func == total_refresh) { total_redraw(); } else if (func == do_help_void) { #ifndef DISABLE_HELP do_help_void(); /* Perhaps the window dimensions have changed. */ browser_refresh(); curs_set(0); #else nano_disabled_msg(); #endif } else if (func == do_search) { /* Search for a filename. */ curs_set(1); do_filesearch(); curs_set(0); } else if (func == do_research) { /* Search for another filename. */ do_fileresearch(); } else if (func == do_page_up) { if (selected >= (editwinrows + fileline % editwinrows) * width) selected -= (editwinrows + fileline % editwinrows) * width; else selected = 0; } else if (func == do_page_down) { selected += (editwinrows - fileline % editwinrows) * width; if (selected > filelist_len - 1) selected = filelist_len - 1; } else if (func == do_first_file) { selected = 0; } else if (func == do_last_file) { selected = filelist_len - 1; } else if (func == goto_dir_void) { /* Go to a specific directory. */ curs_set(1); i = do_prompt(TRUE, #ifndef DISABLE_TABCOMP FALSE, #endif MGOTODIR, ans, #ifndef DISABLE_HISTORIES NULL, #endif /* TRANSLATORS: This is a prompt. */ browser_refresh, _("Go To Directory")); curs_set(0); bottombars(MBROWSER); /* If the directory begins with a newline (i.e. an * encoded null), treat it as though it's blank. */ if (i < 0 || *answer == '\n') { /* We canceled. Indicate that on the statusbar, and * blank out ans, since we're done with it. */ statusbar(_("Cancelled")); ans = mallocstrcpy(ans, ""); continue; } else if (i != 0) { /* Put back the "Go to Directory" key and save * answer in ans, so that the file list is displayed * again, the prompt is displayed again, and what we * typed before at the prompt is displayed again. */ unget_kbinput(sc_seq_or(do_gotolinecolumn_void, 0), FALSE, FALSE); ans = mallocstrcpy(ans, answer); continue; } /* We have a directory. Blank out ans, since we're done * with it. */ ans = mallocstrcpy(ans, ""); /* Convert newlines to nulls, just before we go to the * directory. */ sunder(answer); align(&answer); new_path = real_dir_from_tilde(answer); if (new_path[0] != '/') { new_path = charealloc(new_path, strlen(path) + strlen(answer) + 1); sprintf(new_path, "%s%s", path, answer); } #ifndef DISABLE_OPERATINGDIR if (check_operating_dir(new_path, FALSE)) { statusbar(_("Can't go outside of %s in restricted mode"), operating_dir); free(new_path); continue; } #endif dir = opendir(new_path); if (dir == NULL) { /* We can't open this directory for some reason. * Complain. */ statusbar(_("Error reading %s: %s"), answer, strerror(errno)); beep(); free(new_path); continue; } /* Start over again with the new path value. */ free(path); path = new_path; goto change_browser_directory; } else if (func == do_up_void) { if (selected >= width) selected -= width; } else if (func == do_down_void) { if (selected + width <= filelist_len - 1) selected += width; } else if (func == do_left) { if (selected > 0) selected--; } else if (func == do_right) { if (selected < filelist_len - 1) selected++; } else if (func == do_enter_void) { /* We can't move up from "/". */ if (strcmp(filelist[selected], "/..") == 0) { statusbar(_("Can't move up a directory")); beep(); continue; } #ifndef DISABLE_OPERATINGDIR /* Note: The selected file can be outside the operating * directory if it's ".." or if it's a symlink to a * directory outside the operating directory. */ if (check_operating_dir(filelist[selected], FALSE)) { statusbar(_("Can't go outside of %s in restricted mode"), operating_dir); beep(); continue; } #endif if (stat(filelist[selected], &st) == -1) { /* We can't open this file for some reason. * Complain. */ statusbar(_("Error reading %s: %s"), filelist[selected], strerror(errno)); beep(); continue; } if (!S_ISDIR(st.st_mode)) { /* We've successfully opened a file, we're done, so * get out. */ retval = mallocstrcpy(NULL, filelist[selected]); break; } else if (strcmp(tail(filelist[selected]), "..") == 0) /* We've successfully opened the parent directory, * save the current directory in prev_dir, so that * we can easily return to it by hitting Enter. */ prev_dir = mallocstrcpy(NULL, striponedir(filelist[selected])); dir = opendir(filelist[selected]); if (dir == NULL) { /* We can't open this directory for some reason. * Complain. */ statusbar(_("Error reading %s: %s"), filelist[selected], strerror(errno)); beep(); continue; } path = mallocstrcpy(path, filelist[selected]); /* Start over again with the new path value. */ goto change_browser_directory; } else if (func == do_exit) { /* Exit from the file browser. */ break; } } titlebar(NULL); edit_refresh(); curs_set(1); if (old_const_update) SET(CONST_UPDATE); free(path); free(ans); free_chararray(filelist, filelist_len); filelist = NULL; filelist_len = 0; return retval; }
/* Our main help-viewer function. */ void do_help(void) { int kbinput = ERR; bool old_no_help = ISSET(NO_HELP); size_t line = 0; /* The line number in help_text of the first displayed help * line. This variable is zero-based. */ size_t last_line = 0; /* The line number in help_text of the last help line. This * variable is zero-based. */ int oldmenu = currmenu; /* The menu we were called from. */ const char *ptr; /* The current line of the help text. */ size_t old_line = (size_t)-1; /* The line we were on before the current line. */ functionptrtype func; /* The function of the key the user typed in. */ /* Don't show a cursor in the help screen. */ curs_set(0); blank_edit(); blank_statusbar(); /* Set help_text as the string to display. */ help_init(); assert(help_text != NULL); if (ISSET(NO_HELP)) { /* Make sure that the help screen's shortcut list will actually * be displayed. */ UNSET(NO_HELP); window_init(); } bottombars(MHELP); wnoutrefresh(bottomwin); while (TRUE) { size_t i; ptr = help_text; /* Find the line number of the last line of the help text. */ for (last_line = 0; *ptr != '\0'; last_line++) { ptr += help_line_len(ptr); if (*ptr == '\n') ptr++; } if (last_line > 0) last_line--; /* Redisplay if the text was scrolled or an invalid key was pressed. */ if (line != old_line || kbinput == ERR) { blank_edit(); ptr = help_text; /* Advance in the text to the first line to be displayed. */ for (i = 0; i < line; i++) { ptr += help_line_len(ptr); if (*ptr == '\n') ptr++; } /* Now display as many lines as the window will hold. */ for (i = 0; i < editwinrows && *ptr != '\0'; i++) { size_t j = help_line_len(ptr); mvwaddnstr(edit, i, 0, ptr, j); ptr += j; if (*ptr == '\n') ptr++; } } wnoutrefresh(edit); old_line = line; lastmessage = HUSH; kbinput = get_kbinput(edit); #ifndef NANO_TINY if (kbinput == KEY_WINCH) { kbinput = ERR; continue; /* Redraw the screen. */ } #endif #ifndef DISABLE_MOUSE if (kbinput == KEY_MOUSE) { int mouse_x, mouse_y; get_mouseinput(&mouse_x, &mouse_y, TRUE); continue; /* Redraw the screen. */ } #endif func = parse_help_input(&kbinput); if (func == total_refresh) { total_redraw(); } else if (func == do_up_void) { if (line > 0) line--; } else if (func == do_down_void) { if (line + (editwinrows - 1) < last_line) line++; } else if (func == do_page_up) { if (line > editwinrows - 2) line -= editwinrows - 2; else line = 0; } else if (func == do_page_down) { if (line + (editwinrows - 1) < last_line) line += editwinrows - 2; } else if (func == do_first_line) { line = 0; } else if (func == do_last_line) { if (line + (editwinrows - 1) < last_line) line = last_line - (editwinrows - 1); } else if (func == do_exit) { /* Exit from the help viewer. */ break; } else unbound_key(kbinput); } if (old_no_help) { blank_bottombars(); wnoutrefresh(bottomwin); currmenu = oldmenu; SET(NO_HELP); window_init(); } else bottombars(oldmenu); #ifndef DISABLE_BROWSER if (oldmenu == MBROWSER || oldmenu == MWHEREISFILE || oldmenu == MGOTODIR) browser_refresh(); else #endif edit_refresh(); /* We're exiting from the help screen. */ free(help_text); }
/* Our main file browser function. path is the tilde-expanded path we * start browsing from. */ std::string do_browser(std::string path, DIR *dir) { std::string retval; bool old_const_update = ISSET(CONST_UPDATE); std::string prev_dir; /* The directory we were in before backing up to "..". */ std::string ans; /* The last answer the user typed at the statusbar prompt. */ size_t old_selected; /* The selected file we had before the current selected file. */ curs_set(0); blank_statusbar(); bottombars(MBROWSER); wnoutrefresh(bottomwin); UNSET(CONST_UPDATE); change_browser_directory: /* We go here after we select a new directory. */ path = get_full_path(path); assert(path.length() > 0 && path.back() == '/'); /* Get the file list, and set longest and width in the process. */ browser_init(path, dir); /* Sort the file list. */ std::sort(filelist.begin(), filelist.end(), sort_directories); /* If prev_dir isn't empty, select the directory saved in it, and then blow it away. */ if (prev_dir != "") { browser_select_filename(prev_dir); prev_dir = ""; /* Otherwise, select the first file or directory in the list. */ } else { selected = 0; } old_selected = (size_t)-1; titlebar(path); Key *kbinput = nullptr; while (true) { struct stat st; size_t fileline = selected / width; /* The line number the selected file is on. */ std::string new_path; /* The path we switch to at the "Go to Directory" prompt. */ if (kbinput) { delete kbinput; kbinput = nullptr; } /* Display the file list if we don't have a key, or if the * selected file has changed, and set width in the process. */ if (old_selected != selected) { browser_refresh(); } old_selected = selected; // Deal with the keyboard input kbinput = new Key(get_kbinput(edit)); auto func = func_from_key(*kbinput); if (func == total_refresh) { total_redraw(); } else if (func == do_help_void) { do_help_void(); curs_set(0); /* Search for a filename. */ } else if (func == do_search) { curs_set(1); do_filesearch(); curs_set(0); /* Search for another filename. */ } else if (func == do_research) { do_fileresearch(); } else if (func == do_page_up) { if (selected >= (editwinrows + fileline % editwinrows) * width) { selected -= (editwinrows + fileline % editwinrows) * width; } else { selected = 0; } } else if (func == do_page_down) { selected += (editwinrows - fileline % editwinrows) * width; if (selected > filelist.size() - 1) { selected = filelist.size() - 1; } } else if (func == do_first_file) { selected = 0; } else if (func == do_last_file) { selected = filelist.size() - 1; } else if (func == goto_dir_void) { /* Go to a specific directory. */ curs_set(1); std::shared_ptr<Key> key; PromptResult i = do_prompt(true, false, MGOTODIR, key, ans.c_str(), NULL, browser_refresh, _("Go To Directory")); curs_set(0); bottombars(MBROWSER); /* If the directory begins with a newline (i.e. an * encoded null), treat it as though it's blank. */ if (i == PROMPT_ABORTED || i == PROMPT_BLANK_STRING || answer.front() == '\n') { /* We canceled. Indicate that on the statusbar, and * blank out ans, since we're done with it. */ statusbar(_("Cancelled")); ans = ""; func = nullptr; continue; } else if (i != PROMPT_ENTER_PRESSED) { /* Put back the "Go to Directory" key and save * answer in ans, so that the file list is displayed * again, the prompt is displayed again, and what we * typed before at the prompt is displayed again. */ ans = answer; func = goto_dir_void; continue; } /* We have a directory. Blank out ans, since we're done with it. */ ans = ""; /* Convert newlines to nulls, just before we go to the directory. */ sunder(answer); new_path = real_dir_from_tilde(answer); if (new_path == "") { new_path = path + answer; } dir = opendir(new_path); if (dir == NULL) { /* We can't open this directory for some reason. Complain. */ statusbar(_("Error reading %s: %s"), answer.c_str(), strerror(errno)); beep(); func = nullptr; continue; } /* Start over again with the new path value. */ path = new_path; goto change_browser_directory; } else if (func == do_up_void) { if (selected >= width) { selected -= width; } } else if (func == do_left) { if (selected > 0) { selected--; } } else if (func == do_down_void) { if (selected + width <= filelist.size() - 1) { selected += width; } } else if (func == do_right) { if (selected < filelist.size() - 1) { selected++; } } else if (func == do_enter_void) { /* We can't move up from "/". */ if (filelist[selected] == "/..") { statusbar(_("Can't move up a directory")); beep(); func = nullptr; continue; } if (stat(filelist[selected], &st) == -1) { /* We can't open this file for some reason. Complain. */ statusbar(_("Error reading %s: %s"), filelist[selected].c_str(), strerror(errno)); beep(); func = nullptr; continue; } if (!S_ISDIR(st.st_mode)) { /* We've successfully opened a file, we're done, so get out. */ retval = filelist[selected]; func = nullptr; break; } else if (tail(filelist[selected]) == "..") { /* We've successfully opened the parent directory, * save the current directory in prev_dir, so that * we can easily return to it by hitting Enter. */ prev_dir = striponedir(filelist[selected]); } dir = opendir(filelist[selected].c_str()); if (dir == NULL) { /* We can't open this directory for some reason. Complain. */ statusbar(_("Error reading %s: %s"), filelist[selected].c_str(), strerror(errno)); beep(); func = nullptr; continue; } path = filelist[selected]; /* Start over again with the new path value. */ goto change_browser_directory; } else if (func == do_exit) { /* Exit from the file browser. */ break; } func = nullptr; } if (kbinput) { delete kbinput; } titlebar(NULL); edit_refresh(); curs_set(1); if (old_const_update) { SET(CONST_UPDATE); } filelist.clear(); return retval; }