sh.edit.c
来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 3,065 行 · 第 1/4 页
C
3,065 行
#ifdef CSHEDIT#ifndef lintstatic char *sccsid = "@(#)sh.edit.c 4.2 (ULTRIX) 12/20/90";#endif lint/************************************************************************ * * * Copyright (c) 1986 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used and * * copied only in accordance with the terms of such license and * * with the inclusion of the above copyright notice. This * * software or any other copies thereof may not be provided or * * otherwise made available to any other person. No title to and * * ownership of the software is hereby transferred. * * * * This software is derived from software received from the * * University of California, Berkeley, and from Bell * * Laboratories. Use, duplication, or disclosure is subject to * * restrictions under license agreements with University of * * California and with AT&T. * * * * The information in this software is subject to change without * * notice and should not be construed as a commitment by Digital * * Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability * * of its software on equipment which is not supplied by Digital. * * * ************************************************************************//* * Modification History: sh.edit.c * * Edit routines to edit a command line buffer. The lower level routines * are adapted from ex/vi code. The Top level command processors for both * vi style and emacs style editing are original. * * 009 - Bob Fontaine - Mon Oct 22 10:51:40 EDT 1990 * Prevent csh from hanging when cntrl-\ is typed in emacs editing mode. * Thanks to Akira Tanaka (JRD). Fixes QAR 5522. * * 008 - Bob Fontaine - Mon Oct 22 10:51:40 EDT 1990 * Fix expansion of wild card characters after command line edit mode * has been entered. Thanks to Akri Tanaka (JRD). Fixes qar 5264. * * 007 - Gary A. Gaudet - Tue Feb 27 10:17:01 EST 1990 * Fixed <esc>" infinite loop problem * * 006 - Gary A. Gaudet - Thu Dec 28 17:49:45 EST 1989 * Removed unused variables: oglobp, holdcurs. arg, ret, i, col, * cnt, escape, holdcurs, x, d, colp, l, c * Modified return values. * Removed unused label: egetcount, bakchar, again * Added test for ioctl() return value. * * 005 - 26-Sep-88 afd * Added ^P/^N to scroll up/down thru the history list. * * 004 - 22-Dec-87 afd * Made the edit code "8 bit clean". We never set nor clear the 8th bit. * * 003 - 23-Jul-87 afd * Added history search capability: to vmain() and emain() * command parsers and added srchback() routine to get search string. * Made redraw into a function which is called in several places. * Added "insearch" flag to keep track of when we are in emacs * "search mode" to repeat history searches. * * 002 - 15-Jul-87 afd * Fixed repeat count bug on 'r', "insert", "append" and "put" commands by * NOT using "printf" in vappend(). printf is the shell's version * and it prints to a buffer which is flushed later. * Set DEL and INS buffers to 0 in init_globals before editing a cmd line. * Set lastcmd buffer to 0 in init_globals before editing a cmd line. * * 001 - 28-Nov-86 afd * Created this file for command line edit capability in csh. */#include "sh.edit.h"#define SRCHLEN 78 /* also in sh.editglue.c */int vdebug = 0;#define printd if (vdebug) printfextern short SHIN, SHOUT, SHDIAG; /* csh I/O file descriptors */extern char srchcmd[]; /* cmd to search for in history list *//* * The routines outchar and putchar and are actually variables, * and these variables point at the current definitions of the routines. * Outchar and Putchar will be set to vputchar, vinschar, etc. */int (*Outchar)() = vputchar;int (*Putchar)() = normchar;/* * Main edit procedure. * Transfers control to the main command processing loop, vmain(). */editmain(){ register char *cp; /* * Set up terminal environment. * The tty will be used as a "dumb" terminal if TERM is not * defined in environment or if its type is not in termcap file. */ gettmode(); if ((cp = getenv("TERM")) != 0 && *cp) { setterm(cp); } /* * Do global initializations, call tty setup routine * to get tty into raw mode, call main command parser to edit the line, * then restore the tty. */ if (COLUMNS <= 0) COLUMNS = TUBECOLS; /* couldn't get col size so use 80 */ if (AM) COLUMNS--; vi_tty_setup(1); init_globals(); if (emacs) emain(); else vmain(); CP(cmdline, linebuf); vi_tty_setup(0);}/* * This is the main routine for vi interface command decoding. * We here decode the count preceding a command * and interpret a few of the commands. * Commands which involve a target (i.e. an operator) are decoded * in the routine exoperate. */#define forbid(a) { if (a) goto fonfon; }vmain(){ register int c, cnt; /* * The line is in the line buffer linebuf, * and the cursor at the position cursor. */ done_edit = 0; for (;;) { /* * Decode a command. * Clear state for decoding of next command. */ vglobp = 0; wcursor = 0; Xhadcnt = hadcnt = 0; Xcnt = cnt = 1; /* * Gobble up counts. */ for (;;) {looptop: if (isdigit(peekkey()) && peekkey() != '0') { hadcnt = 1; cnt = vgetcnt(); forbid (cnt <= 0); } break; }reread: /* * Come to reread from below after some macro expansions. * The call to map allows use of function key pads * by performing a terminal dependent mapping of inputs. */ op = getkey(); maphopcnt = 0; do { /* * Keep mapping the char as long as it changes. * This allows for double mappings, e.g., q to #, * #1 to something else. */ c = op; op = map(c,arrows); /* * Maybe the mapped to char is a count. If so, we have * to go back to the "for" to interpret it. */ if (isdigit(c) && c!='0') { /* 007 - GAG */ ungetkey(c); goto looptop; } if (++maphopcnt > 256) error("Infinite macro loop"); } while (c != op); /* * Begin to build an image of this cmd in the buffer workcmd. */ lastcp = workcmd; if (!vglobp) *lastcp++ = c; /* * First level command decode. */ switch (c) { /* * ^C Quit: with no command line. */ case CTRL(c): linebuf[0] = '\0'; destcol = 0; vclreol(); return; /* * search Edit some prior history event * Search for a "word" delineated by white space. */ case '/': exputchar('\/'); exflush(); if (srchback()) { /* * Got an input string from user * If pattern found in history list * edit new line. * else (pattern not found) restore state. */ if (reedit(1)) goto newedit; else { beep(); editevent++; redraw(); continue; } } else continue; /* user aborted command */ /* * n repeat last history search command (/). */ case 'n': if (srchcmd[0] != '\0') { /* * Have a search string * If pattern found in history list * edit new line. * else (pattern not found) restore state. */ editevent--; if (reedit(1)) goto newedit; else { beep(); editevent++; /* redraw(); */ continue; } } else { beep(); continue; /* no search string */ } /* * uparrow Edit prior history event */ case 'k': case CTRL(p): editevent--; if (reedit(0)) goto newedit; else { /* ran off top of history, so keep old line */ editevent++; beep(); continue; } /* * downarrow Edit next history event */ case 'j': case CTRL(n): editevent++; if (reedit(0) <= 0) { /* ran off botm of history, so keep old line */ editevent--; beep(); continue; }newedit: /* * Reset edit state for new command line */ vsetcurs(linebuf); init_globals(); continue; /* * ^L,^R Redraw the line. */ case CTRL(l): case CTRL(r): redraw(); continue; /* * . Repeat the last (modifying) command. */ case '.': /* * Check that there was a last command, and * take its count. */ forbid (lastcmd[0] == 0); if (hadcnt) lastcnt = cnt; cnt = lastcnt; hadcnt = lasthad; vglobp = lastcmd; goto reread; /* * $ Escape just cancels the current command * with a little feedback. */ case ESCAPE: beep(); continue; /* * C Change text to end of line, short for c$. */ case 'C': if (*cursor) { ungetkey('$'), c = 'c'; break; } goto appnd; /* * ~ Switch case of letter under cursor */ case '~': /* * Save command for repeat cmd. * Save old line state for undo. */ setLAST(); CP(oldbuf, linebuf); oldcursor = cursor; while (cnt > 0) { cnt--; if (isalpha(*cursor)) *cursor ^= ' '; /* toggle the case */ exputchar(*cursor); cursor++; if (*cursor == '\0') { cursor--; vsetcurs(cursor); break; } } continue; /* * A Append at end of line, short for $a. */ case 'A': exoperate('$', 1);appnd: c = 'a'; /* fall into ... */ /* * a Appends text after cursor. Text can continue * through arbitrary number of lines. */ case 'a': if (*cursor) { cursor++; } goto insrt; /* * I Insert at beginning of whitespace of line, * short for ^i. */ case 'I': exoperate('0', 1); c = 'i'; /* fall into ... */ /* * i Insert text to an escape in the buffer. * Text is arbitrary. */ case 'i':insrt: /* * Common code for all the insertion commands. * Save command for repeat cmd. Save old line state * for undo. Position cursor and do append. * Note that nothing is doomed. * If NL or CR entered in insert mode (vgetline()), * we end insert AND end entire edit with "done_edit" * flag. So flush output buffer and end edit. (flusho * is normally called in "getbr()" before getting a char) */ setLAST(); CP(oldbuf, linebuf); oldcursor = cursor; vgoto(column(cursor), 0); doomed = 0; vappend(c, cnt, 0); if (done_edit) { flusho(); return; /* 006 - GAG */ } continue; /* * ZZ, NL, CR end edit so cmd can be executed. */ case 'Z': forbid(getkey() != 'Z'); case NL: case CR: globp = "x"; return; /* 006 - GAG */ /* * P Put back text before cursor. * * p Like P but after rather than before cursor. */ case 'P': case 'p': /* * Save command for repeat cmd. Save old line state * for undo. * Use an append or insert to put it back so as to * use insert mode. */ setLAST(); CP(oldbuf, linebuf); oldcursor = cursor; if (DEL[0]) { forbid (DEL[0] == NULL); vglobp = DEL; ungetkey(c == 'p' ? 'a' : 'i'); goto reread; } beep(); continue; /* * u undo last change (but won't undo an undo) */ case 'u': CP(linebuf, oldbuf); /* * Redraw the line */ vsetcurs(linebuf); normline(); /* * Clear from last char to end of line */ destcol = column(strend(linebuf)) + 1; vclreol(); /* * Reset cursor */ cursor = oldcursor; if (*cursor == '\0') cursor--; vsetcurs(cursor); continue; /* * U undo: restore current line to initial state. */ case 'U': CP(linebuf, cmdline); /* * Redraw the line */ vsetcurs(linebuf); normline(); /* * Clear from last char to end of line */ destcol = column(strend(linebuf)) + 1; vclreol(); /* * Reset cursor to begining of line */ vsetcurs(linebuf); continue;fonfon: beep(); vmacp = 0; continue; } /* end switch (c) */ /* * Rest of commands are decoded by the exoperate * routine. * * If "done_edit" got set in vgetline (during change) * then flush output buffer and end edit. (flusho is * normally called in "getbr()" before getting a char) */ exoperate(c, cnt); if (done_edit) { flusho(); break; } } return;}#define blank() isspace(wcursor[0])#undef forbid#define forbid(a) if (a) goto errlab;/* * Decode an operator/operand type command. * The work here is setting up a function variable to point * to the routine we want, and manipulation of the variable * wcursor, which marks the other end of the affected * area. */exoperate(c, cnt) register int c, cnt;{ register int i; int (*moveop)(), (*deleteop)(); register int (*opf)(); bool subop = 0; static char lastFKND, lastFCHR; moveop = vmove, deleteop = vdelete; wcursor = cursor; dir = 1; switch (c) { /* * d delete operator. */ case 'd': CP(oldbuf, linebuf); oldcursor = cursor; moveop = vdelete; deleteop = beep; break; /* * c Change operator. */ case 'c': CP(oldbuf, linebuf); oldcursor = cursor; if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S') subop++; moveop = vchange; deleteop = beep; break; /* * r Replace character under cursor with single following * character. */ case 'r': CP(oldbuf, linebuf); oldcursor = cursor; vrep(cnt); return; default: goto nocount; } /* * Had an operator, so accept another count. * Multiply counts together. */ if (isdigit(peekkey()) && peekkey() != '0') { cnt *= vgetcnt(); Xcnt = cnt; forbid (cnt <= 0); } /* * Get next character, mapping it and saving it. */ c = map(getesc(),arrows); if (c == 0) return; if (!subop) *lastcp++ = c;nocount: opf = moveop; switch (c) { /* * b Back up a word. * B Back up a word, liberal definition. */ case 'b': case 'B': dir = -1; /* fall into ... */ /* * w Forward a word. * W Forward a word, liberal definition. */ case 'W': case 'w': wdkind = c & ' '; forbid(lfind(2, cnt, opf) < 0); break; /* * E to end of following blank/nonblank word */ case 'E': wdkind = 0; goto ein; /* * e To end of following word. */ case 'e': wdkind = 1;ein: forbid(lfind(3, cnt - 1, opf) < 0); break; /* * % To matching (), [], or {}. If not at one scan for * first such after cursor on this line. */ case '%': i = lmatchp(); forbid(!i); if (opf != vmove) if (dir > 0) wcursor++; else cursor++; break; /* * 0 To beginning of real line. */ case '0': wcursor = linebuf; break; /* * F Find single character before cursor in current line. * T Like F, but stops before character. */ case 'F': /* inverted find */ case 'T': dir = -1; /* fall into ... */ /* * f Find single character following cursor in current line. * t Like f, but stop before character. */ case 'f': /* find */ case 't': if (!subop) { i = getesc(); if (i == 0) return; *lastcp++ = i; } if (vglobp == 0) lastFKND = c, lastFCHR = i; for (; cnt > 0; cnt--) forbid (find(i) == 0); switch (c) { case 'T': wcursor++; break; case 't': wcursor--; case 'f':fixup: if (moveop != vmove) wcursor++; break; } break; /* * $ To end of line. */ case '$': if (cnt > 1) cnt = 1; if (linebuf[0]) { wcursor = strend(linebuf) - 1; goto fixup; } wcursor = linebuf; break; /* * h Back a character. * ^H Back a character. */ case 'h': case CTRL(h): dir = -1; /* fall into ... */ /* * space Forward a character. */ case 'l': case ' ': forbid (margin() || opf == vmove && edge()); while (cnt > 0 && !margin()) wcursor += dir, cnt--; if (margin() && opf == vmove || wcursor < linebuf) wcursor -= dir; break; /* * D Delete to end of line, short for d$. */ case 'D': cnt = INF; goto deleteit; /* * X Delete character before cursor. */ case 'X': dir = -1; /* fall into ... */deleteit: /* * x Delete character at cursor, leaving cursor where it is. */ case 'x': CP(oldbuf, linebuf); oldcursor = cursor; if (margin()) goto errlab; while (cnt > 0 && !margin()) wcursor += dir, cnt--; opf = deleteop; break; default: if (opf == vmove || c != workcmd[0]) {errlab: beep(); return; } } /* * Apply. */ (*opf)(c);}/* * This is the main routine for emacs interface command decoding. * We here decode the count preceding a command and interpret the commands. */#define eforbid(a) { if (a) goto efonfon; }emain(){ register int c, cnt; register int i; register int (*opf)();
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?