/* backspace in command buffer. */ static cmd_erase() { /* * backspace past beginning of the string: this usually means * abort the command. */ if (cp == cmdbuf) return(1); /* erase an extra character, for the carat. */ if (CONTROL_CHAR(*--cp)) { backspace(); --cmd_col; } backspace(); --cmd_col; return(0); }
/* * process a single character of a multi-character command, such as * a number, or the pattern of a search command. */ int cmd_char(int c) { if (c == erase_char) return(cmd_erase()); /* in this order, in case werase == erase_char */ if (c == werase_char) { if (cp > cmdbuf) { while (isspace(cp[-1]) && !cmd_erase()); while (!isspace(cp[-1]) && !cmd_erase()); while (isspace(cp[-1]) && !cmd_erase()); } return(cp == cmdbuf); } if (c == kill_char) { while (!cmd_erase()); return(1); } /* * No room in the command buffer, or no room on the screen; * {{ Could get fancy here; maybe shift the displayed line * and make room for more chars, like ksh. }} */ if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3) putchar('\7'); else { *cp++ = c; if (CONTROL_CHAR(c)) { putchar('^'); cmd_col++; c = CARAT_CHAR(c); } putchar(c); cmd_col++; } return(0); }
int pappend(int c) { if (c == '\0') { /* * Terminate any special modes, if necessary. * Append a '\0' to the end of the line. */ switch (ln_state) { case LN_UL_X: curr[0] = curr[-1]; curr[-1] = UE_CHAR; curr++; break; case LN_BO_X: curr[0] = curr[-1]; curr[-1] = BE_CHAR; curr++; break; case LN_UL_XB: case LN_UNDERLINE: *curr++ = UE_CHAR; break; case LN_BO_XB: case LN_BOLDFACE: *curr++ = BE_CHAR; break; } ln_state = LN_NORMAL; *curr = '\0'; return(0); } if (curr > linebuf + sizeof(linebuf) - 12) /* * Almost out of room in the line buffer. * Don't take any chances. * {{ Linebuf is supposed to be big enough that this * will never happen, but may need to be made * bigger for wide screens or lots of backspaces. }} */ return(1); if (!bs_mode) { /* * Advance the state machine. */ switch (ln_state) { case LN_NORMAL: if (curr <= linebuf + 1 || curr[-1] != (char)('H' | 0200)) break; column -= 2; if (c == curr[-2]) goto enter_boldface; if (c == '_' || curr[-2] == '_') goto enter_underline; curr -= 2; break; enter_boldface: /* * We have "X\bX" (including the current char). * Switch into boldface mode. */ column--; if (column + bo_width + be_width + 1 >= sc_width) /* * Not enough room left on the screen to * enter and exit boldface mode. */ return (1); if (bo_width > 0 && curr > linebuf + 2 && curr[-3] == ' ') { /* * Special case for magic cookie terminals: * if the previous char was a space, replace * it with the "enter boldface" sequence. */ curr[-3] = BO_CHAR; column += bo_width-1; } else { curr[-1] = curr[-2]; curr[-2] = BO_CHAR; column += bo_width; curr++; } goto ln_bo_xb_case; enter_underline: /* * We have either "_\bX" or "X\b_" (including * the current char). Switch into underline mode. */ column--; if (column + ul_width + ue_width + 1 >= sc_width) /* * Not enough room left on the screen to * enter and exit underline mode. */ return (1); if (ul_width > 0 && curr > linebuf + 2 && curr[-3] == ' ') { /* * Special case for magic cookie terminals: * if the previous char was a space, replace * it with the "enter underline" sequence. */ curr[-3] = UL_CHAR; column += ul_width-1; } else { curr[-1] = curr[-2]; curr[-2] = UL_CHAR; column += ul_width; curr++; } goto ln_ul_xb_case; /*NOTREACHED*/ case LN_UL_XB: /* * Termination of a sequence "_\bX" or "X\b_". */ if (c != '_' && curr[-2] != '_' && c == curr[-2]) { /* * We seem to have run on from underlining * into boldfacing - this is a nasty fix, but * until this whole routine is rewritten as a * real DFA, ... well ... */ curr[0] = curr[-2]; curr[-2] = UE_CHAR; curr[-1] = BO_CHAR; curr += 2; /* char & non-existent backspace */ ln_state = LN_BO_XB; goto ln_bo_xb_case; } ln_ul_xb_case: if (c == '_') c = curr[-2]; curr -= 2; ln_state = LN_UNDERLINE; break; case LN_BO_XB: /* * Termination of a sequnce "X\bX". */ if (c != curr[-2] && (c == '_' || curr[-2] == '_')) { /* * We seem to have run on from * boldfacing into underlining. */ curr[0] = curr[-2]; curr[-2] = BE_CHAR; curr[-1] = UL_CHAR; curr += 2; /* char & non-existent backspace */ ln_state = LN_UL_XB; goto ln_ul_xb_case; } ln_bo_xb_case: curr -= 2; ln_state = LN_BOLDFACE; break; case LN_UNDERLINE: if (column + ue_width + bo_width + 1 + be_width >= sc_width) /* * We have just barely enough room to * exit underline mode and handle a possible * underline/boldface run on mixup. */ return (1); ln_state = LN_UL_X; break; case LN_BOLDFACE: if (c == '\b') { ln_state = LN_BO_XB; break; } if (column + be_width + ul_width + 1 + ue_width >= sc_width) /* * We have just barely enough room to * exit underline mode and handle a possible * underline/boldface run on mixup. */ return (1); ln_state = LN_BO_X; break; case LN_UL_X: if (c == '\b') ln_state = LN_UL_XB; else { /* * Exit underline mode. * We have to shuffle the chars a bit * to make this work. */ curr[0] = curr[-1]; curr[-1] = UE_CHAR; column += ue_width; if (ue_width > 0 && curr[0] == ' ') /* * Another special case for magic * cookie terminals: if the next * char is a space, replace it * with the "exit underline" sequence. */ column--; else curr++; ln_state = LN_NORMAL; } break; case LN_BO_X: if (c == '\b') ln_state = LN_BO_XB; else { /* * Exit boldface mode. * We have to shuffle the chars a bit * to make this work. */ curr[0] = curr[-1]; curr[-1] = BE_CHAR; column += be_width; if (be_width > 0 && curr[0] == ' ') /* * Another special case for magic * cookie terminals: if the next * char is a space, replace it * with the "exit boldface" sequence. */ column--; else curr++; ln_state = LN_NORMAL; } break; } } if (c == '\t') { /* * Expand a tab into spaces. */ do { NEW_COLUMN(1); } while ((column % tabstop) != 0); *curr++ = '\t'; return (0); } if (c == '\b') { if (ln_state == LN_NORMAL) NEW_COLUMN(2); else column--; *curr++ = ('H' | 0200); return(0); } if (CONTROL_CHAR(c)) { /* * Put a "^X" into the buffer. The 0200 bit is used to tell * put_line() to prefix the char with a ^. We don't actually * put the ^ in the buffer because we sometimes need to move * chars around, and such movement might separate the ^ from * its following character. */ NEW_COLUMN(2); *curr++ = (CARAT_CHAR(c) | 0200); return(0); } /* * Ordinary character. Just put it in the buffer. */ NEW_COLUMN(1); *curr++ = c; return (0); }
/* * Main command processor. * Accept and execute commands until a quit command, then return. */ void commands(void) { int c, action; last_mca = 0; nscroll = (sc_height + 1) / 2; for (;;) { mca = 0; number = 0; /* * See if any signals need processing. */ if (sigs) { psignals(); if (quitting) quit(); } /* * Display prompt and accept a character. */ CMD_RESET; if (!prompt()) { next_file(1); continue; } noprefix(); c = getcc(); again: if (sigs) continue; /* * If we are in a multicharacter command, call mca_char. * Otherwise we call cmd_decode to determine the * action to be performed. */ if (mca) switch (mca_char(c)) { case MCA_MORE: /* * Need another character. */ c = getcc(); goto again; case MCA_DONE: /* * Command has been handled by mca_char. * Start clean with a prompt. */ continue; case NO_MCA: /* * Not a multi-char command * (at least, not anymore). */ break; } /* decode the command character and decide what to do. */ switch (action = cmd_decode(c)) { case A_DIGIT: /* first digit of a number */ start_mca(A_DIGIT, ":"); goto again; case A_F_SCREEN: /* forward one screen */ CMD_EXEC; if (number <= 0 && (number = sc_window) <= 0) number = sc_height - 1; forward(number, 1); break; case A_B_SCREEN: /* backward one screen */ CMD_EXEC; if (number <= 0 && (number = sc_window) <= 0) number = sc_height - 1; backward(number, 1); break; case A_F_LINE: /* forward N (default 1) line */ CMD_EXEC; forward(number <= 0 ? 1 : number, 0); break; case A_B_LINE: /* backward N (default 1) line */ CMD_EXEC; backward(number <= 0 ? 1 : number, 0); break; case A_F_SCROLL: /* forward N lines */ CMD_EXEC; if (number > 0) nscroll = number; forward(nscroll, 0); break; case A_B_SCROLL: /* backward N lines */ CMD_EXEC; if (number > 0) nscroll = number; backward(nscroll, 0); break; case A_FREPAINT: /* flush buffers and repaint */ if (!ispipe) { ch_init(0, 0); clr_linenum(); } /* FALLTHROUGH */ case A_REPAINT: /* repaint the screen */ CMD_EXEC; repaint(); break; case A_GOLINE: /* go to line N, default 1 */ CMD_EXEC; if (number <= 0) number = 1; jump_back(number); break; case A_PERCENT: /* go to percent of file */ CMD_EXEC; if (number < 0) number = 0; else if (number > 100) number = 100; jump_percent(number); break; case A_GOEND: /* go to line N, default end */ CMD_EXEC; if (number <= 0) jump_forw(); else jump_back(number); break; case A_STAT: /* print file name, etc. */ longprompt = 1; continue; case A_QUIT: /* exit */ quit(); case A_F_SEARCH: /* search for a pattern */ case A_B_SEARCH: if (number <= 0) number = 1; start_mca(action, (action==A_F_SEARCH) ? "/" : "?"); last_mca = mca; wsearch = 1; c = getcc(); if (c == '!') { /* * Invert the sense of the search; set wsearch * to 0 and get a new character for the start * of the pattern. */ start_mca(action, (action == A_F_SEARCH) ? "!/" : "!?"); wsearch = 0; c = getcc(); } goto again; case A_AGAIN_SEARCH: /* repeat previous search */ if (number <= 0) number = 1; if (wsearch) start_mca(last_mca, (last_mca == A_F_SEARCH) ? "/" : "?"); else start_mca(last_mca, (last_mca == A_F_SEARCH) ? "!/" : "!?"); CMD_EXEC; (void)search(mca == A_F_SEARCH, (char *)NULL, number, wsearch); break; case A_HELP: /* help */ lower_left(); clear_eol(); fputs("help", stdout); CMD_EXEC; help(); break; case A_TAGFILE: /* tag a new file */ CMD_RESET; start_mca(A_TAGFILE, "Tag: "); c = getcc(); goto again; case A_FILE_LIST: /* show list of file names */ CMD_EXEC; showlist(); repaint(); break; case A_EXAMINE: /* edit a new file */ CMD_RESET; start_mca(A_EXAMINE, "Examine: "); c = getcc(); goto again; case A_VISUAL: /* invoke the editor */ if (ispipe) { error("Cannot edit standard input"); break; } CMD_EXEC; editfile(); ch_init(0, 0); clr_linenum(); break; case A_NEXT_FILE: /* examine next file */ if (number <= 0) number = 1; next_file(number); break; case A_PREV_FILE: /* examine previous file */ if (number <= 0) number = 1; prev_file(number); break; case A_SETMARK: /* set a mark */ lower_left(); clear_eol(); start_mca(A_SETMARK, "mark: "); c = getcc(); if (c == erase_char || c == kill_char) break; setmark(c); break; case A_GOMARK: /* go to mark */ lower_left(); clear_eol(); start_mca(A_GOMARK, "goto mark: "); c = getcc(); if (c == erase_char || c == kill_char) break; gomark(c); break; case A_PREFIX: /* * The command is incomplete (more chars are needed). * Display the current char so the user knows what's * going on and get another character. */ if (mca != A_PREFIX) start_mca(A_PREFIX, ""); if (CONTROL_CHAR(c)) { putchar('^'); c = CARAT_CHAR(c); } putchar(c); c = getcc(); goto again; default: putchar('\7'); break; } } }