/* Main command processing loop.

   Copyright (C) 1993 Sebastiano Vigna

    This file is part of ne, the nice editor.

    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 1, 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.

In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */


#include "ne.h"
#include "version.h"



/* This macro turns an unspecified integer argument (-1) to 1. This
is what most command require. */

#define NORMALIZE(x)  { x = (x)<0 ? 1 : (x); }


/* Here, given a mask represent a user flag and an integer i, we do as follows:
	i < 0 : toggle flag;
	i = 0 : clear flag;
	i > 0 : set flag;
*/

#define SET_USER_FLAG(b,i,x) {\
	if ((i)<0) (b)->x = !(b)->x;\
	else (b)->x = ((i) != 0);\
}




/* This is the vector table through which all actions which have some effect
on the text are dispatched. The arguments are an action to be executed, a
possible integer parameter and a possible string parameter. -1 and NULL are,
respectively, reserved values meaning "no argument". For most operations,
the integer argument is the number of repetitions. When an on/off choice is
required, nonzero means on, zero means off, no argument means toggle. */


int do_action(buffer *b, action a, int c, unsigned char *p) {
	int i, error = 0;
	unsigned char *q;

	stop = FALSE;

	if (b->recording) record_action(b->cur_macro, a, c, p, b->verbose_macros);

	switch(a) {

		case EXIT:
			if (save_all_modified_buffers()) {
				print_error(CANT_SAVE_EXIT_SUSPENDED);
				return(ERROR);
			}
			else {
				unset_interactive_mode();
				exit(0);
			}
			return(OK);

		case QUIT:
			if (modified_buffers() && !request_response(b, "Some documents have not been saved; are you sure?", FALSE)) return(ERROR);
			unset_interactive_mode();
			exit(0);

		case LINE_UP:
			NORMALIZE(c);
			for(i=0; i<c && !(error = line_up(b)) && !stop; i++);
			return(stop ? STOPPED : error);

		case LINE_DOWN:
			NORMALIZE(c);
			for(i=0; i<c && !(error = line_down(b)) && !stop; i++);
			return(stop ? STOPPED : error);

		case PREV_PAGE:
			NORMALIZE(c);
			for(i=0; i<c && !(error = page_up(b)) && !stop; i++);
			return(stop ? STOPPED : error);

		case NEXT_PAGE:
			NORMALIZE(c);
			for(i=0; i<c && !(error = page_down(b)) && !stop; i++);
			return(stop ? STOPPED : error);

		case MOVE_LEFT:
			NORMALIZE(c);
			for(i=0; i<c && !(error = char_left(b)) && !stop; i++);
			return(stop ? STOPPED : error);

		case MOVE_RIGHT:
			NORMALIZE(c);
			for(i=0; i<c && !(error = char_right(b)) && !stop; i++);
			return(stop ? STOPPED : error);

		case MOVE_TO_SOL:
			move_to_sol(b);
			return(OK);

		case MOVE_TO_EOL:
			move_to_eol(b);
			return(OK);

		case MOVE_TO_SOF:
			move_to_sof(b);
			return(OK);

		case MOVE_TO_EOF:
			move_to_eof(b);
			return(OK);

		case TOGGLE_SOF_EOF:
			toggle_sof_eof(b);
			return(OK);

		case TOGGLE_SOL_EOL:
			toggle_sol_eol(b);
			return(OK);

		case NEXT_WORD:
			NORMALIZE(c);
			for(i=0; i<c && !(error = search_word(b, 1)) && !stop; i++);
			return(stop ? STOPPED : error);

		case PREV_WORD:
			NORMALIZE(c);
			for(i=0; i<c && !(error = search_word(b, -1)) && !stop; i++);
			return(stop ? STOPPED : error);

		case MOVE_TO_EOW:
			move_to_eow(b);
			return(OK);

		case SET_BOOKMARK:
			if (c<0) c = 0;
			if (c<NUM_BOOKMARKS) {
				b->bookmark[c].pos = b->cur_pos;
				b->bookmark[c].line = b->cur_line;
				return(OK);
			}
			return(BOOKMARK_OUT_OF_RANGE);

		case GOTO_BOOKMARK:
			if (c<0) c = 0;
			if (c<NUM_BOOKMARKS) {
				goto_line(b, b->bookmark[c].line);
				goto_pos(b, b->bookmark[c].pos);
				return(OK);
			}
			return(BOOKMARK_OUT_OF_RANGE);

		case GOTO_LINE:
			if (c<0 && (c = request_number("Line", b->cur_line+1))<0) return(ERROR);
			if (c>b->line_num) c = b->line_num;
			goto_line(b, c ? --c : 0);
			return(OK);

		case GOTO_COLUMN:
			if (c<0 && (c = request_number("Column", b->cur_x+b->win_x+1))<0) return(ERROR);
			goto_column(b, c ? --c : 0);
			return(OK);

		case INSERT_CHAR:
			if (b->read_only) return(FILE_IS_READ_ONLY);

			if (c == 0) return(CANT_INSERT_0);

			i = b->cur_pos < b->cur_line_desc->line_len ? b->cur_line_desc->line[b->cur_pos] : 0;

			start_undo_chain(b);

			if (!(b->insert) && b->cur_pos < b->cur_line_desc->line_len)
				delete_char(b, b->cur_line_desc, b->cur_line, b->cur_pos);

			if (b->cur_pos > b->cur_line_desc->line_len)
				insert_spaces(b, b->cur_line_desc, b->cur_line, b->cur_line_desc->line_len, b->cur_pos-b->cur_line_desc->line_len);

			insert_char(b, b->cur_line_desc, b->cur_line, b->cur_pos, (unsigned char)c);

			end_undo_chain(b);

			if (b->insert) update_inserted_char(b, b->cur_line_desc, b->cur_pos, b->cur_y, b->cur_x);
			else update_overwritten_char(b, i, b->cur_line_desc, b->cur_pos, b->cur_y, b->cur_x);

			char_right(b);

			/* Note the use of columns-1. This avoids a double horizontal scrolling each time a 
			word wrap happens with b->right_margin = 0. */

			if (b->word_wrap && b->win_x+b->cur_x >= (b->right_margin ? b->right_margin : columns-1)) {
				if ((i = word_wrap(b)) != ERROR) {

					int j = 0;

					/* If b->win_x is nonzero, the move_to_sol() call will refresh
					the entire video, so we shouldn't scroll. */

					if (b->win_x) {
						move_to_sol(b);
						line_down(b);
					}
					else {
						update_line(b, b->cur_y);
						move_to_sol(b);
						line_down(b);
						if (b->cur_y < lines-1) scroll_window(b, b->cur_y, 1);
					}

					if (b->auto_indent && (j = auto_indent_line(b))) update_line(b, b->cur_y);
					goto_pos(b, i+j);

				}
			}
			return(OK);


		case BACKSPACE:
		case DELETE_CHAR:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !stop; i++) {
				if (a == BACKSPACE) {
					if (b->win_x+b->cur_x == 0 && b->cur_line == 0) return(ERROR);
					if (b->cur_pos > b->cur_line_desc->line_len && b->cur_line_desc->line_len>0) {
						char_left(b);
						return(OK);
					}
					char_left(b);
				}

				if (b->cur_pos < b->cur_line_desc->line_len) {
					char old_char = b->cur_line_desc->line[b->cur_pos];

					delete_char(b, b->cur_line_desc, b->cur_line, b->cur_pos);

					update_deleted_char(b, old_char, b->cur_line_desc, b->cur_pos, b->cur_y, b->cur_x);
				}
				else if (b->cur_pos == b->cur_line_desc->line_len) {

					delete_char(b, b->cur_line_desc, b->cur_line, b->cur_pos);
					update_partial_line(b, b->cur_y, b->cur_x, TRUE);

					if (b->cur_y < lines-2) scroll_window(b, b->cur_y+1, -1);
				}
			}
			return(stop ? STOPPED : 0);

		case INSERT_LINE:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !stop; i++) {
				if (insert_lin(b, b->cur_line_desc, b->cur_line, b->cur_pos) == OK) {

					/* If b->win_x is nonzero, the move_to_sol() call will refresh
					the entire video, so we shouldn't scroll. */

					if (b->win_x) {
						move_to_sol(b);
						line_down(b);
					}
					else {
						update_partial_line(b, b->cur_y, b->cur_x, FALSE);
						move_to_sol(b);
						line_down(b);
						if (b->cur_y < lines-1) scroll_window(b, b->cur_y, 1);
					}
					
					if (b->auto_indent && (i = auto_indent_line(b))) {
						update_line(b, b->cur_y);
						goto_pos(b, i);
					}

				}
			}
			return(stop ? STOPPED : 0);

		case DELETE_LINE:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !stop; i++) {
				delete_lin(b, b->cur_line_desc, b->cur_line);
				move_to_sol(b);
				scroll_window(b, b->cur_y, -1);
			}
			return(stop ? STOPPED : 0);

		case UNDEL_LINE:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !stop; i++) {
				if (undelete_line(b) == OK) {
					update_partial_line(b, b->cur_y, b->cur_x, FALSE);
					if (b->cur_y < lines-2) scroll_window(b, b->cur_y+1, 1);
				}
				else return(ERROR);
			}
			return(stop ? STOPPED : 0);

		case DELETE_TO_EOL:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			delete_to_eol(b, b->cur_line_desc, b->cur_line, b->cur_pos);
			update_partial_line(b, b->cur_y, b->cur_x, FALSE);

			return(OK);

		case SAVE:
			p = strdup(b->filename);

		case SAVEAS:
			if (p || (p = request_string("Filename", b->filename, FALSE))) {
				print_info(SAVING);

				error = save_buffer_to_file(b, p);

				if (!print_error(error)) {
					change_filename(b, p);
					print_info(DONE);
				}
				else {
					free(p);
					return(ERROR);
				}
			}
			return(OK);

		case CLEAR:
         if ((b->buffer_is_modified) && !request_response(b, "This document is not saved; are you sure?", FALSE)) return(ERROR);
			clear_buffer(b);
			reset_window();
			return(OK);

		case OPEN:
         if ((b->buffer_is_modified) && !request_response(b, "This document is not saved; are you sure?", FALSE)) return(ERROR);

			if (p || (p = request_file(b, "Filename", b->filename))) {

				buffer *dup = get_buffer_named(p);

				if (!dup || dup == b || request_response(b, "There is another document with the same name; are you sure?", FALSE)) {
					error = load_file_in_buffer(b, p);

					change_filename(b, p);

					if (b->auto_prefs) load_auto_prefs(b, NULL);

					reset_window();
					print_error(error);
					return(OK);
				}
				free(p);
			}
			return(ERROR);

		case ABOUT:
			print_message(ABOUT_MSG);
			return(OK);

		case REFRESH:
			clear_entire_screen();
			reset_window();
			return(OK);

		case FIND:
		case FIND_REGEXP:
			if (p || (p = request_string(a == FIND ? "Find" : "Find RegExp", b->find_string, FALSE))) {
				free(b->find_string);
				b->find_string = p;
				b->find_string_changed = 1;
				print_error(error = (a == FIND ? find : find_regexp)(b, NULL, 0, TRUE));
			}

			b->last_was_replace = 0;
			b->last_was_regexp = (a == FIND_REGEXP);
			return(error ? ERROR : 0);

		case REPLACE:
		case REPLACE_ONCE:
		case REPLACE_ALL:

			if (b->read_only) {
				free(p);
				return(FILE_IS_READ_ONLY);
			}

			if ((q = b->find_string) || (q = request_string(b->last_was_regexp ? "Find RegExp" : "Find", NULL, FALSE))) {

				if (q != b->find_string) {
					free(b->find_string);
					b->find_string = q;
					b->find_string_changed = 1;
				}

				if (p || (p = request_string(b->last_was_regexp ? "Replace RegExp" : "Replace", b->replace_string, TRUE))) {

					int dir = b->search_back ? -1 : 1, first_search = TRUE, num_replace = 0;
					unsigned char c = 0;

					b->last_was_replace = 1;

					free(b->replace_string);
					b->replace_string = p;

					while(!stop && !(error = (b->last_was_regexp ? find_regexp : find)(b, NULL, dir, !first_search && a != REPLACE_ALL && c != 'A' && c != 'Y'))) {

						if (c != 'A' && a != REPLACE_ALL && a != REPLACE_ONCE) {
							refresh_window(b);
							c = request_char(b, dir>0 ? "Replace (Yes/No/Last/All/Quit/Backward)" : "Replace (Yes/No/Last/All/Quit/Forward)", 'n');
							if (c == 'Q') break;
							if (c == 'A') start_undo_chain(b);
						}

						if (c == 'A' || c == 'Y' || c == 'L' || a == REPLACE_ONCE || a == REPLACE_ALL) {
							if (b->last_was_regexp) error = replace_regexp(b, p);
							else error = replace(b, strlen(b->find_string), p);
							update_line(b, b->cur_y);
							if (print_error(error)) {
								if (a == REPLACE_ALL || c == 'A') end_undo_chain(b);
								return(ERROR);
							}
							num_replace++;
						}

						if (c == 'B' && !(b->search_back) || c == 'F' && (b->search_back)) {
							dir = -dir;
							b->search_back = !b->search_back;
						}

						if (a == REPLACE_ONCE || c == 'L') break;

						first_search = FALSE;
					}

					if (a == REPLACE_ALL || c == 'A') end_undo_chain(b);

					if (stop) return(STOPPED);

					if ((c != 'A' && a != REPLACE_ALL || first_search) && error || error != NOT_FOUND) {
						print_error(error);
						return(ERROR);
					}
					return(OK);
				}
         }
         return(ERROR);

		case REPEAT_LAST:
			if (b->read_only && b->last_was_replace) return(FILE_IS_READ_ONLY);
			if (!b->find_string) print_error(NO_SEARCH_STRING);
			else if ((b->last_was_replace) && !b->replace_string) print_error(NO_REPLACE_STRING);
			else {
				int return_code = 0;

				NORMALIZE(c);

				for(i=0; i<c; i++) {
					if (!print_error((b->last_was_regexp ? find_regexp : find)(b, NULL, 0, !b->last_was_replace))) {
						if (b->last_was_replace) {
							if (b->last_was_regexp) error = replace_regexp(b, b->replace_string);
							else error = replace(b, strlen(b->find_string), b->replace_string);
							update_line(b, b->cur_y);
							if (print_error(error)) {
								return_code = ERROR;
								break;
							}
						}
					}
					else {
						return_code = ERROR;
						break;
					}
				}

				return(return_code);
			}
			return(ERROR);

		case MATCH_BRACKET:
			return(print_error(match_bracket(b)) ? ERROR : 0);

		case BEEP:
			ring_bell();
			return(OK);

		case FLASH:
			do_flash();
			return(OK);

		case TAB_SIZE:
			if (c<0 && (c = request_number("TAB Size", b->tab_size))<=0) return(ERROR);
			if (c<columns/2) {
				move_to_sol(b);
				b->tab_size = c;
				reset_window();
				return(OK);
			}
			print_error(TAB_SIZE_OUT_OF_RANGE);
			return(ERROR);

		case TURBO:
			if (c<0 && (c = request_number("Turbo threshold", b->turbo))<0) return(ERROR);
			b->turbo = c;
			return(OK);

		case CLIP_NUMBER:
			if (c<0 && (c = request_number("Clip Number", b->cur_clip))<0) return(ERROR);
			b->cur_clip = c;
			return(OK);

      case RIGHT_MARGIN:
			if (c<0 && (c = request_number("Right Margin", b->right_margin))<0) return(ERROR);
			b->right_margin = c;
         return(OK);

		case FREE_FORM:
			SET_USER_FLAG(b, c, free_form);
			return(OK);

		case STATUS_BAR:
			SET_USER_FLAG(b, c, status_bar);
			reset_status_bar();
			return(OK);

		case FAST_GUI:
			SET_USER_FLAG(b, c, fast_gui);
			reset_status_bar();
			return(OK);

		case INSERT:
			SET_USER_FLAG(b, c, insert);
			return(OK);

		case WORD_WRAP:
			SET_USER_FLAG(b, c, word_wrap);
			return(OK);

		case AUTO_INDENT:
			SET_USER_FLAG(b, c, auto_indent);
			return(OK);

		case VERBOSE_MACROS:
			SET_USER_FLAG(b, c, verbose_macros);
			return(OK);

		case AUTO_PREFS:
			SET_USER_FLAG(b, c, auto_prefs);
			return(OK);

		case BINARY:
			SET_USER_FLAG(b, c, binary);
			return(OK);

		case NO_FILE_REQ:
			SET_USER_FLAG(b, c, no_file_req);
			return(OK);

		case DO_UNDO:
			SET_USER_FLAG(b, c, do_undo);
			if (!(b->do_undo)) reset_undo_buffer(&b->undo);
			return(OK);

		case READ_ONLY:
			SET_USER_FLAG(b, c, read_only);
			return(OK);

		case CASE_SEARCH:
			SET_USER_FLAG(b, c, case_search);
			return(OK);

		case SEARCH_BACK:
			SET_USER_FLAG(b, c, search_back);
			return(OK);

		case RECORD:
			i = b->recording;
			SET_USER_FLAG(b, c, recording);
			if (b->recording && !i) b->cur_macro = reset_stream(b->cur_macro);
			return(OK);

		case PLAY:
			if (!b->recording) {
				if (c<0 && (c = request_number("Times", 1))<=0) return(ERROR);
				for(i=0; i<c && !(error = play_macro(b, b->cur_macro)); i++);
				return(print_error(error) ? ERROR : 0);
			}
			else return(ERROR);

		case SAVE_MACRO:
			if (p || (p = request_file(b, "Macro Name", NULL))) {
				print_info(SAVING);
				if ((error = print_error(save_stream(b->cur_macro, p))) == OK) print_info(DONE);
				free(p);
				return(error ? ERROR : 0);
			}
			return(ERROR);

		case OPEN_MACRO:
			if (p || (p = request_file(b, "Macro Name", NULL))) {
				char_stream *cs;
				cs = load_stream(b->cur_macro, p);
				if (cs) b->cur_macro = cs;
				free(p);
				return(cs ? 0 : ERROR);
			}
			return(ERROR);

		case MACRO:
			if (p || (p = request_file(b, "Macro Name", NULL))) {
				error = print_error(execute_macro(b, p));
				free(p);
				return(error ? ERROR : 0);
			}
			return(ERROR);

		case UNLOAD_MACROS:
			unload_macros();
			return(OK);

		case NEW_BUFFER:
			new_buffer();
			reset_window();
			return(OK);

		case CLOSE_BUFFER:
			if ((b->buffer_is_modified) && !request_response(b, "This document is not saved; are you sure?", FALSE)) return(ERROR);
			if (!delete_buffer()) {
				unset_interactive_mode();
				exit(0);
			}
			reset_window();

			/* We always return ERROR after a buffer has been deleted. Otherwise,
			the calling routines (and macros) could work on an unexisting buffer. */

			return(ERROR);

		case NEXT_BUFFER:
			if (b->b_node.next->next) cur_buffer = (buffer *)b->b_node.next;
			else cur_buffer = (buffer *)buffers.head;
			reset_window();
			return(OK);

		case PREV_BUFFER:
			if (b->b_node.prev->prev) cur_buffer = (buffer *)b->b_node.prev;
			else cur_buffer = (buffer *)buffers.tail_pred;
			reset_window();
			return(OK);

		case SELECT_BUFFER:
			if ((i = request_document()) < 0 || !(b = get_nth_buffer(i))) return(ERROR);
			cur_buffer = b;
			reset_window();
			return(OK);

		case MARK_BLOCK:
		case MARK_VERT:
			SET_USER_FLAG(b, c, marking);
			b->mark_is_vertical = (a == MARK_VERT);
			b->block_start_line = b->cur_line;
			b->block_start_pos = b->cur_pos;
			b->block_start_col = b->win_x+b->cur_x;
			return(OK);

		case CUT:
			if (b->read_only) return(FILE_IS_READ_ONLY);
		case COPY:
			if (!(error = print_error((b->mark_is_vertical ? copy_vert_to_clip : copy_to_clip)(b, c < 0 ? b->cur_clip : c, a == CUT)))) {
				b->marking = 0;
				if (a == CUT) update_window(b);
			}
			return(error ? ERROR : 0);

		case ERASE:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			if (!(error = print_error((b->mark_is_vertical ? erase_vert_block : erase_block)(b)))) {
				b->marking = 0;
				update_window(b);
			}
			return(OK);

		case PASTE:
		case PASTE_VERT:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			if (b->cur_pos > b->cur_line_desc->line_len)
				insert_spaces(b, b->cur_line_desc, b->cur_line, b->cur_line_desc->line_len, b->cur_pos-b->cur_line_desc->line_len);
			if (!(error = print_error((a == PASTE ? paste_to_buffer : paste_vert_to_buffer)(b, c < 0 ? b->cur_clip : c))))
				update_window(b);
			return(error ? ERROR : 0);

		case GOTO_MARK:
			if (b->marking) {
				goto_line(b, b->block_start_line);
				goto_column(b, b->block_start_col);
				return(OK);
			}
			print_error(MARK_BLOCK_FIRST);
			return(ERROR);

		case OPEN_CLIP:
			if (p || (p = request_file(b, "Clip Name", NULL))) {
				error = print_error(load_clip(b->cur_clip, p));
				free(p);
				return(error ? ERROR : 0);
			}
			return(ERROR);

		case SAVE_CLIP:
			if (p || (p = request_file(b, "Clip Name", NULL))) {
				print_info(SAVING);
				if ((error = print_error(save_clip(b->cur_clip, p))) == OK) print_info(DONE);
				free(p);
				return(error ? ERROR : 0);
			}
			return(ERROR);

		case EXEC:
			if (p || (p = request_string("Command", b->command_line, FALSE))) {
				free(b->command_line);
				b->command_line = p;
				return(print_error(execute_command_line(b, p)) ? ERROR : 0);
			}
			return(ERROR);

		case SYSTEM:
			unset_interactive_mode();
			system(p);
			set_interactive_mode();

			free(p);
			reset_window();
			return(OK);

		case TO_UPPER:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !(error = to_upper(b)) && !stop; i++);
			if (stop) error = STOPPED;
			return(print_error(error) ? ERROR : 0);

		case TO_LOWER:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !(error = to_lower(b)) && !stop; i++);
			if (stop) error = STOPPED;
			return(print_error(error) ? ERROR : 0);

		case CAPITALIZE:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !(error = capitalize(b)) && !stop; i++);
			if (stop) error = STOPPED;
			return(print_error(error) ? ERROR : 0);


		case CENTER:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !(error = center(b)) && !stop; i++) {
				update_line(b, b->cur_y);
				move_to_sol(b);
				if (line_down(b) != OK) break;
			}

			if (stop) error = STOPPED;
			return(print_error(error) ? ERROR : 0);

		case PARAGRAPH:
			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !(error = paragraph(b)) && !stop; i++);

			if (stop) error = STOPPED;
			return(print_error(error) ? ERROR : 0);

		case LOAD_PREFS:
			if (p || (p = request_file(b, "Prefs Name", NULL))) {
				error = print_error(load_prefs(b, p));
				free(p);
				return(error ? ERROR : OK);
			}
			return(ERROR);

		case SAVE_PREFS:
			if (p || (p = request_file(b, "Prefs Name", NULL))) {
				error = print_error(save_prefs(b, p));
				free(p);
				return(error ? ERROR : OK);
			}
			return(ERROR);

		case LOAD_AUTO_PREFS:
			return(print_error(load_auto_prefs(b, NULL)) ? ERROR : OK);

		case SAVE_AUTO_PREFS:
			return(print_error(save_auto_prefs(b, NULL)) ? ERROR : OK);

		case SAVE_DEF_PREFS:
			return(print_error(save_auto_prefs(b, DEF_PREFS_NAME)) ? ERROR : OK);

		case ESCAPE:
			handle_menus();
			return(OK);

		case UNDO:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !(error = undo(b)) && !stop; i++);
			if (stop) error = STOPPED;
			update_window(b);
			return(print_error(error) ? ERROR : 0);

		case REDO:

			if (b->read_only) return(FILE_IS_READ_ONLY);

			NORMALIZE(c);

			for(i=0; i<c && !(error = redo(b)) && !stop; i++);

			if (stop) error = STOPPED;
			update_window(b);
			return(print_error(error) ? ERROR : 0);

		case HELP:
			help(p);
			reset_window();
			return(OK);

		default:
			return(OK);
			break;
	}
}
