/* * input.c: does the actual input line stuff... keeps the appropriate stuff * on the input line, handles insert/delete of characters/words... the whole * ball o wax * * Written By Michael Sandrof * * Copyright(c) 1990 * * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT */ #include "irc.h" static char cvsrevision[] = "$Id: input.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $"; CVS_REVISION(input_c) #include #include "struct.h" #include "alias.h" #include "commands.h" #include "exec.h" #include "history.h" #include "bsdglob.h" #include "hook.h" #include "input.h" #include "ircaux.h" #include "keys.h" #include "screen.h" #include "server.h" #include "ircterm.h" #include "list.h" #include "vars.h" #include "misc.h" #include "screen.h" #include "output.h" #include "chelp.h" #include "dcc.h" #include "cdcc.h" #include "whowas.h" #include "tcl_bx.h" #include "window.h" #include "status.h" #include "hash2.h" #ifdef TRANSLATE #include "translat.h" #endif #ifdef WANT_HEBREW #include "hebrew.h" #endif #define MAIN_SOURCE #include "modval.h" #include void get_history (int); static char new_nick[NICKNAME_LEN+1] = ""; static char *input_lastmsg = NULL; extern NickTab *BX_getnextnick (int, char *, char *, char *); extern int extended_handled; extern char *BX_getchannick (char *, char *); extern int foreground; NickTab *tabkey_array = NULL, *autoreply_array = NULL; const int WIDTH = 10; /* input_prompt: contains the current, unexpanded input prompt */ static char *input_prompt = NULL; enum I_STATE { STATE_NORMAL = 0, STATE_COMPLETE, STATE_TABKEY, STATE_TABKEYNEXT, STATE_CNICK, STATE_CNICKNEXT } in_completion = STATE_NORMAL; /* These are sanity macros. The file was completely unreadable before * i put these in here. I make no apologies for them. */ #define current_screen last_input_screen #define INPUT_CURSOR current_screen->input_cursor #define INPUT_BUFFER current_screen->input_buffer #define MIN_POS current_screen->buffer_min_pos #define THIS_POS current_screen->buffer_pos #define THIS_CHAR INPUT_BUFFER[THIS_POS] #define MIN_CHAR INPUT_BUFFER[MIN_POS] #define PREV_CHAR INPUT_BUFFER[THIS_POS-1] #define NEXT_CHAR INPUT_BUFFER[THIS_POS+1] #define ADD_TO_INPUT(x) strmcat(INPUT_BUFFER, (x), INPUT_BUFFER_SIZE); #define INPUT_ONSCREEN current_screen->input_visible #define INPUT_VISIBLE INPUT_BUFFER[INPUT_ONSCREEN] #define ZONE current_screen->input_zone_len #define START_ZONE current_screen->input_start_zone #define END_ZONE current_screen->input_end_zone #define INPUT_PROMPT current_screen->input_prompt #define INPUT_PROMPT_LEN current_screen->input_prompt_len #define INPUT_LINE current_screen->input_line #define BUILT_IN_KEYBINDING(x) void x (char key, char *string) #define HOLDLAST current_screen->current_window->screen_hold Display *get_screen_hold(Window *win) { return win->screen_hold; } static int safe_puts (register char *str, int len, int echo) { int i = 0; while (*str && i < len) { term_putchar(*str); str++; i++; } return i; } /* cursor_to_input: move the cursor to the input line, if not there already */ extern void BX_cursor_to_input (void) { Screen *oldscreen = last_input_screen; Screen *screen; if (!foreground) return; for (screen = screen_list; screen; screen = screen->next) { if (screen->alive && is_cursor_in_display(screen)) { output_screen = screen; last_input_screen = screen; term_move_cursor(INPUT_CURSOR, INPUT_LINE); cursor_not_in_display(screen); term_flush(); } } output_screen = last_input_screen = oldscreen; } /* * update_input: does varying amount of updating on the input line depending * upon the position of the cursor and the update flag. If the cursor has * move toward one of the edge boundaries on the screen, update_cursor() * flips the input line to the next (previous) line of text. The update flag * may be: * * NO_UPDATE - only do the above bounds checking. * * UPDATE_JUST_CURSOR - do bounds checking and position cursor where is should * be. * * UPDATE_FROM_CURSOR - does all of the above, and makes sure everything from * the cursor to the right edge of the screen is current (by redrawing it). * * UPDATE_ALL - redraws the entire line */ extern void BX_update_input (int update) { int old_zone; char *ptr, *ptr_free; int len, free_it = 0, echo = 1, max; char *prompt; Screen *os = last_input_screen; Screen *ns; Window *saved_current_window = current_window; #ifdef WANT_HEBREW void BX_set_input_heb (char *str); char prehebbuff[2000]; #endif if (dumb_mode || !foreground) return; for (ns = screen_list; ns; ns = ns->next) { last_input_screen = ns; current_window = ns->current_window; #ifdef WANT_HEBREW /* * crisk: hebrew thingy */ if (get_int_var(HEBREW_TOGGLE_VAR)) { update = UPDATE_ALL; strcpy(prehebbuff, get_input()); hebrew_process(get_input()); } #endif cursor_to_input(); if (last_input_screen && last_input_screen->promptlist) prompt = last_input_screen->promptlist->prompt; else prompt = input_prompt; if (prompt) { char *loc; int i; extern int in_chelp; loc = alloca(strlen(prompt)+200); for (i = 0; prompt[i]; i++) { if (prompt[i] == '$') loc[i++] = '$'; loc[i] = prompt[i]; } loc[i] = 0; in_chelp++; prompt = convert_output_format(loc, NULL, NULL); in_chelp--; } if (prompt && update != NO_UPDATE) { int args_used; if (is_valid_process(get_target_by_refnum(0)) != -1) ptr = (char *)get_prompt_by_refnum(0); else { ptr = expand_alias(prompt, empty_string, &args_used, NULL); free_it = 1; } if (last_input_screen->promptlist) term_echo(last_input_screen->promptlist->echo); ptr_free = ptr; ptr = (char *)strip_ansi(ptr); strcat(ptr, ALL_OFF_STR); /* Yes, we can do this */ if (free_it) new_free(&ptr_free); free_it = 1; if ((ptr && !INPUT_LINE) || (!ptr && INPUT_LINE) || strcmp(ptr, last_input_screen->input_buffer)) { if (last_input_screen->input_prompt_malloc) new_free(&INPUT_PROMPT); last_input_screen->input_prompt_malloc = free_it; INPUT_PROMPT = ptr; len = strlen(INPUT_PROMPT); INPUT_PROMPT_LEN = output_with_count(INPUT_PROMPT, 0, 0); update = UPDATE_ALL; } else { if (free_it) new_free(&ptr); } } /* * * HAS THE SCREEN CHANGED SIZE SINCE THE LAST TIME? * */ /* * If the screen has resized, then we need to re-compute the * side-to-side scrolling effect. */ if ((last_input_screen->li != last_input_screen->old_li) || (last_input_screen->co != last_input_screen->old_co)) { /* * The input line is always the bottom line */ INPUT_LINE = last_input_screen->li - 1; /* * The "zone" is the range in which when you type, the * input line does not scroll. Its WIDTH chars in from * either side. */ ZONE = last_input_screen->co - (WIDTH * 2); if (ZONE < 10) ZONE = 10; /* Take that! */ START_ZONE = WIDTH; END_ZONE = last_input_screen->co - WIDTH; last_input_screen->old_co = last_input_screen->co; last_input_screen->old_li = last_input_screen->li; } /* * About zones: * The input line is divided into "zones". A "zone" is set above, * and is the width of the screen minus 20 (by default). The input * line, as displayed, is therefore composed of the current "zone", * plus 10 characters from the previous zone, plus 10 characters * from the next zone. When the cursor moves to an adjacent zone, * (by going into column 9 from the right or left of the edge), the * input line is redrawn. There is one catch. The first "zone" * includes the first ten characters of the input line. */ old_zone = START_ZONE; /* * The BEGINNING of the current "zone" is a calculated value: * The number of characters since the origin of the input buffer * is the number of printable chars in the input prompt plus the * current position in the input buffer. We subtract from that * the WIDTH delta to take off the first delta, which doesnt * count towards the width of the zone. Then we divide that by * the size of the zone, to get an integer, then we multiply it * back. This gives us the first character on the screen. We * add WIDTH to the result in order to get the start of the zone * itself. * The END of the current "zone" is just the beginning plus the width. * If we have moved to an adjacent "zone" since last time, we want to * completely redraw the input line. */ START_ZONE = ((INPUT_PROMPT_LEN + THIS_POS - WIDTH) / ZONE) * ZONE + WIDTH; END_ZONE = START_ZONE + ZONE; if (old_zone != START_ZONE) update = UPDATE_ALL; /* * Now that we know where the "zone" is in the input buffer, we can * easily calculate where where we want to start displaying stuff * from the INPUT_BUFFER. If we're in the first "zone", then we will * output from the beginning of the buffer. If we're not in the first * "zone", then we will begin to output from 10 characters to the * left of the zone, after adjusting for the length of the prompt. */ if (START_ZONE == WIDTH) INPUT_ONSCREEN = 0; else INPUT_ONSCREEN = START_ZONE - WIDTH - INPUT_PROMPT_LEN; /* * And the cursor is simply how many characters away THIS_POS is * from the first column on the screen. */ if (INPUT_ONSCREEN == 0) INPUT_CURSOR = INPUT_PROMPT_LEN + THIS_POS; else INPUT_CURSOR = THIS_POS - INPUT_ONSCREEN; /* * If the cursor moved, or if we're supposed to do a full update, * then redrwa the entire input line. */ if (update == UPDATE_ALL) { term_move_cursor(0, INPUT_LINE); /* * If the input line is NOT empty, and we're starting the * display at the beginning of the input buffer, then we * output the prompt first. */ if (INPUT_ONSCREEN == 0 && INPUT_PROMPT && *INPUT_PROMPT) { /* * Forcibly turn on echo. */ int echo = term_echo(1); /* * Crop back the input prompt so it does not extend * past the end of the zone. */ if (INPUT_PROMPT_LEN > (last_input_screen->co - WIDTH)) INPUT_PROMPT_LEN = last_input_screen->co - WIDTH - 1; /* * Output the prompt. */ output_with_count(INPUT_PROMPT, 0, 1); /* * Turn the echo back to what it was before, * and output the rest of the input buffer. */ term_echo(echo); safe_puts(INPUT_BUFFER, last_input_screen->co - INPUT_PROMPT_LEN, echo); } /* * Otherwise we just output whatever we have. */ else if (echo) safe_puts(&(INPUT_VISIBLE), last_input_screen->co, echo); /* * Clear the rest of the input line and reset the cursor * to the current input position. */ term_clear_to_eol(); } else if (update == UPDATE_FROM_CURSOR) { /* * Move the cursor to where its supposed to be, * Figure out how much we can output from here, * and then output it. */ #ifdef __EMXPM__ int echo = term_echo(1); #endif term_move_cursor(INPUT_CURSOR, INPUT_LINE); max = last_input_screen->co - (THIS_POS - INPUT_ONSCREEN); if (INPUT_ONSCREEN == 0 && INPUT_PROMPT && *INPUT_PROMPT) max -= INPUT_PROMPT_LEN; if ((len = strlen(&(THIS_CHAR))) > max) len = max; safe_puts(&(THIS_CHAR), len, echo); term_clear_to_eol(); } term_move_cursor(INPUT_CURSOR, INPUT_LINE); term_echo(1); term_flush(); #ifdef WANT_HEBREW /* * crisk: hebrew thingy */ if (get_int_var(HEBREW_TOGGLE_VAR)) BX_set_input_heb(prehebbuff); #endif } last_input_screen = os; current_window = saved_current_window; } void change_input_prompt (int direction) { if (!last_input_screen->promptlist) { strcpy(INPUT_BUFFER, last_input_screen->saved_input_buffer); THIS_POS = last_input_screen->saved_buffer_pos; MIN_POS = last_input_screen->saved_min_buffer_pos; *last_input_screen->saved_input_buffer = '\0'; last_input_screen->saved_buffer_pos = 0; last_input_screen->saved_min_buffer_pos = 0; update_input(UPDATE_ALL); } else if (direction == -1) update_input(UPDATE_ALL); else if (!last_input_screen->promptlist->next) { strcpy(last_input_screen->saved_input_buffer, INPUT_BUFFER); last_input_screen->saved_buffer_pos = THIS_POS; last_input_screen->saved_min_buffer_pos = MIN_POS; *INPUT_BUFFER = '\0'; THIS_POS = MIN_POS = 0; update_input(UPDATE_ALL); } } /* input_move_cursor: moves the cursor left or right... got it? */ extern void input_move_cursor (int dir) { cursor_to_input(); if (dir) { if (THIS_CHAR) { THIS_POS++; term_cursor_right(); } } else { if (THIS_POS > MIN_POS) { THIS_POS--; term_cursor_left(); } } update_input(NO_UPDATE); } /* * set_input: sets the input buffer to the given string, discarding whatever * was in the input buffer before */ void BX_set_input (char *str) { strmcpy(INPUT_BUFFER + MIN_POS, str, INPUT_BUFFER_SIZE - MIN_POS); THIS_POS = strlen(INPUT_BUFFER); } #ifdef WANT_HEBREW /* * eilon: same as the above just without the cursor reposition. * this allows input buffer "traveling" in Hebrew mode */ void BX_set_input_heb (char *str) { strmcpy(INPUT_BUFFER + MIN_POS, str, INPUT_BUFFER_SIZE - MIN_POS); } #endif /* * get_input: returns a pointer to the input buffer. Changing this will * actually change the input buffer. This is a bad way to change the input * buffer tho, cause no bounds checking won't be done */ char *BX_get_input (void) { return (&(MIN_CHAR)); } /* init_input: initialized the input buffer by clearing it out */ extern void init_input (void) { *INPUT_BUFFER = (char) 0; THIS_POS = MIN_POS; } /* get_input_prompt: returns the current input_prompt */ extern char *BX_get_input_prompt (void) { return input_prompt; } /* * set_input_prompt: sets a prompt that will be displayed in the input * buffer. This prompt cannot be backspaced over, etc. It's a prompt. * Setting the prompt to null uses no prompt */ void BX_set_input_prompt (Window *win, char *prompt, int unused) { if (prompt) malloc_strcpy(&input_prompt, prompt); else if (input_prompt) malloc_strcpy(&input_prompt, empty_string); else return; update_input(UPDATE_ALL); } /* * Why did i put these in this file? I dunno. But i do know that the ones * in edit.c didnt have to be here, and i knew the ones that were here DID * have to be here, so i just moved them all to here, so that they would all * be in the same place. Easy enough. (jfn, june 1995) */ /* * input_forward_word: move the input cursor forward one word in the input * line */ BUILT_IN_KEYBINDING(input_forward_word) { cursor_to_input(); while ((my_isspace(THIS_CHAR) || ispunct((unsigned char)THIS_CHAR)) && (THIS_CHAR)) THIS_POS++; while (!(ispunct((unsigned char)THIS_CHAR) || my_isspace(THIS_CHAR)) && (THIS_CHAR)) THIS_POS++; update_input(UPDATE_JUST_CURSOR); } /* input_backward_word: move the cursor left on word in the input line */ BUILT_IN_KEYBINDING(input_backward_word) { cursor_to_input(); while ((THIS_POS > MIN_POS) && (my_isspace(PREV_CHAR) || ispunct((unsigned char)PREV_CHAR))) THIS_POS--; while ((THIS_POS > MIN_POS) && !(ispunct((unsigned char)PREV_CHAR) || my_isspace(PREV_CHAR))) THIS_POS--; update_input(UPDATE_JUST_CURSOR); } /* inPut_delete_character: deletes a character from the input line */ BUILT_IN_KEYBINDING(input_delete_character) { int pos; cursor_to_input(); in_completion = STATE_NORMAL; if (!THIS_CHAR) return; ov_strcpy(&THIS_CHAR, &NEXT_CHAR); if (!(termfeatures & TERM_CAN_DELETE)) update_input(UPDATE_FROM_CURSOR); else { term_delete(1); pos = INPUT_ONSCREEN + last_input_screen->co - 1; if (pos < strlen(INPUT_BUFFER)) { term_move_cursor(last_input_screen->co - 1, INPUT_LINE); term_putchar(INPUT_BUFFER[pos]); term_move_cursor(INPUT_CURSOR, INPUT_LINE); } update_input(NO_UPDATE); } } /* input_backspace: does a backspace in the input buffer */ BUILT_IN_KEYBINDING(input_backspace) { cursor_to_input(); if (THIS_POS > MIN_POS) { char *ptr = NULL; int pos; ptr = LOCAL_COPY(&THIS_CHAR); strcpy(&(PREV_CHAR), ptr); THIS_POS--; term_cursor_left(); if (THIS_CHAR) { if (!(termfeatures & TERM_CAN_DELETE)) update_input(UPDATE_FROM_CURSOR); else { term_delete(1); pos = INPUT_ONSCREEN + last_input_screen->co - 1; /* * If the prompt is visible right now, * then we have to cope with that. */ if (START_ZONE == WIDTH) pos -= INPUT_PROMPT_LEN; if (pos < strlen(INPUT_BUFFER)) { term_move_cursor(last_input_screen->co - 1, INPUT_LINE); term_putchar(INPUT_BUFFER[pos]); } update_input(UPDATE_JUST_CURSOR); } } else { term_putchar(' '); #ifndef __EMX__ term_cursor_left(); update_input(NO_UPDATE); #else update_input(UPDATE_FROM_CURSOR); #endif } } if (THIS_POS == MIN_POS) HOLDLAST = NULL; in_completion = STATE_NORMAL; *new_nick = 0; } /* * input_beginning_of_line: moves the input cursor to the first character in * the input buffer */ BUILT_IN_KEYBINDING(input_beginning_of_line) { cursor_to_input(); THIS_POS = MIN_POS; update_input(UPDATE_JUST_CURSOR); } /* * input_beginning_of_line: moves the input cursor to the first character in * the input buffer */ BUILT_IN_KEYBINDING(new_input_beginning_of_line) { cursor_to_input(); THIS_POS = MIN_POS; update_input(UPDATE_JUST_CURSOR); extended_handled = 1; } /* * input_end_of_line: moves the input cursor to the last character in the * input buffer */ BUILT_IN_KEYBINDING(input_end_of_line) { cursor_to_input(); THIS_POS = strlen(INPUT_BUFFER); update_input(UPDATE_JUST_CURSOR); } BUILT_IN_KEYBINDING(input_delete_to_previous_space) { int old_pos; char c; cursor_to_input(); old_pos = THIS_POS; c = THIS_CHAR; while (!my_isspace(THIS_CHAR) && THIS_POS >= MIN_POS) THIS_POS--; if (THIS_POS < old_pos) { strcpy(&(NEXT_CHAR), &(INPUT_BUFFER[old_pos])); THIS_POS++; } update_input(UPDATE_FROM_CURSOR); } /* * input_delete_previous_word: deletes from the cursor backwards to the next * space character. */ BUILT_IN_KEYBINDING(input_delete_previous_word) { int old_pos; char c; cursor_to_input(); old_pos = THIS_POS; while ((THIS_POS > MIN_POS) && (my_isspace(PREV_CHAR) || ispunct((unsigned char)PREV_CHAR))) THIS_POS--; while ((THIS_POS > MIN_POS) && !(ispunct((unsigned char)PREV_CHAR) || my_isspace(PREV_CHAR))) THIS_POS--; c = INPUT_BUFFER[old_pos]; INPUT_BUFFER[old_pos] = (char) 0; malloc_strcpy(&cut_buffer, &THIS_CHAR); INPUT_BUFFER[old_pos] = c; strcpy(&(THIS_CHAR), &(INPUT_BUFFER[old_pos])); update_input(UPDATE_FROM_CURSOR); } /* * input_delete_next_word: deletes from the cursor to the end of the next * word */ BUILT_IN_KEYBINDING(input_delete_next_word) { int pos; char *ptr = NULL, c; cursor_to_input(); pos = THIS_POS; while ((my_isspace(INPUT_BUFFER[pos]) || ispunct((unsigned char)INPUT_BUFFER[pos])) && INPUT_BUFFER[pos]) pos++; while (!(ispunct((unsigned char)INPUT_BUFFER[pos]) || my_isspace(INPUT_BUFFER[pos])) && INPUT_BUFFER[pos]) pos++; c = INPUT_BUFFER[pos]; INPUT_BUFFER[pos] = (char) 0; malloc_strcpy(&cut_buffer, &(THIS_CHAR)); INPUT_BUFFER[pos] = c; malloc_strcpy(&ptr, &(INPUT_BUFFER[pos])); strcpy(&(THIS_CHAR), ptr); new_free(&ptr); update_input(UPDATE_FROM_CURSOR); } /* * input_add_character: adds the character c to the input buffer, repecting * the current overwrite/insert mode status, etc */ BUILT_IN_KEYBINDING(input_add_character) { int display_flag = NO_UPDATE; if (last_input_screen->promptlist) term_echo(last_input_screen->promptlist->echo); cursor_to_input(); if (THIS_POS >= INPUT_BUFFER_SIZE) { term_echo(1); return; } if (get_int_var(INSERT_MODE_VAR)) { if (THIS_CHAR) { char *ptr = NULL; ptr = alloca(strlen(&(THIS_CHAR)) + 1); strcpy(ptr, &(THIS_CHAR)); THIS_CHAR = key; NEXT_CHAR = 0; ADD_TO_INPUT(ptr); if (termfeatures & TERM_CAN_INSERT) term_insert(key); else { term_putchar(key); if (NEXT_CHAR) display_flag = UPDATE_FROM_CURSOR; else display_flag = NO_UPDATE; } } else { THIS_CHAR = key; NEXT_CHAR = 0; term_putchar(key); } } else { if (THIS_CHAR == 0) NEXT_CHAR = 0; THIS_CHAR = key; term_putchar(key); } if (!THIS_POS) HOLDLAST = current_window->display_ip; THIS_POS++; #ifdef GUI gui_flush(); #endif update_input(display_flag); if (in_completion == STATE_COMPLETE && key == ' ' && input_lastmsg) { new_free(&input_lastmsg); *new_nick = 0; in_completion = STATE_NORMAL; } term_echo(1); } /* input_clear_to_eol: erases from the cursor to the end of the input buffer */ BUILT_IN_KEYBINDING(input_clear_to_eol) { cursor_to_input(); malloc_strcpy(&cut_buffer, &(THIS_CHAR)); THIS_CHAR = 0; term_clear_to_eol(); update_input(NO_UPDATE); } /* * input_clear_to_bol: clears from the cursor to the beginning of the input * buffer */ BUILT_IN_KEYBINDING(input_clear_to_bol) { char *ptr = NULL; cursor_to_input(); malloc_strcpy(&cut_buffer, &(MIN_CHAR)); cut_buffer[THIS_POS - MIN_POS] = (char) 0; malloc_strcpy(&ptr, &(THIS_CHAR)); MIN_CHAR = (char) 0; ADD_TO_INPUT(ptr); new_free(&ptr); THIS_POS = MIN_POS; term_move_cursor(INPUT_PROMPT_LEN, INPUT_LINE); term_clear_to_eol(); update_input(UPDATE_FROM_CURSOR); } /* * input_clear_line: clears entire input line */ BUILT_IN_KEYBINDING(input_clear_line) { cursor_to_input(); malloc_strcpy(&cut_buffer, INPUT_BUFFER + MIN_POS); MIN_CHAR = (char) 0; THIS_POS = MIN_POS; term_move_cursor(INPUT_PROMPT_LEN, INPUT_LINE); term_clear_to_eol(); update_input(NO_UPDATE); } /* * input_transpose_characters: swaps the positions of the two characters * before the cursor position */ BUILT_IN_KEYBINDING(input_transpose_characters) { cursor_to_input(); if (last_input_screen->buffer_pos > MIN_POS) { u_char c1[3] = { 0 }; int pos, end_of_line = 0; if (THIS_CHAR) pos = THIS_POS; else if (strlen(get_input()) > MIN_POS + 2) { pos = THIS_POS - 1; end_of_line = 1; } else return; c1[0] = INPUT_BUFFER[pos]; c1[1] = INPUT_BUFFER[pos] = INPUT_BUFFER[pos - 1]; INPUT_BUFFER[pos - 1] = c1[0]; term_cursor_left(); if (end_of_line) term_cursor_left(); term_putchar(c1[0]); term_putchar(c1[1]); if (!end_of_line) term_cursor_left(); update_input(NO_UPDATE); } } BUILT_IN_KEYBINDING(refresh_inputline) { update_input(UPDATE_ALL); } /* * input_yank_cut_buffer: takes the contents of the cut buffer and inserts it * into the input line */ BUILT_IN_KEYBINDING(input_yank_cut_buffer) { char *ptr = NULL; if (cut_buffer) { malloc_strcpy(&ptr, &(THIS_CHAR)); /* Ooops... */ THIS_CHAR = 0; ADD_TO_INPUT(cut_buffer); ADD_TO_INPUT(ptr); new_free(&ptr); update_input(UPDATE_FROM_CURSOR); THIS_POS += strlen(cut_buffer); if (THIS_POS > INPUT_BUFFER_SIZE) THIS_POS = INPUT_BUFFER_SIZE; update_input(UPDATE_JUST_CURSOR); } } /* used with input_move_cursor */ #define RIGHT 1 #define LEFT 0 /* BIND functions: */ BUILT_IN_KEYBINDING(forward_character) { input_move_cursor(RIGHT); } BUILT_IN_KEYBINDING(backward_character) { input_move_cursor(LEFT); } BUILT_IN_KEYBINDING(backward_history) { get_history(PREV); } BUILT_IN_KEYBINDING(forward_history) { get_history(NEXT); } BUILT_IN_KEYBINDING(toggle_insert_mode) { int tog = get_int_var(INSERT_MODE_VAR); tog ^= 1; set_int_var(INSERT_MODE_VAR, tog); } BUILT_IN_KEYBINDING(input_msgreply) { char *cmdchar; char *line, *cmd, *t; char *snick; NickTab *nick = NULL; int got_space = 0; if (!(cmdchar = get_string_var(CMDCHARS_VAR))) cmdchar = DEFAULT_CMDCHARS; t = line = m_strdup(get_input()); if (t) got_space = strchr(t, ' ') ? 1 : 0; cmd = next_arg(line, &line); snick = next_arg(line, &line); if ((cmd && *cmd == *cmdchar && got_space) || !cmd) { if (cmd && *cmd == *cmdchar) cmd++; if (in_completion == STATE_NORMAL && snick) strncpy(new_nick, snick, sizeof(new_nick)-1); if ((nick = getnextnick(0, new_nick, input_lastmsg, snick))) { if (nick->nick && *(nick->nick)) { snick = nick->nick; malloc_strcpy(&input_lastmsg, nick->nick); } } if (nick) { char *tmp = NULL; input_clear_line('\0', NULL); if (fget_string_var(FORMAT_NICK_MSG_FSET)) malloc_strcpy(&tmp, stripansicodes(convert_output_format(fget_string_var(FORMAT_NICK_MSG_FSET), "%s%s %s %s", cmdchar, nick->type ? nick->type:cmd?cmd:"msg", nick->nick, line?line:empty_string))); else malloc_sprintf(&tmp, "%s%s %s %s", cmdchar, nick->type?nick->type:cmd?cmd:"msg", nick->nick, line?line:empty_string); set_input(tmp); new_free(&tmp); } else command_completion(0, NULL); } else command_completion(0, NULL); update_input(UPDATE_ALL); new_free(&t); } BUILT_IN_KEYBINDING(input_msgreplyback) { #if 0 char *tmp = NULL; char *cmdchar; tmp = gettabkey(-1, 0, NULL); if (tmp && *tmp) { char *tmp1 = NULL; input_clear_line('\0', NULL); if (!(cmdchar = get_string_var(CMDCHARS_VAR))) cmdchar = DEFAULT_CMDCHARS; if (fget_string_var(FORMAT_NICK_MSG_FSET)) malloc_strcpy(&tmp1, stripansicodes(convert_output_format(fget_string_var(FORMAT_NICK_MSG_FSET), "%cmsg %s", *cmdchar, tmp))); else malloc_sprintf(&tmp1, "%cmsg %s ", *cmdchar, tmp); set_input(tmp1); new_free(&tmp1); } #endif } void add_autonick_input(char *nick, char *line) { char *tmp1 = NULL; input_clear_line('\0', NULL); if ((do_hook(REPLY_AR_LIST, "%s", nick))) { if (fget_string_var(FORMAT_NICK_AUTO_FSET)) malloc_strcpy(&tmp1, stripansicodes(convert_output_format(fget_string_var(FORMAT_NICK_AUTO_FSET), "%s %s", nick, line?line:empty_string))); else malloc_sprintf(&tmp1, "%s\002:\002 %s" , nick, line); set_input(tmp1); new_free(&tmp1); } update_input(UPDATE_ALL); } BUILT_IN_KEYBINDING(input_autoreply) { char *tmp = NULL, *q; char *nick = NULL; char *line = NULL; q = line = m_strdup(&last_input_screen->input_buffer[MIN_POS]); if ((nick = next_arg(line, &line))) { if ((tmp = strrchr(nick, ':'))) *tmp = 0; if ((tmp = strrchr(nick, '\002'))) *tmp = 0; } if (!input_lastmsg) { NickTab *t; t = gettabkey(1, 1, nick); if (*new_nick && !tmp) t = gettabkey(1,1,new_nick); if (t) tmp = t->nick; } if (tmp && *tmp) { add_autonick_input(tmp, line); strcpy(new_nick, tmp); } else { tmp = getchannick(input_lastmsg, nick); if (*new_nick && !tmp) tmp = getchannick(input_lastmsg, new_nick); if (tmp) { add_autonick_input(tmp, line); strcpy(new_nick, tmp); } } malloc_strcpy(&input_lastmsg, nick); in_completion = STATE_COMPLETE; new_free(&q); } BUILT_IN_KEYBINDING(input_autoreplyback) { #if 0 char *tmp = NULL; tmp = gettabkey(-1, 1, NULL); if (tmp && *tmp) { char *tmp1 = NULL; input_clear_line('\0', NULL); if ((do_hook(AR_REPLY_LIST, "%s", tmp))) { if (fget_int_var(FORMAT_NICK_AUTO_FSET)) malloc_strcpy(&tmp1, stripansicodes(convert_output_format(fget_string_var(FORMAT_NICK_AUTO_FSET), "%s", tmp))); else malloc_sprintf(&tmp1, "%s\002:\002", tmp); set_input(tmp1); new_free(&tmp1); } } #endif } NickList *BX_lookup_nickcompletion(ChannelList *chan, char *possible) { NickList *nick, *ntmp = NULL, *bestmatch = NULL; for (nick = next_nicklist(chan, NULL); nick; nick = next_nicklist(chan, nick)) { if (!my_stricmp(possible, nick->nick)) bestmatch = ntmp = nick; else if (!my_strnicmp(possible, nick->nick, strlen(possible))) ntmp = nick; else if (!ntmp && my_strnstr(nick->nick, possible, strlen(possible))) ntmp = nick; } return bestmatch ? bestmatch : ntmp; } BUILT_IN_KEYBINDING(send_line) { int server; WaitPrompt *OldPrompt; server = from_server; from_server = get_window_server(0); unhold_a_window(last_input_screen->current_window); if (last_input_screen->promptlist && last_input_screen->promptlist->type == WAIT_PROMPT_LINE) { OldPrompt = last_input_screen->promptlist; (*OldPrompt->func)(OldPrompt->data, get_input()); set_input(empty_string); last_input_screen->promptlist = OldPrompt->next; new_free(&OldPrompt->data); new_free(&OldPrompt->prompt); new_free((char **)&OldPrompt); change_input_prompt(-1); } else { char *line, *cmdchar, *tmp = NULL; line = get_input(); if (!(cmdchar = get_string_var(CMDCHARS_VAR))) cmdchar = "/"; malloc_strcpy(&tmp, line); if (line && (*line != *cmdchar) && get_int_var(NICK_COMPLETION_VAR) && !query_nick()) { char auto_comp_char; char *p; if (!(auto_comp_char = (char)get_int_var(NICK_COMPLETION_CHAR_VAR))) auto_comp_char = ':'; /* possible nick completion */ if ((p = strchr(tmp, auto_comp_char)) && do_hook(NICK_COMP_LIST, "%s", line)) { ChannelList *chan; NickList *nick; char *channel; *p++ = 0; if (*tmp && *p && (strlen(tmp) >= get_int_var(NICK_COMPLETION_LEN_VAR) && (channel = get_current_channel_by_refnum(0)))) { chan = lookup_channel(channel, from_server, 0); nick = lookup_nickcompletion(chan, tmp); if (nick) { if (fget_string_var(FORMAT_NICK_COMP_FSET)) malloc_strcpy(&tmp, stripansicodes(convert_output_format(fget_string_var(FORMAT_NICK_COMP_FSET), "%s %s", nick->nick, p))); else malloc_sprintf(&tmp, "%s\002%c\002%s", nick->nick, auto_comp_char, p); } else malloc_strcpy(&tmp, line); } else malloc_strcpy(&tmp, line); } } set_input(empty_string); update_input(UPDATE_ALL); reset_display_target(); #ifdef WANT_TCL if (!check_tcl_input(tmp) && do_hook(INPUT_LIST, "%s", tmp)) #else if (do_hook(INPUT_LIST, "%s", tmp)) #endif { if (get_int_var(INPUT_ALIASES_VAR)) parse_line(NULL, tmp, empty_string, 1, 0, 1); else parse_line(NULL, tmp, NULL, 1, 0, 1); } new_free(&tmp); } new_free(&input_lastmsg); *new_nick = 0; in_completion = STATE_NORMAL; HOLDLAST = NULL; from_server = server; } #define METAX(x) \ BUILT_IN_KEYBINDING( meta ## x ## _char ) \ { last_input_screen->meta_hit = (x); } METAX(39) METAX(38) METAX(37) METAX(36) METAX(35) METAX(34) METAX(33) METAX(32) METAX(31) METAX(30) METAX(29) METAX(28) METAX(27) METAX(26) METAX(25) METAX(24) METAX(23) METAX(22) METAX(21) METAX(20) METAX(19) METAX(18) METAX(17) METAX(16) METAX(15) METAX(14) METAX(13) METAX(12) METAX(11) METAX(10) METAX(9) METAX(8) METAX(7) METAX(6) METAX(5) METAX(3) METAX(2) METAX(1) BUILT_IN_KEYBINDING(meta4_char) { if (last_input_screen->meta_hit == 4) last_input_screen->meta_hit = 0; else last_input_screen->meta_hit = 4; } BUILT_IN_KEYBINDING(quote_char) { last_input_screen->quote_hit = 1; } /* These four functions are boomerang functions, which allow the highlight * characters to be bound by simply having these functions put in the * appropriate characters when you press any key to which you have bound * that highlight character. >;-) */ BUILT_IN_KEYBINDING(insert_bold) { input_add_character (BOLD_TOG, string); } BUILT_IN_KEYBINDING(insert_reverse) { input_add_character (REV_TOG, string); } BUILT_IN_KEYBINDING(insert_underline) { input_add_character (UND_TOG, string); } BUILT_IN_KEYBINDING(highlight_off) { input_add_character (ALL_OFF, string); } BUILT_IN_KEYBINDING(insert_blink) { input_add_character (BLINK_TOG, string); } BUILT_IN_KEYBINDING(insert_altcharset) { input_add_character (ALT_TOG, string); } /* type_text: the BIND function TYPE_TEXT */ BUILT_IN_KEYBINDING(type_text) { if (!string) return; for (; *string; string++) input_add_character(*string, empty_string); } /* * clear_screen: the CLEAR_SCREEN function for BIND. Clears the screen and * starts it if it is held */ BUILT_IN_KEYBINDING(clear_screen) { hold_mode(NULL, OFF, 1); clear_window_by_refnum(0); } /* parse_text: the bindable function that executes its string */ BUILT_IN_KEYBINDING(parse_text) { parse_line(NULL, string, empty_string, 0, 0, 1); } /* * edit_char: handles each character for an input stream. Not too difficult * to work out. */ void edit_char (u_char key) { void (*func) (char, char *) = NULL; char *ptr = NULL; u_char extended_key; WaitPrompt *oldprompt; int xxx_return = 0; if (dumb_mode) { #ifdef TIOCSTI ioctl(0, TIOCSTI, &key); #else say("Sorry, your system doesnt support 'faking' user input..."); #endif return; } /* were we waiting for a keypress? */ if (last_input_screen->promptlist && last_input_screen->promptlist->type == WAIT_PROMPT_KEY) { unsigned char key_[2] = "\0"; key_[0] = key; oldprompt = last_input_screen->promptlist; last_input_screen->promptlist = oldprompt->next; (*oldprompt->func)(oldprompt->data, key_); new_free(&oldprompt->data); new_free(&oldprompt->prompt); new_free((char **)&oldprompt); set_input(empty_string); change_input_prompt(-1); xxx_return = 1; } if (last_input_screen->promptlist && last_input_screen->promptlist->type == WAIT_PROMPT_DUMMY) { oldprompt = last_input_screen->promptlist; (*oldprompt->func)(oldprompt->data, NULL); last_input_screen->promptlist = oldprompt->next; new_free(&oldprompt->data); new_free(&oldprompt->prompt); new_free((char **)&oldprompt); } if (xxx_return) return; #if __bsdi__ if (key & 0x80) { if (meta_mode) { edit_char('\033'); key &= ~0x80; } else if (!term_eight_bit()) key &= ~0x80; } #endif #ifdef TRANSLATE if (translation) extended_key = transFromClient[key]; else #endif extended_key = key; #ifdef __EMX__ if (key == 0) key = 27; #endif /* did we just hit the quote character? */ if (last_input_screen->quote_hit) { last_input_screen->quote_hit = 0; input_add_character(extended_key, empty_string); } else { int m = last_input_screen->meta_hit; int i; if ((i = get_binding(m, key, &func, &ptr))) last_input_screen->meta_hit = i; else if (last_input_screen->meta_hit != 4) last_input_screen->meta_hit = 0; if (func) func(extended_key, SAFE(ptr)); } } #ifdef GUI BUILT_IN_KEYBINDING(paste_to_input) { /* Try calling this directly ... */ parse_line(NULL, "//pmpaste -input", NULL, 0, 0, 1); } #endif BUILT_IN_KEYBINDING(my_scrollback) { scrollback_backwards(key, string); extended_handled = 1; } BUILT_IN_KEYBINDING(my_scrollforward) { scrollback_forwards(key, string); extended_handled = 1; } BUILT_IN_KEYBINDING(my_scrollend) { scrollback_end(key, string); extended_handled = 1; } #ifdef WANT_CHELP BUILT_IN_KEYBINDING(do_chelp) { chelp(NULL, "INDEX", NULL, NULL); extended_handled = 1; } #endif #ifdef WANT_CDCC BUILT_IN_KEYBINDING(cdcc_plist) { l_plist(NULL, NULL); extended_handled = 1; } #endif BUILT_IN_KEYBINDING(dcc_plist) { dcc_glist(NULL, NULL); extended_handled = 1; } BUILT_IN_KEYBINDING(toggle_cloak) { if (get_int_var(CLOAK_VAR) == 1 || get_int_var(CLOAK_VAR) == 2) set_int_var(CLOAK_VAR, 0); else set_int_var(CLOAK_VAR, 1); put_it("CTCP Cloaking is now [\002%s\002]", on_off(get_int_var(CLOAK_VAR))); extended_handled = 1; } extern int in_window_command; static void handle_swap(int windownum) { char *p = NULL; malloc_sprintf(&p, "SWAP %d", windownum); windowcmd(NULL, p, NULL, NULL); set_channel_window(current_window, get_current_channel_by_refnum(current_window->refnum), current_window->server); new_free(&p); set_input_prompt(current_window, get_string_var(INPUT_PROMPT_VAR), 0); target_window = NULL; update_input(UPDATE_ALL); update_all_windows(); } BUILT_IN_KEYBINDING(window_swap1) { handle_swap(1); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap2) { handle_swap(2); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap3) { handle_swap(3); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap4) { handle_swap(4); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap5) { handle_swap(5); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap6) { handle_swap(6); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap7) { handle_swap(7); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap8) { handle_swap(8); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap9) { handle_swap(9); extended_handled = 1; } BUILT_IN_KEYBINDING(window_swap10) { handle_swap(10); extended_handled = 1; } BUILT_IN_KEYBINDING(channel_chops) { users("chops", "-ops", NULL, NULL); extended_handled = 1; } BUILT_IN_KEYBINDING(channel_nonops) { users("nops", "-nonops", NULL, NULL); extended_handled = 1; } #ifdef WANT_CHELP BUILT_IN_KEYBINDING(w_help) { chelp(NULL, "WINDOW", NULL, NULL); } #endif BUILT_IN_KEYBINDING(change_to_split) { extern char *last_split_server; if (!last_split_server) return; servercmd(NULL, last_split_server, NULL, NULL); } BUILT_IN_KEYBINDING(join_last_invite) { if (invite_channel) { if (!in_join_list(invite_channel, from_server)) { win_create(JOIN_NEW_WINDOW_VAR, 0); send_to_server("JOIN %s", invite_channel); new_free(&invite_channel); } else bitchsay("Already joining %s", invite_channel); } else bitchsay("You haven't been invited to a channel yet"); } BUILT_IN_KEYBINDING(wholeft) { show_wholeft(NULL); } static int oiwc; static int osu; BUILT_IN_KEYBINDING(window_key_balance) { oiwc = in_window_command; osu = status_update_flag; in_window_command = 1; set_display_target(NULL, LOG_CURRENT); rebalance_windows(current_window->screen); in_window_command = oiwc; status_update_flag = osu; update_all_windows(); update_all_status(current_window, NULL, 0); reset_display_target(); } BUILT_IN_KEYBINDING(window_grow_one) { oiwc = in_window_command; osu = status_update_flag; in_window_command = 1; set_display_target(NULL, LOG_CURRENT); resize_window(1, current_window, 1); in_window_command = oiwc; status_update_flag = osu; update_all_windows(); update_all_status(current_window, NULL, 0); reset_display_target(); } BUILT_IN_KEYBINDING(window_key_hide) { oiwc = in_window_command; osu = status_update_flag; in_window_command = 1; set_display_target(NULL, LOG_CURRENT); hide_window(current_window); in_window_command = oiwc; status_update_flag = osu; update_all_windows(); update_all_status(current_window, NULL, 0); reset_display_target(); } BUILT_IN_KEYBINDING(window_key_kill) { oiwc = in_window_command; osu = status_update_flag; in_window_command = 1; set_display_target(NULL, LOG_CURRENT); delete_window(current_window); in_window_command = oiwc; status_update_flag = osu; update_all_windows(); update_all_status(current_window, NULL, 0); reset_display_target(); } BUILT_IN_KEYBINDING(window_key_list) { oiwc = in_window_command; osu = status_update_flag; in_window_command = 1; set_display_target(NULL, LOG_CURRENT); window_list(current_window, NULL, NULL); in_window_command = oiwc; status_update_flag = osu; reset_display_target(); } BUILT_IN_KEYBINDING(window_key_move) { oiwc = in_window_command; osu = status_update_flag; in_window_command = 1; set_display_target(NULL, LOG_CURRENT); move_window(current_window, 1); in_window_command = oiwc; status_update_flag = osu; update_all_windows(); update_all_status(current_window, NULL, 0); reset_display_target(); } BUILT_IN_KEYBINDING(window_shrink_one) { oiwc = in_window_command; osu = status_update_flag; in_window_command = 1; set_display_target(NULL, LOG_CURRENT); resize_window(1, current_window, -1); in_window_command = oiwc; status_update_flag = osu; update_all_windows(); update_all_status(current_window, NULL, 0); reset_display_target(); } BUILT_IN_KEYBINDING(dcc_ostats) { set_display_target(NULL, LOG_CURRENT); extended_handled = 1; dcc_stats(NULL, NULL); reset_display_target(); } BUILT_IN_KEYBINDING(cpu_saver_on) { cpu_saver = 1; update_all_status(current_window, NULL, 0); } BUILT_IN_KEYBINDING(input_unclear_screen) { hold_mode(NULL, OFF, 1); unclear_window_by_refnum(0); } BUILT_IN_KEYBINDING(ignore_last_nick) { NickTab *nick; char *tmp1; if ((nick = gettabkey(1, 0, NULL))) { set_input(empty_string); tmp1 = m_sprintf("%sig %s", get_string_var(CMDCHARS_VAR), nick->nick); set_input(tmp1); new_free(&tmp1); } update_input(UPDATE_ALL); } BUILT_IN_KEYBINDING(nick_completion) { char *q, *line; int i = -1; char *nick = NULL, *tmp; q = line = m_strdup(&last_input_screen->input_buffer[MIN_POS]); if (in_completion == STATE_NORMAL) { i = word_count(line); nick = extract(line, i-1, i); } if (nick) line[strlen(line)-strlen(nick)] = 0; else *line = 0; if ((tmp = getchannick(input_lastmsg, nick && *nick ? nick : NULL))) { malloc_strcat(&q, tmp); set_input(q); update_input(UPDATE_ALL); malloc_strcpy(&input_lastmsg, tmp); in_completion = STATE_COMPLETE; } new_free(&q); new_free(&nick); } char *BX_getchannick (char *oldnick, char *nick) { ChannelList *chan; char *channel, *tnick = NULL; NickList *cnick; channel = get_current_channel_by_refnum(0); if (channel) { if (!(chan = lookup_channel(channel, from_server, 0)) || !(cnick = next_nicklist(chan, NULL))) { in_completion = STATE_NORMAL; return NULL; } /* * we've never been here before so return first nick * user hasn't entered anything on the line. */ if (!oldnick && !nick && cnick) { in_completion = STATE_CNICK; return cnick->nick; } /* * user has been here before so we attempt to find the correct * first nick to start from. */ if (oldnick) { /* find the old nick so we have a frame of reference */ for(cnick = next_nicklist(chan, NULL); cnick; cnick = next_nicklist(chan, cnick)) { if (!my_strnicmp(cnick->nick, oldnick, strlen(oldnick))) { tnick = cnick->nick; if ((cnick = next_nicklist(chan, cnick))) tnick = cnick->nick; break; } } } /* * if the user has put something on the line * we attempt to pattern match here. */ if (nick && in_completion == STATE_NORMAL) { /* * if oldnick was the last one in the channel * cnick will be NULL; */ if (!cnick) { cnick = next_nicklist(chan, NULL); tnick = cnick->nick; } /* we have a new nick */ else if (next_nicklist(chan, cnick)) { /* * if there's more than one nick, start * scanning. */ for (; cnick; cnick = next_nicklist(chan, cnick)) { #if 1 if (!my_strnicmp(cnick->nick, nick, strlen(nick)) || my_strnstr(cnick->nick, nick, strlen(nick))) #else if (!my_strnicmp(cnick->nick, nick, strlen(nick))) #endif { tnick = cnick->nick; break; } } #if 1 /* Same as before, make sure bx gets its try w/o ours interfering... */ if (!tnick) { for (; cnick; cnick = next_nicklist(chan, cnick)) { if (my_strnstr(cnick->nick, nick, strlen(nick))) { tnick = cnick->nick; break; } } } #endif } else tnick = cnick->nick; } else if (in_completion == STATE_CNICK) { /* * else we've been here before so * attempt to continue through the nicks */ if (!cnick) cnick = next_nicklist(chan, NULL); tnick = cnick->nick; } } if (tnick) in_completion = STATE_CNICK; return tnick; } /* * set which to 1 to access autoreply array. * 0 for msglist array */ void BX_addtabkey(char *nick, char *type, int which) { NickTab *tmp, *new; tmp = (which == 1) ? autoreply_array : tabkey_array; if (!tmp || !(new = (NickTab *)remove_from_list((List **)&tmp, nick))) { new = (NickTab *)new_malloc(sizeof(NickTab)); malloc_strcpy(&new->nick, nick); if (type) malloc_strcpy(&new->type, type); } /* * most recent nick is at the top of the list */ new->next = tmp; tmp = new; if (which == 1) autoreply_array = tmp; else tabkey_array = tmp; } NickTab *BX_gettabkey(int direction, int which, char *nick) { NickTab *tmp, *new; new = tmp = (which == 1) ? autoreply_array : tabkey_array; if (nick) { for (; tmp; tmp = tmp->next) if (!my_strnicmp(nick, tmp->nick, strlen(nick))) return tmp; return NULL; } tmp = new; if (!tmp) return NULL; switch(direction) { case 1: default: { /* * need at least two nicks in the list */ if (new->next) { /* * reset top of array */ if (which == 1) autoreply_array = new->next; else tabkey_array = new->next; /* * set the current nick next pointer to NULL * and then reset top of list. */ new->next = NULL; if (which == 1) tmp = autoreply_array; else tmp = tabkey_array; /* * find the last nick in the list * so we can make the old top pointer * point to the item */ while (tmp) if (tmp->next) tmp = tmp->next; else break; /* set the pointer and then return. */ tmp->next = new; } break; } case -1: { if (new && new->next) { tmp = new; while(tmp) if (tmp->next && tmp->next->next) tmp = tmp->next; else break; /* * tmp now points at last two items in list * now just swap some pointers. */ new = tmp->next; tmp->next = NULL; if (which == 1) { new->next = autoreply_array; autoreply_array = new; } else { new->next = tabkey_array; tabkey_array = new; } } break; } } if (new && new->nick) return new; return NULL; } NickTab *BX_getnextnick(int which, char *input_nick, char *oldnick, char *nick) { ChannelList *chan; NickList *cnick = NULL; NickTab *tmp = (which == 1) ? autoreply_array : tabkey_array; int server = from_server; static NickTab sucks = { NULL }; if (tmp && (in_completion == STATE_NORMAL || in_completion == STATE_TABKEY)) { if (!oldnick && !nick && tmp) { in_completion = STATE_TABKEY; return tmp; } if (oldnick) { for (; tmp; tmp = tmp->next) { if (!my_strnicmp(oldnick, tmp->nick, strlen(oldnick))) break; } /* nick was not in the list. oops didn't come from here */ if (!tmp && in_completion == STATE_TABKEY) tmp = (which == 1) ? autoreply_array : tabkey_array; else if (tmp) tmp = tmp->next; } if (nick && in_completion != STATE_TABKEY) { if (tmp && tmp->next) { for (; tmp; tmp = tmp->next) if (!my_strnicmp(nick, tmp->nick, strlen(nick))) break; } } if (tmp) { in_completion = STATE_TABKEY; return tmp; } } if ((chan = prepare_command(&server, NULL, 3))) { cnick = next_nicklist(chan, NULL); /* * we've never been here before so return first nick * user hasn't entered anything on the line. */ if (!oldnick && !nick && cnick) { in_completion = STATE_CNICK; sucks.nick = cnick->nick; return &sucks; } /* * user has been here before so we attempt to find the correct * first nick to start from. */ if (oldnick) { /* find the old nick so we have a frame of reference */ for (; cnick; cnick = next_nicklist(chan, cnick)) { if (!my_strnicmp(cnick->nick, oldnick, strlen(oldnick))) { cnick = next_nicklist(chan, cnick); break; } } } /* * if the user has put something on the line * we attempt to pattern match here. */ if (input_nick) { /* * if oldnick was the last one in the channel * cnick will be NULL; */ if (!cnick && oldnick) cnick = next_nicklist(chan, NULL); /* we have a new nick */ else if (cnick) { /* * if there's more than one nick, start * scanning. */ for (;cnick; cnick = next_nicklist(chan, cnick)) { if (!my_strnicmp(cnick->nick, input_nick, strlen(input_nick)) || !strcasecmp(cnick->nick, input_nick)) break; } } } else if (in_completion == STATE_CNICK) { /* * else we've been here before so * attempt to continue through the nicks */ /* if (!cnick) cnick = chan->nicks; */ } } if (!cnick) in_completion = STATE_NORMAL; else in_completion = STATE_CNICK; if (cnick) sucks.nick = cnick->nick; return sucks.nick ? &sucks : NULL; } #ifdef WANT_TABKEY #define TABDEBUG struct _name_type { char name[10]; int len; }; struct _name_type name_type[] = { {"MP3", 3}, {"DCC", 3}, {"EXEC",4}, {"LS", 2}, {"LOAD",4}, {"SERVER",6}, {"CDCC",4}, {"MSG", 1}, {"KB", 2}, {"CD", 2}, { "", 0}}; enum tab_types { MP3 = 0, DCC, EXEC, LS, LOAD, SERVER, CDCC, IMSG, KB, CHDIR}; typedef struct _ext_name_type { struct _ext_name_type *next; char *name; int len; enum completion type; } Ext_Name_Type; Ext_Name_Type *ext_completion = NULL; char *get_home_dir(char *possible, int *count) { #ifdef HAVE_GETPWENT struct passwd *pw; char *booya = NULL; int len = 0; char *q; char str[BIG_BUFFER_SIZE+1]; (*count) = 0; if (possible) { possible++; len = strlen(possible); } while ((pw = getpwent())) { *str = 0; if ((q = strrchr(pw->pw_dir, '/'))) q++; if (possible && *possible && q && strncmp(possible, q, len)) continue; if (q && *q) { strmopencat(str, BIG_BUFFER_SIZE, "~", q, NULL); m_s3cat(&booya, space, str); } else m_s3cat(&booya, space, pw->pw_dir); (*count)++; } endpwent(); if (*count) return booya; #endif return NULL; } char *get_completions(enum completion type, char *possible, int *count, char **suggested) { char *booya = NULL; char *path = NULL; char *path2, *freeme; glob_t globbers; int numglobs = 0, i; int globtype = GLOB_MARK; #if defined(__EMX__) || defined(WINNT) if (possible && *possible) convert_unix(possible); #endif switch(type) { case SERVER_COMPLETION: { for (i = 0; i < server_list_size(); i++) { if (possible && my_strnicmp(possible, get_server_name(i), strlen(possible))) continue; m_s3cat(&booya, space, get_server_name(i)); (*count)++; } return booya; } case TABKEY_COMPLETION: { NickTab *n = tabkey_array; *count = 0; if (possible) { if (*possible == '#' || *possible == '&') { ChannelList *chan; for (chan = get_server_channels(current_window->server); chan; chan = chan->next) { if (my_strnicmp(possible, chan->channel, strlen(possible))) continue; m_s3cat(&booya, space, chan->channel); (*count)++; } } else { for ( ; n; n = n->next) { if (possible && my_strnicmp(possible, n->nick, strlen(possible))) continue; m_s3cat(&booya, space, n->nick); (*count)++; } if (*count == 1 && !strcmp(possible, booya)) { new_free(&booya); (*count) = 0; possible = NULL; } } } if (((*count) == 0) || ((*count) == 1)) { /* * nothing specified for a match. * so grab the first one, if available. * or, only one match was available. */ if (possible && count == 0) possible = NULL; if ((n = gettabkey(1, 0, possible)) && !booya) { (*count) = 1; booya = m_strdup(n->nick); if (suggested) *suggested = n->type; } } return booya; } case COM_COMPLETION: command_completion(0, NULL); return NULL; case CHAN_COMPLETION: { NickList *cnick = NULL; ChannelList *chan = NULL; int server = from_server; chan = prepare_command(&server, NULL, 3); if (possible && (*possible == '#' || *possible == '&')) { int len = strlen(possible); for (chan = get_server_channels(current_window->server); chan; chan = chan->next) { if ((len == 1) || (len >= 2 && !my_strnicmp(possible, chan->channel, len))) { (*count)++; m_s3cat(&booya, space, chan->channel); } } } else if (chan) { cnick = next_nicklist(chan, NULL); for (; cnick; cnick = next_nicklist(chan, cnick)) { if (possible && my_strnicmp(cnick->nick, possible, strlen(possible))) continue; (*count)++; m_s3cat(&booya, space, cnick->nick); } } if (!booya) { for (chan = get_server_channels(current_window->server); chan; chan = chan->next) { cnick = next_nicklist(chan, NULL); for (; cnick; cnick = next_nicklist(chan, cnick)) { if (possible && my_strnicmp(cnick->nick, possible, strlen(possible))) continue; (*count)++; m_s3cat(&booya, space, cnick->nick); } } } return booya; } case LOAD_COMPLETION: { if (!possible) path = m_sprintf("~/.BitchX/"); else { if (*possible == '/' || *possible == '~' || *possible == '.') path = m_sprintf("%s*", possible); else path = m_sprintf("~/%s*", possible); } break; } case EXEC_COMPLETION: { if (!possible) #if !defined(__EMX__) && !defined(WINNT) path = m_sprintf("/usr/bin/*"); #elif defined(__EMX__) path = m_sprintf("C:/OS2/*.EXE"); #elif defined(WINNT) /* Might need something for WinNT itself C:/WINNT/SYSTEM32/ *.EXE */ path = m_sprintf("C:/WINDOWS/COMMAND/*.EXE"); #endif else { if (*possible == '~' && (*(possible+1) != '/')) { if ((booya = get_home_dir(possible, count))) return booya; } #if defined(__EMX__) || defined(WINNT) if (*possible == '/' || *possible == '~' || *possible == '.' || (strlen(possible) > 3 && *(possible+1) == ':' && *(possible+2) == '/')) path = m_sprintf("%s*.EXE", possible); else path = m_sprintf("~/%s*.EXE", possible); #else if (*possible == '/' || *possible == '~' || *possible == '.') path = m_sprintf("%s*", possible); else path = m_sprintf("~/%s*", possible); #endif } break; } case CDCC_COMPLETION: case DCC_COMPLETION: { char *dl = get_string_var(DCC_DLDIR_VAR); if (!possible) path = m_sprintf("%s%s%s", dl, strrchr(dl, '/')?empty_string:"/", "*"); else { if (*possible == '~' && (*(possible+1) != '/')) { if ((booya = get_home_dir(possible, count))) return booya; } #if defined(__EMX__) || defined(WINNT) if (*possible == '/' || *possible == '~' || *possible == '.' || (strlen(possible) > 3 && *(possible+1) == ':' && *(possible+2) == '/')) #else if (*possible == '/' || *possible == '~' || *possible == '.') #endif path = m_sprintf("%s*", possible); else path = m_sprintf("~/%s*", possible); } break; } case FILE_COMPLETION: { globtype |= GLOB_TILDE; if (!possible) path = m_sprintf("~/*"); else if (*possible == '~' && (*(possible+1) != '/')) { if ((booya = get_home_dir(possible, count))) return booya; path = m_strdup(possible); } else if (*possible == '/' || *possible == '~' || *possible == '.') path = m_sprintf("%s*", possible); else path = m_sprintf("~/%s*", possible); #if 0 path = m_sprintf("%s%s%s", possible, strrchr(possible, '/') ? empty_string: "/", strchr(possible, '*')?empty_string:"*"); #endif break; } default: return NULL; } #ifdef NEED_GLOB #define glob bsd_glob #define globfree bsd_globfree #endif freeme = path2 = expand_twiddle(path); if (!path2) path2 = path; memset(&globbers, 0, sizeof(glob_t)); numglobs = glob(path2, globtype, NULL, &globbers); for (i = 0; i < globbers.gl_pathc; i++) { if (strchr(globbers.gl_pathv[i], ' ')) { int len = strlen(globbers.gl_pathv[i])+4; char *b = alloca(len+1); *b = 0; strmopencat(b, len, "\"", globbers.gl_pathv[i], "\"", NULL); m_s3cat(&booya, space, b); } else m_s3cat(&booya, space, globbers.gl_pathv[i]); (*count)++; } globfree(&globbers); new_free(&freeme); new_free(&path); return booya; } /* * this is an oddball routine that attempts to figure out where * the last char is in the completes array, that is equal in all * the array conditions. when found it sets the input prompt to the * command [inp] and the greatest amount of chars found common in all * array elements. */ void set_input_best(enum completion type, char *inp, char *old, int count, char **completes) { int i, c, j = 0, match = 0; char buffer[BIG_BUFFER_SIZE+1]; match = strlen(old); if (count == 2) { #if !defined(__EMX__) && !defined(WINNT) if (type == CHAN_COMPLETION || type == NICK_COMPLETION || type == TABKEY_COMPLETION) i = strieq(completes[0], completes[1]); else #endif i = streq(completes[0], completes[1]); completes[0][i] = 0; } else { int getout; i = match - 1; while (1) { getout = 0; c = 1; for (j = 1; j < count; j++) { if (!completes[0][i] || !completes[j][i]) break; #if defined(__EMX__) || defined(WINNT) if (toupper(completes[0][i]) == toupper(completes[j][i])) c++; #else if (type == CHAN_COMPLETION || type == NICK_COMPLETION || type == TABKEY_COMPLETION) { if (toupper(completes[0][i]) == toupper(completes[j][i])) c++; else { getout = 1; break; } } else { if (completes[0][i] == completes[j][i]) c++; else { getout = 1; break; } } #endif } if ((c != count) || getout) break; i++; } if (i) completes[0][i] = 0; } if (old && *old) { if (strchr(completes[0], ' ')) sprintf(buffer, "%s%s\"%s\"", inp, space, i ? completes[0] : old); else sprintf(buffer, "%s%s%s", inp, space, i ? completes[0] : old); } else strcpy(buffer, completes[0]); if (strcmp(buffer, get_input())) set_input(buffer); if (strchr(completes[0], ' ')) THIS_POS = strlen(INPUT_BUFFER) - 1; } BUILT_IN_KEYBINDING(tab_completion) { int count = 0, wcount = 0; enum completion type = NO_COMPLETION; char *inp = NULL; char *possible = NULL, *old_pos = NULL; char *cmdchar; char *suggested = NULL; int got_space = 0; char *get = NULL; Ext_Name_Type *extcomp = ext_completion; /* * is this the != second word, then just complete from the * channel nicks. if it is the second word, grab the first word, and * this is used to determine what type of nick completion will be * done. */ inp = alloca(strlen(get_input())+2); strcpy(inp, get_input() ? get_input() : empty_string); if (*inp && inp[strlen(inp)-1] == ' ') got_space = 1; wcount = word_count(inp); if (!(cmdchar = get_string_var(CMDCHARS_VAR))) cmdchar = "/"; switch(wcount) { case 0: { type = TABKEY_COMPLETION; break; } case 1: { if (*inp != *cmdchar) { type = CHAN_COMPLETION; possible = m_strdup(get_input()); break; } else if (!got_space) { type = COM_COMPLETION; break; } } default: { char *p, *old_p; int i, got_comp = 0; old_p = p = extract(inp, 0, 0); if (wcount > 1) old_pos = possible = extract(inp, wcount-1, EOS); if ((*p == *cmdchar)) p++; if (possible && (*possible == '"')) { possible++; if (*possible) chop(possible, 1); else possible = NULL; } if (type == NO_COMPLETION) type = CHAN_COMPLETION; for (i = 0; *p && name_type[i].len; i++) { if (!my_strnicmp(p, name_type[i].name, name_type[i].len/*strlen(p)*/)) { got_comp = 1; switch(i) { case IMSG: case KB: type = TABKEY_COMPLETION; break; case DCC: if (wcount <= 3 && !got_space) type = TABKEY_COMPLETION; else type = DCC_COMPLETION; if (wcount == 3 && got_space) { new_free(&old_pos); possible = NULL; } break; case EXEC: type = EXEC_COMPLETION; break; case CHDIR: case MP3: case LS: type = FILE_COMPLETION; break; case LOAD: type = LOAD_COMPLETION; break; case SERVER: type = SERVER_COMPLETION; break; case CDCC: if (wcount == 2 || wcount == 3) type = CDCC_COMPLETION; break; } break; } } if (!got_comp) { for (extcomp = ext_completion; extcomp; extcomp = extcomp->next) { if (!my_strnicmp(p, extcomp->name, extcomp->len)) { type = extcomp->type; break; } } } new_free(&old_p); break; } } #ifdef TABDEBUG1 put_it("type = %s", type == TABKEY_COMPLETION ? "TABKEY": type == CHAN_COMPLETION? "CHAN" : type == COM_COMPLETION? "COMMAND": type == EXEC_COMPLETION? "EXEC": type == FILE_COMPLETION? "FILE": type == DCC_COMPLETION? "DCC": "NO_COMPLETION"); #endif do_more_tab: count = 0; if ((get = get_completions(type, possible, &count, &suggested))) { char buffer[BIG_BUFFER_SIZE+1]; char *p = NULL; char *old = NULL; *buffer = 0; if (count == 1) { if (wcount > 1) p = extract(get_input(), 0, wcount - 2); else if (suggested && *suggested) p = m_3dup("/", suggested, ""); if (type == TABKEY_COMPLETION) snprintf(buffer, BIG_BUFFER_SIZE, "%s %s%s%s ", (p && *p == '/') ? p : "/m", get, (p && (*p != '/'))?space:empty_string, (p && (*p != '/'))?p:empty_string); else { if (wcount == 1 && got_space) snprintf(buffer, BIG_BUFFER_SIZE, "%s %s ", get_input(), get); else snprintf(buffer, BIG_BUFFER_SIZE, "%s%s%s ", p ? p : get, p ? space : empty_string, p ? get : empty_string); } if ((type == CDCC_COMPLETION || type == LOAD_COMPLETION || type == FILE_COMPLETION || type == DCC_COMPLETION) || ((type == EXEC_COMPLETION) && (get[strlen(get)-1] == '/'))) chop(buffer, 1); set_input(buffer); if (strchr(get, ' ')) THIS_POS = strlen(INPUT_BUFFER) - 1; new_free(&p); } else { char **completes = NULL; int c = 0, matches = count; completes = RESIZE(completes, char *, count+1); if (wcount > 1) { if (!got_space) { old = inp; old = last_arg(&inp); if ((*old == '"')) { old++; chop(old, 1); } } } switch(type) { case LOAD_COMPLETION: case DCC_COMPLETION: case FILE_COMPLETION: case CDCC_COMPLETION: { char *n, *use = get; bitchsay("Found %d files/dirs", count); n = new_next_arg(use, &use); while (n && *n) { put_it("%s", n); completes[c++] = n; n = new_next_arg(use, &use); } break; } case EXEC_COMPLETION: { char *n, *q, *use = get; char path[BIG_BUFFER_SIZE+1]; *path = 0; memset(path, 0, sizeof(path)); n = new_next_arg(use, &use); if ((q = strrchr(n, '/')) && *(q+1)) { strncpy(path, n, q - n + 1); bitchsay("path = %s", path); } count = 0; while (n && *n) { if ((q = strrchr(n, '/')) && *(q+1)) q++; else q = n; strmcat(buffer, q, BIG_BUFFER_SIZE); strmcat(buffer, space, BIG_BUFFER_SIZE); if (++count == 4) { put_it("%s", convert_output_format(fget_string_var(FORMAT_COMPLETE_FSET),"%s", buffer)); count = 0; *buffer = 0; } completes[c++] = n; n = new_next_arg(use, &use); } if (count) put_it("%s", convert_output_format(fget_string_var(FORMAT_COMPLETE_FSET),"%s", buffer)); break; } case SERVER_COMPLETION: case TABKEY_COMPLETION: case CHAN_COMPLETION: { char *n, *use = get; n = new_next_arg(use, &use); count = 0; while (n && *n) { strmcat(buffer, n, BIG_BUFFER_SIZE); strmcat(buffer, space, BIG_BUFFER_SIZE); if (++count == 4) { put_it("%s", convert_output_format(fget_string_var(FORMAT_COMPLETE_FSET),"%s", buffer)); count = 0; *buffer = 0; } completes[c++] = n; n = new_next_arg(use, &use); } if (count) put_it("%s", convert_output_format(fget_string_var(FORMAT_COMPLETE_FSET),"%s", buffer)); break; } case NO_COMPLETION: break; default: put_it("get = %s[%d]", get, count); } if (old && c) set_input_best(type, inp, old, matches, completes); new_free((char **)&completes); } update_input(UPDATE_ALL); } else if (type == TABKEY_COMPLETION) { if (get_current_channel_by_refnum(0)) { type = CHAN_COMPLETION; goto do_more_tab; } } new_free(&get); new_free(&old_pos); return; } int BX_add_completion_type(char *name, int len, enum completion type) { Ext_Name_Type *new; if (!find_in_list((List **)&ext_completion, name, 0)) { new = (Ext_Name_Type *)new_malloc(sizeof(Ext_Name_Type)); new->name = m_strdup(name); new->len = len; new->type = type; add_to_list((List **)&ext_completion, (List *)new); return 1; } return 0; } #endif /* * type: The TYPE command. This parses the given string and treats each * character as though it were typed in by the user. Thus key bindings * are used for each character parsed. Special case characters are control * character sequences, specified by a ^ follow by a legal control key. * Thus doing "/TYPE ^B" will be as tho ^B were hit at the keyboard, * probably moving the cursor backward one character. * * This was moved from keys.c, because it certainly does not belong there, * and this seemed a reasonable place for it to go for now. */ BUILT_IN_COMMAND(type) { int c; char key; while (*args) { if (*args == '^') { switch (*(++args)) { case '?': { key = '\177'; args++; break; } default: { c = *(args++); if (islower(c)) c = toupper(c); if (c < 64) { say("Invalid key sequence: ^%c", c); return; } key = c - 64; break; } } } else if (*args == '\\') { key = *++args; args++; } else key = *(args++); edit_char(key); } }