home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / xvi / part03 < prev    next >
Encoding:
Text File  |  1992-10-22  |  55.2 KB  |  2,176 lines

  1. Newsgroups: comp.sources.misc
  2. From: jmd@cyclone.bt.co.uk (John Downey)
  3. Subject:  v33i012:  xvi - portable multi-window vi-like editor, Part03/18
  4. Message-ID: <1992Oct23.181052.29857@sparky.imd.sterling.com>
  5. X-Md4-Signature: e0b027660312cdc8c470e7d18f7d83ff
  6. Date: Fri, 23 Oct 1992 18:10:52 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: jmd@cyclone.bt.co.uk (John Downey)
  10. Posting-number: Volume 33, Issue 12
  11. Archive-name: xvi/part03
  12. Environment: Unix, MS-DOS, OS/2, QNX
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then feed it
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # Contents:  xvi/doc/help xvi/src/normal.c
  19. # Wrapped by kent@sparky on Thu Oct 22 09:03:41 1992
  20. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  21. echo If this archive is complete, you will see the following message:
  22. echo '          "shar: End of archive 3 (of 18)."'
  23. if test -f 'xvi/doc/help' -a "${1}" != "-c" ; then 
  24.   echo shar: Will not clobber existing file \"'xvi/doc/help'\"
  25. else
  26.   echo shar: Extracting \"'xvi/doc/help'\" \(10258 characters\)
  27.   sed "s/^X//" >'xvi/doc/help' <<'END_OF_FILE'
  28. XIf you don't know how to use vi (or xvi) at all, you should not
  29. Xread through this help text; it is intended simply as a reminder
  30. Xfor those who already know how to use the editor.
  31. X
  32. XTo get out of xvi, type the following:
  33. X        :q!
  34. Xand then press RETURN.
  35. X
  36. XThe rest of this file contains a summary of xvi commands.
  37. XIf you remember the commands ^D (control-D) and ^U (control-U)
  38. Xthen that will be enough to get you around this help file.
  39. X
  40. XNumeric Prefixes
  41. X================
  42. X
  43. XMost commands can be prefixed with a number, which will alter
  44. Xtheir action. For instance, to go to line 37 of a file, you can
  45. Xtype
  46. X
  47. X        37G
  48. X
  49. Xbut beware - the meaning of the prefix varies between commands.
  50. X
  51. XCursor positioning
  52. X==================
  53. X
  54. X        ^F      Move forward one screenful in file
  55. X        ^B      Move backward one screenful
  56. X        ^D      Scroll down half screen
  57. X        ^U      Scroll up half screen
  58. X        G       Goto line (defaults to end of file)
  59. X        /re     Next occurence of regular expression 're'
  60. X        ?re     Prior occurence of regular expression 're'
  61. X        n       Repeat last / or ?
  62. X        N       Reverse last / or ?
  63. X        %       Find matching (, ), {, }, [, or ]
  64. X        ]]      Start of next function
  65. X        [[      Start of previous function
  66. X        g       Go to next buffer window
  67. X
  68. XAdjusting the screen
  69. X====================
  70. X
  71. X        ^L      Redraw the screen
  72. X        ^E      Scroll window down 1 line
  73. X        ^Y      Scroll window up 1 line
  74. X
  75. X        z<CR>   Redraw, current line at top
  76. X        z-      ... at bottom
  77. X        z.      ... at centre
  78. X
  79. X        ^T      Shrink current window by 1 line
  80. X        ^W      Grow current window by 1 line
  81. X        ^O      Grow current window to maximum size
  82. X
  83. XCharacter Positioning
  84. X=====================
  85. X
  86. X        ^       First non-white on current line
  87. X        0       Beginning of line
  88. X        $       End of line
  89. X        h       Left one character
  90. X        l       Right one character
  91. X        ^H      Same as 'h'
  92. X        space   Same as 'l'
  93. X        fx      Find 'x' forward in current line
  94. X        Fx      Find 'x' backward in current line
  95. X        tx      Go up to 'x' forward in current line
  96. X        Tx      Go up to 'x' backward in current line
  97. X        ;       Repeat last f, F, t, or T
  98. X        ,       Repeat last f, F, t, or T, changing direction
  99. X        |       Go to specified column in current line
  100. X
  101. XLine Positioning
  102. X================
  103. X
  104. X        H       Go to home window line
  105. X        L       Go to last window line
  106. X        M       Go to middle window line
  107. X        +       Go to next line, at first non-white
  108. X        -       Go to previous line, at first non-white
  109. X        CR      Same as +
  110. X        j       Go down, staying in same column if possible
  111. X        k       Go up, staying in same column if possible
  112. X
  113. XWords, sentences, paragraphs
  114. X============================
  115. X
  116. X        w       Go to start of next word
  117. X        b       Go to start of previous word
  118. X        e       Go to end of current word
  119. X        W       As w, but using blanks to delimit words
  120. X        B       As b, but using blanks to delimit words
  121. X        E       As e, but using blanks to delimit words
  122. X        )       Go to start of next sentence (not yet implemented)
  123. X        (       Go to start of previous sentence (not yet implemented)
  124. X        }       Go to start of next paragraph
  125. X        {       Go to start of previous paragraph
  126. X
  127. XMarking and Returning
  128. X=====================
  129. X
  130. X        ``      Go to previous context position
  131. X        ''      Go to previous context line
  132. X        mx      Mark current position with letter 'x'
  133. X        `x      Go to mark 'x'
  134. X        'x      Go to mark 'x' at first non-white in line
  135. X        :ka     Mark current line with letter 'a'
  136. X        :7ka    Mark line 7 with letter 'a'
  137. X
  138. XInsert and Replace
  139. X==================
  140. X
  141. X        a       Append after cursor
  142. X        i       Insert before cursor
  143. X        A       Append at end of current line
  144. X        I       Insert before first non-blank on current line
  145. X        o       Open line below current line
  146. X        O       Open line above current line
  147. X        rx      Replace single character with 'x'
  148. X        R       Enter replace (overwrite) mode
  149. X
  150. XOperators (double to affect lines)
  151. X==================================
  152. X
  153. X        d       Delete
  154. X        c       Change
  155. X        y       Yank to buffer (to be put back with 'p' or 'P')
  156. X        <       Left shift (by no of spaces specified by "shiftwidth" parameter)
  157. X        >       Right shift (ditto)
  158. X                !               Pipe line range through specified system command
  159. X
  160. XMiscellaneous operations
  161. X========================
  162. X
  163. X        C       Change rest of line
  164. X        D       Delete rest of line
  165. X        s       Substitute characters
  166. X        S       As cc (unimplemented)
  167. X        J       Join next line on to end of this line
  168. X        x       Delete character
  169. X        X       Delete character before cursor
  170. X
  171. XYank and Put
  172. X============
  173. X
  174. X    y    yank some text (see "operators")
  175. X        Y       Yank current line
  176. X        :y      Yank current line
  177. X        :17y    Yank line 17
  178. X        :3,5y   Yank lines from 3 to 5 inclusive
  179. X
  180. X        p       Put back text last yanked or deleted (see "operators")
  181. X        P       As p, but before current position
  182. X        :put    Put back text (same as 'p')
  183. X        :5put   Put back text after line 5
  184. X        :$put   Put back text at end of buffer
  185. X        :0put   Put back text at start of buffer
  186. X
  187. X    "x    Prefix before any of 'yYpP', uses named buffer x to store text
  188. X        Buffer name may be any lower-case letter, '@' (the default),
  189. X        or ':' which normally stores the last colon command executed.
  190. X    "xp    Inserts contents of named buffer x after current position
  191. X
  192. X    @x    Take contents of named yank buffer as command input
  193. X    @@    Take last yanked or deleted text as input
  194. X    @:    Redo last colon command
  195. X
  196. XUndo & Redo
  197. X===========
  198. X
  199. X        u       Undo last change
  200. X        U       Restore current line (not yet implemented)
  201. X        .       Repeat last change
  202. X
  203. XGlobal Editing
  204. X==============
  205. X
  206. X        :s/RE/SUB/      Change 1st RE in current line to SUB
  207. X        :s/RE/SUB/g     Change all RE's in current line to SUB
  208. X        :g/RE/p         Print all lines containing RE
  209. X        :g/RE/d         Delete all lines containing RE
  210. X        :g/RE/s/...     Perform 's' command on all lines containing RE
  211. X    :&        Redo last substitution
  212. X    :~        Substitute last SUB for last RE used    
  213. X
  214. X    Any of the above may be preceded by a line range, e.g. 12,15.
  215. X
  216. X    &        Redo last substitution (same as :&)
  217. X
  218. X    :x,yyank    Yank lines from x through y
  219. X    :x,ydelete    Delete lines from x through y
  220. X    :x,ycopyz    Copy lines from x through y to after line z
  221. X    :x,ymovez    Move lines from x through y to after line z
  222. X
  223. XFile and buffer manipulation
  224. X============================
  225. X
  226. X        ZZ      Same as :x
  227. X
  228. X        :w      Write buffer to file
  229. X        :w name Write buffer to file "name"
  230. X        :wq     Write buffer to file and then quit
  231. X        :x      Close window, if last window onto buffer then:
  232. X                        Write buffer if modified, then close buffer
  233. X        :cl     Close current window, closing buffer if only window onto it
  234. X        :cl!    Close current window, discarding any changes
  235. X        :q      Quit
  236. X        :q!     Quit, discarding any changes
  237. X        :b      Create new buffer and window
  238. X        :b name Create new buffer and window, editing file "name"
  239. X        :split  Create a new screen window onto the current buffer
  240. X        :e name Edit file "name" in current buffer
  241. X        :e!     Reedit current file, discarding any changes
  242. X        :e #    Edit file which was last closed
  243. X    ^^    (Control-uparrow) Same as :e #
  244. X        :n      Edit next file in arglist
  245. X        :n args Specify new arglist
  246. X        :wn     Write current file and edit next
  247. X        :args   Show list of files being edited
  248. X        :rew    Rewind arglist to start
  249. X        :f      Show current file and lines
  250. X        ^G      Same as :f
  251. X        :f name Change current file name to "name"
  252. X        :ta tag Find tag entry "tag" and go to it
  253. X        ^]      :ta, current word is tag
  254. X        :cd dir Change current directory
  255. X        :so file
  256. X                Read commands from file
  257. X
  258. XDisplaying and Setting Parameters
  259. X=================================
  260. X
  261. X        :set            Display parameters which have been set
  262. X        :set all        Display values of all parameters
  263. X        :set name       Set the boolean parameter "name"
  264. X        :set noname     Unset the boolean parameter "name"
  265. X        :set name=value Set the string or numeric parameter "name" to "value"
  266. X
  267. XKey Mapping
  268. X===========
  269. X
  270. X        :map lhs rhs
  271. X                Causes input of "lhs" to produce input "rhs" instead.
  272. X        :map! lhs rhs
  273. X                Like map, but works in insert mode instead.
  274. X        :map    Display all current maps.
  275. X        :set timeout=<num>
  276. X                Re-set the number of milliseconds that the editor waits
  277. X                for another character to be entered to complete a map.
  278. X                Default value is 200, which is okay for fast typists.
  279. X        :set remap
  280. X                When "remap" is set, the result of a map is re-processed
  281. X                through the map system. This option is extremely dangerous,
  282. X                as it can cause uncontrolled recursion.
  283. X
  284. XCommand line options
  285. X====================
  286. X
  287. X        -s param=value  Set param to value before starting
  288. X        -t tag          Edit file containing tag
  289. X        +num            Start up editing file at line num
  290. X        +/pat           Start up editing file at line containing pat
  291. X
  292. XUsing the mouse (not implemented on all systems)
  293. X================================================
  294. X
  295. X        Click mouse button on:
  296. X
  297. X        any line outside current window
  298. X
  299. X                Change current window to the one indicated by the mouse
  300. X                        (can be used instead of 'g')
  301. X
  302. X        top line of current window
  303. X
  304. X                Scroll window downwards
  305. X                        (same as '^Y')
  306. X
  307. X        bottom line of current window
  308. X
  309. X                Scroll window upwards
  310. X                        (same as '^E')
  311. X
  312. X        any other line of current window
  313. X        (or either line of a 2-line window)
  314. X
  315. X                Grow window by 1 line
  316. X                        (same as '^W')
  317. X
  318. X        status line of current window
  319. X
  320. X                Show current file and lines
  321. X                        (same as '^G')
  322. END_OF_FILE
  323.   if test 10258 -ne `wc -c <'xvi/doc/help'`; then
  324.     echo shar: \"'xvi/doc/help'\" unpacked with wrong size!
  325.   fi
  326.   # end of 'xvi/doc/help'
  327. fi
  328. if test -f 'xvi/src/normal.c' -a "${1}" != "-c" ; then 
  329.   echo shar: Will not clobber existing file \"'xvi/src/normal.c'\"
  330. else
  331.   echo shar: Extracting \"'xvi/src/normal.c'\" \(41946 characters\)
  332.   sed "s/^X//" >'xvi/src/normal.c' <<'END_OF_FILE'
  333. X/* Copyright (c) 1990,1991,1992 Chris and John Downey */
  334. X#ifndef lint
  335. Xstatic char *sccsid = "@(#)normal.c    2.7 (Chris & John Downey) 8/24/92";
  336. X#endif
  337. X
  338. X/***
  339. X
  340. X* program name:
  341. X    xvi
  342. X* function:
  343. X    PD version of UNIX "vi" editor, with extensions.
  344. X* module name:
  345. X    normal.c
  346. X* module function:
  347. X    Main routine for processing characters in command mode
  348. X    as well as routines for handling the operators.
  349. X* history:
  350. X    STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  351. X    Originally by Tim Thompson (twitch!tjt)
  352. X    Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  353. X    Heavily modified by Chris & John Downey
  354. X
  355. X***/
  356. X
  357. X#include "xvi.h"
  358. X
  359. Xstatic    bool_t    do_target P((int, int));
  360. Xstatic    bool_t    do_cmd P((int, int));
  361. Xstatic    bool_t    do_badcmd P((int, int));
  362. Xstatic    bool_t    do_page P((int, int));
  363. Xstatic    bool_t    do_scroll P((int, int));
  364. Xstatic    bool_t    do_word P((int, int));
  365. Xstatic    bool_t    do_csearch P((int, int));
  366. Xstatic    bool_t    do_z P((int, int));
  367. Xstatic    bool_t    do_x P((int, int));
  368. Xstatic    bool_t    do_HLM P((int, int));
  369. Xstatic    bool_t    do_rchar P((int, int));
  370. Xstatic    bool_t    do_ins P((int, int));
  371. Xstatic    void    op_shift P((int, int, int, long, Posn *, Posn *));
  372. Xstatic    void    op_delete P((int, int, long, Posn *, Posn *));
  373. Xstatic    void    op_change P((int, int, long, Posn *, Posn *));
  374. Xstatic    void    op_yank P((Posn *, Posn *));
  375. Xstatic    bool_t    dojoin P((void));
  376. X
  377. X/*
  378. X * Command type table. This is used for certain operations which are
  379. X * the same for "classes" of commands, e.g. for disallowing their use
  380. X * as targets of operators.
  381. X *
  382. X * Entries in this table having value 0 are unimplemented commands.
  383. X *
  384. X * If TARGET is set, the command may be used as the target for one of
  385. X * the operators (e.g. 'c'); the default is that targets are character-
  386. X * based unless TGT_LINE is set in which case they are line-based.
  387. X * Similarly, the default is that targets are exclusive, unless the
  388. X * TGT_INCLUSIVE flag is set.
  389. X *
  390. X * Q: WHAT DO WE DO ABOUT RETURN and LINEFEED???
  391. X * A: The ascii_map() macro (see ascii.h) handles this for QNX (I think).
  392. X */
  393. X#define        COMMAND        0x1        /* is implemented */
  394. X#define        TARGET        0x2        /* can serve as a target */
  395. X#define        TGT_LINE    0x4        /* a line-based target */
  396. X#define        TGT_CHAR    0        /* a char-based target */
  397. X#define        TGT_INCLUSIVE    0x8        /* an inclusive target */
  398. X#define        TGT_EXCLUSIVE    0        /* an exclusive target */
  399. X#define        TWO_CHAR    0x10        /* a two-character command */
  400. X
  401. Xstatic    struct {
  402. X    bool_t        (*c_func) P((int, int));
  403. X    unsigned char    c_flags;
  404. X} cmd_types[256] = {
  405. X /* ^@ */    do_badcmd,    0,
  406. X /* ^A */    do_badcmd,    0,
  407. X /* ^B */    do_page,    COMMAND,
  408. X /* ^C */    do_badcmd,    0,
  409. X /* ^D */    do_scroll,    COMMAND,
  410. X /* ^E */    do_scroll,    COMMAND,
  411. X /* ^F */    do_page,    COMMAND,
  412. X /* ^G */    do_cmd,        COMMAND,
  413. X /* ^H */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  414. X /* ^I */    do_badcmd,    0,
  415. X /* ^J */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  416. X /* ^K */    do_badcmd,    0,
  417. X /* ^L */    do_cmd,        COMMAND,
  418. X /* ^M */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  419. X /* ^N */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  420. X /* ^O */    do_cmd,        COMMAND,
  421. X /* ^P */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  422. X /* ^Q */    do_badcmd,    0,
  423. X /* ^R */    do_cmd,        COMMAND,
  424. X /* ^S */    do_badcmd,    0,
  425. X /* ^T */    do_cmd,        COMMAND,
  426. X /* ^U */    do_scroll,    COMMAND,
  427. X /* ^V */    do_badcmd,    0,
  428. X /* ^W */    do_cmd,        COMMAND,
  429. X /* ^X */    do_badcmd,    0,
  430. X /* ^Y */    do_scroll,    COMMAND,
  431. X /* ^Z */    do_cmd,        COMMAND,
  432. X /* ESCAPE */    do_cmd,        COMMAND,
  433. X /* ^\ */    do_badcmd,    0,
  434. X /* ^] */    do_cmd,        COMMAND,
  435. X /* ^^ */    do_cmd,        COMMAND,
  436. X /* ^_ */    do_rchar,    COMMAND,
  437. X /* space */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  438. X /* ! */    do_cmd,        COMMAND,
  439. X /* " */    do_cmd,        COMMAND | TWO_CHAR,
  440. X /* # */    do_badcmd,    0,
  441. X /* $ */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  442. X /* % */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  443. X /* & */    do_cmd,        COMMAND,
  444. X /* ' */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
  445. X /* ( */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  446. X /* ) */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  447. X /* * */    do_badcmd,    0,
  448. X /* + */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  449. X /* , */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  450. X /* - */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  451. X /* . */    do_cmd,        COMMAND,
  452. X /* / */    do_cmd,        COMMAND,
  453. X /* 0 */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  454. X /* 1 */    do_badcmd,    0,
  455. X /* 2 */    do_badcmd,    0,
  456. X /* 3 */    do_badcmd,    0,
  457. X /* 4 */    do_badcmd,    0,
  458. X /* 5 */    do_badcmd,    0,
  459. X /* 6 */    do_badcmd,    0,
  460. X /* 7 */    do_badcmd,    0,
  461. X /* 8 */    do_badcmd,    0,
  462. X /* 9 */    do_badcmd,    0,
  463. X /* : */    do_cmd,        COMMAND,
  464. X /* ; */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  465. X /* < */    do_cmd,        COMMAND,
  466. X /* = */    do_badcmd,    0,
  467. X /* > */    do_cmd,        COMMAND,
  468. X /* ? */    do_cmd,        COMMAND,
  469. X /* @ */    do_cmd,        COMMAND | TWO_CHAR,
  470. X /* A */    do_ins,        COMMAND,
  471. X /* B */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  472. X /* C */    do_cmd,        COMMAND,
  473. X /* D */    do_cmd,        COMMAND,
  474. X /* E */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  475. X /* F */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
  476. X /* G */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  477. X /* H */    do_HLM,        COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  478. X /* I */    do_ins,        COMMAND,
  479. X /* J */    do_cmd,        COMMAND,
  480. X /* K */    do_badcmd,    0,
  481. X /* L */    do_HLM,        COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  482. X /* M */    do_HLM,        COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  483. X /* N */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  484. X /* O */    do_ins,        COMMAND,
  485. X /* P */    do_cmd,        COMMAND,
  486. X /* Q */    do_badcmd,    0,
  487. X /* R */    do_cmd,        COMMAND,
  488. X /* S */    do_cmd,        COMMAND,
  489. X /* T */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
  490. X /* U */    do_badcmd,    0,
  491. X /* V */    do_badcmd,    0,
  492. X /* W */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  493. X /* X */    do_x,        COMMAND,
  494. X /* Y */    do_cmd,        COMMAND,
  495. X /* Z */    do_cmd,        COMMAND | TWO_CHAR,
  496. X /* [ */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
  497. X /* \ */    do_badcmd,    0,
  498. X /* ] */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE | TWO_CHAR,
  499. X /* ^ */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  500. X /* _ */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  501. X /* ` */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE | TWO_CHAR,
  502. X /* a */    do_ins,        COMMAND,
  503. X /* b */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  504. X /* c */    do_cmd,        COMMAND,
  505. X /* d */    do_cmd,        COMMAND,
  506. X /* e */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE,
  507. X /* f */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE | TWO_CHAR,
  508. X /* g */    do_cmd,        COMMAND,
  509. X /* h */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  510. X /* i */    do_ins,        COMMAND,
  511. X /* j */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  512. X /* k */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  513. X /* l */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  514. X /* m */    do_cmd,        COMMAND | TWO_CHAR,
  515. X /* n */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  516. X /* o */    do_ins,        COMMAND,
  517. X /* p */    do_cmd,        COMMAND,
  518. X /* q */    do_badcmd,    0,
  519. X /* r */    do_cmd,        COMMAND,
  520. X /* s */    do_cmd,        COMMAND,
  521. X /* t */    do_csearch,    COMMAND | TARGET | TGT_CHAR | TGT_INCLUSIVE | TWO_CHAR,
  522. X /* u */    do_cmd,        COMMAND,
  523. X /* v */    do_badcmd,    0,
  524. X /* w */    do_word,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  525. X /* x */    do_x,        COMMAND,
  526. X /* y */    do_cmd,        COMMAND,
  527. X /* z */    do_z,        COMMAND | TWO_CHAR,
  528. X /* { */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  529. X /* | */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  530. X /* } */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  531. X /* ~ */    do_rchar,    COMMAND,
  532. X /* DEL */    do_badcmd,    0,
  533. X /* K_HELP */    do_cmd,        COMMAND,
  534. X /* K_UNDO */    do_cmd,        COMMAND,
  535. X /* K_INSERT */    do_ins,        COMMAND,
  536. X /* K_HOME */    do_HLM,        COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  537. X /* K_UARROW */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  538. X /* K_DARROW */    do_target,    COMMAND | TARGET | TGT_LINE | TGT_EXCLUSIVE,
  539. X /* K_LARROW */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  540. X /* K_RARROW */    do_target,    COMMAND | TARGET | TGT_CHAR | TGT_EXCLUSIVE,
  541. X /* K_CGRAVE */    do_cmd,        COMMAND,
  542. X};
  543. X
  544. X#define    NOP        '\0'        /* no pending operation */
  545. X
  546. Xstatic    int    operator = NOP;        /* current pending operator */
  547. X
  548. X/*
  549. X * When a cursor motion command is made, it is marked as being a character
  550. X * or line oriented motion. Then, if an operator is in effect, the operation
  551. X * becomes character or line oriented accordingly.
  552. X *
  553. X * Character motions are marked as being inclusive or not. Most char.
  554. X * motions are inclusive, but some (e.g. 'w') are not.
  555. X */
  556. X
  557. Xstatic    enum {
  558. X    m_bad,        /* 'bad' motion type marks unusable yank buf */
  559. X    m_nonincl,        /* non-inclusive character motion */
  560. X    m_incl,        /* inclusive character motion */
  561. X    m_line        /* line-based motion */
  562. X} mtype;        /* type of the current cursor motion */
  563. X
  564. X/*
  565. X * Cursor position at start of operator.
  566. X */
  567. Xstatic    Posn    startop;
  568. X
  569. X/*
  570. X * Operators can have counts either before the operator, or between the
  571. X * operator and the following cursor motion as in:
  572. X *
  573. X *    d3w or 3dw
  574. X *
  575. X * The number is initially stored in Prenum as it is processed.
  576. X *
  577. X * If a count is given before an operator, it is saved in opnum when the
  578. X * initial recognition of the operator takes place. If normal() is called
  579. X * with a pending operator, the count in opnum (if present) overrides
  580. X * any count that comes later.
  581. X */
  582. Xstatic    long    Prenum;
  583. Xstatic    long    opnum;
  584. X
  585. X/*
  586. X * This variable contains the name of the yank/put buffer we are
  587. X * currently using. It is set by the " command. The default value
  588. X * is always '@'; other values are a-z and possibly ':'.
  589. X */
  590. Xstatic    int    cur_yp_name = '@';
  591. X
  592. X/*
  593. X * This state variable is TRUE if we got a preceding buffer
  594. X * name: if set, the buffer_name variable contains the letter
  595. X * which was given as the buffer name.
  596. X */
  597. Xstatic    bool_t    got_name = FALSE;
  598. Xstatic    char    buffer_name;
  599. X
  600. X/*
  601. X * Return value of Prenum unless it is 0, in which case return the
  602. X * default value of 1.
  603. X */
  604. X#define    LDEF1PRENUM    ((Prenum == 0 ? 1L : Prenum))
  605. X
  606. X/*
  607. X * Return value of Prenum as an int, unless it is 0, in which case
  608. X * return the default value of 1.
  609. X *
  610. X * Note that this assumes that Prenum will never be negative.
  611. X */
  612. X#define    IDEF1PRENUM    (Prenum == 0 ? \
  613. X             1 : \
  614. X             sizeof (int) == sizeof (long) || \
  615. X               Prenum <= INT_MAX ? \
  616. X             (int) Prenum : \
  617. X             INT_MAX)
  618. X
  619. X/*
  620. X * Redo buffer.
  621. X */
  622. Xstruct redo {
  623. X    enum {
  624. X        r_insert,
  625. X    r_replace1,
  626. X    r_normal
  627. X    }        r_mode;
  628. X    Flexbuf    r_fb;
  629. X} Redo;
  630. X
  631. X/*
  632. X * Execute a command in "normal" (i.e. command) mode.
  633. X */
  634. Xbool_t
  635. Xnormal(c)
  636. Xregister int    c;
  637. X{
  638. X    /*
  639. X     * This variable is used to recall whether we got
  640. X     * an operator last time, to decide whether we
  641. X     * should apply it this time.
  642. X     */
  643. X    register bool_t    finish_op;
  644. X
  645. X    /*
  646. X     * TRUE if we are awaiting a second character to finish the
  647. X     * current command - this is for two-character commands like
  648. X     * ZZ, and for commands taking a single character argument.
  649. X     * The "first_char" and "second_char" variables are for the
  650. X     * first character (stored between calls), and the second one.
  651. X     */
  652. X    static bool_t    two_char = FALSE;
  653. X    static int        first_char;
  654. X    int            second_char;
  655. X    unsigned char    cflags;
  656. X    bool_t        (*cfunc) P((int, int)) = NULL;
  657. X
  658. X
  659. X    /*
  660. X     * If the character is a digit, and it is not a leading '0',
  661. X     * compute Prenum instead of doing a command.  Leading zeroes
  662. X     * are treated specially because '0' is a valid command.
  663. X     *
  664. X     * If two_char is set, don't treat digits this way; they are
  665. X     * passed in as the second character. This is because none of
  666. X     * the two-character commands are allowed to take prenums in
  667. X     * the middle; you want a prenum, you have to type it before
  668. X     * the command. So "t3" works as you might expect it to.
  669. X     */
  670. X    if (!two_char && is_digit(c) && (c != '0' || Prenum > 0)) {
  671. X    Prenum = Prenum * 10 + (c - '0');
  672. X    return(FALSE);
  673. X    }
  674. X
  675. X    /*
  676. X     * If there is an operator pending, then the command we take
  677. X     * this time will terminate it.  Finish_op tells us to finish
  678. X     * the operation before returning this time (unless it was
  679. X     * cancelled).
  680. X     */
  681. X    finish_op = (operator != NOP);
  682. X
  683. X    /*
  684. X     * If we're in the middle of an operator AND we had a count before
  685. X     * the operator, then that count overrides the current value of
  686. X     * Prenum. What this means effectively, is that commands like
  687. X     * "3dw" get turned into "d3w" which makes things fall into place
  688. X     * pretty neatly.
  689. X     */
  690. X    if (finish_op || two_char) {
  691. X    if (opnum != 0) {
  692. X        Prenum = opnum;
  693. X    }
  694. X    } else {
  695. X    opnum = 0;
  696. X    }
  697. X
  698. X    /*
  699. X     * If we got given a buffer name last time around, it is only
  700. X     * good for one operation; so at the start of each new command
  701. X     * we set or clear the yankput module's idea of the buffer name.
  702. X     * We don't do this if finish_op is set because it is not the
  703. X     * start of a new command.
  704. X     */
  705. X    if (!finish_op) {
  706. X    cur_yp_name = got_name ? buffer_name : '@';
  707. X    got_name = FALSE;
  708. X    }
  709. X
  710. X    if (c > (sizeof(cmd_types) / sizeof(cmd_types[0]) - 1)) {
  711. X    operator = NOP;
  712. X    Prenum = 0;
  713. X        beep(curwin);
  714. X    return(FALSE);
  715. X    }
  716. X
  717. X    /*
  718. X     * If two_char is set, it means we got the first character of
  719. X     * a two-character command last time. So check the second char,
  720. X     * and set cflags appropriately.
  721. X     */
  722. X    if (two_char) {
  723. X    second_char = c;
  724. X    two_char = FALSE;
  725. X    cflags = cmd_types[ascii_map(first_char)].c_flags;
  726. X    cfunc = cmd_types[ascii_map(first_char)].c_func;
  727. X
  728. X    /*
  729. X     * This seems to be a universal rule - if a two-character
  730. X     * command has ESC as the second character, it means "abort".
  731. X     */
  732. X    if (second_char == ESC) {
  733. X        operator = NOP;
  734. X        finish_op = FALSE;
  735. X        Prenum = 0;
  736. X        return(FALSE);
  737. X    }
  738. X
  739. X    } else {
  740. X    /*
  741. X     * Received a command. Find out its characteristics ...
  742. X     */
  743. X    first_char = c;
  744. X    second_char = '\0';
  745. X    cflags = cmd_types[ascii_map(first_char)].c_flags;
  746. X    cfunc = cmd_types[ascii_map(first_char)].c_func;
  747. X
  748. X    /*
  749. X     * It's a two-character command. So wait until we get
  750. X     * the second character before proceeding.
  751. X     */
  752. X    if (cflags & TWO_CHAR) {
  753. X        two_char = TRUE;
  754. X        first_char = c;
  755. X        if (Prenum != 0) {
  756. X        opnum = Prenum;
  757. X        Prenum = 0;
  758. X        }
  759. X        return(FALSE);
  760. X    }
  761. X
  762. X    /*
  763. X     * If we got an operator last time, and the user
  764. X     * typed the same character again, we fake out the
  765. X     * default "apply to this line" rule by changing
  766. X     * the input character to a '_' which means "the
  767. X     * current line."
  768. X     */
  769. X    if (finish_op) {
  770. X        if (operator == c) {
  771. X        first_char = c = '_';
  772. X        cflags = cmd_types[ascii_map(c)].c_flags;
  773. X        cfunc = cmd_types[ascii_map(c)].c_func;
  774. X        } else if (!(cflags & TARGET)) {
  775. X        beep(curwin);
  776. X        operator = NOP;
  777. X        Prenum = 0;
  778. X        return(FALSE);
  779. X        }
  780. X    }
  781. X    }
  782. X
  783. X    /*
  784. X     * At this point, cfunc must be set - if not, the entry in the
  785. X     * command table is zero, so disallow the input character.
  786. X     */
  787. X    if (cfunc == NULL) {
  788. X    operator = NOP;
  789. X    Prenum = 0;
  790. X        beep(curwin);
  791. X    return(FALSE);
  792. X    }
  793. X
  794. X    if (cflags & TARGET) {
  795. X
  796. X    /*
  797. X     * A cursor movement command.
  798. X     */
  799. X
  800. X    if (cflags & TGT_LINE) {
  801. X        mtype = m_line;
  802. X    } else {
  803. X        if (cflags & TGT_INCLUSIVE) {
  804. X        mtype = m_incl;
  805. X        } else {
  806. X        mtype = m_nonincl;
  807. X        }
  808. X    }
  809. X
  810. X    if (!(*cfunc)(first_char, second_char)) {
  811. X        beep(curwin);
  812. X        operator = NOP;
  813. X        Prenum = 0;
  814. X        return(FALSE);
  815. X    }
  816. X
  817. X    /*
  818. X     * If an operation is pending, handle it...
  819. X     */
  820. X    if (finish_op) {
  821. X        Posn    top, bot;
  822. X
  823. X        top = startop;
  824. X        bot = *curwin->w_cursor;
  825. X
  826. X        /*
  827. X         * Put the cursor back to its starting position.
  828. X         */
  829. X        move_cursor(curwin, startop.p_line, startop.p_index);
  830. X
  831. X        if (lt(&bot, &top)) {
  832. X        pswap(&top, &bot);
  833. X        }
  834. X
  835. X        switch (operator) {
  836. X        case '<':
  837. X        case '>':
  838. X        op_shift(operator, first_char, second_char, Prenum, &top, &bot);
  839. X        break;
  840. X
  841. X        case 'd':
  842. X        op_delete(first_char, second_char, Prenum, &top, &bot);
  843. X        break;
  844. X
  845. X        case 'y':
  846. X        op_yank(&top, &bot);
  847. X        break;
  848. X
  849. X        case 'c':
  850. X        op_change(first_char, second_char, Prenum, &top, &bot);
  851. X        break;
  852. X
  853. X        case '!':
  854. X        specify_pipe_range(curwin, top.p_line, bot.p_line);
  855. X        cmd_init(curwin, '!');
  856. X        break;
  857. X
  858. X        default:
  859. X        beep(curwin);
  860. X        }
  861. X        operator = NOP;
  862. X    }
  863. X    } else {
  864. X    /*
  865. X     * A command that does something.
  866. X     * Since it isn't a target, no operators need apply.
  867. X     */
  868. X    if (finish_op) {
  869. X        beep(curwin);
  870. X        operator = NOP;
  871. X    }
  872. X
  873. X    (void) (*cfunc)(first_char, second_char);
  874. X    }
  875. X
  876. X    Prenum = 0;
  877. X    return(TRUE);
  878. X}
  879. X
  880. X/*
  881. X * Handle cursor movement commands.
  882. X * These are used simply to move the cursor somewhere,
  883. X * and also as targets for the various operators.
  884. X *
  885. X * If the return value is FALSE, the caller will complain
  886. X * loudly to the user and cancel any outstanding operator.
  887. X * The cursor should hopefully not have moved.
  888. X *
  889. X * Arguments are the first and second characters (where appropriate).
  890. X */
  891. Xstatic bool_t
  892. Xdo_target(c1, c2)
  893. Xint    c1, c2;
  894. X{
  895. X    bool_t        skip_spaces = FALSE;
  896. X    bool_t        retval = TRUE;
  897. X
  898. X    switch (c1) {
  899. X    case 'G':
  900. X    setpcmark(curwin);
  901. X    do_goto((Prenum > 0) ? Prenum : MAX_LINENO);
  902. X    skip_spaces = TRUE;
  903. X    break;
  904. X
  905. X    case 'l':
  906. X    case ' ':
  907. X    c1 = K_RARROW;
  908. X    /* fall through ... */
  909. X    case K_RARROW:
  910. X    case 'h':
  911. X    case K_LARROW:
  912. X    case CTRL('H'):
  913. X    {
  914. X    register bool_t (*mvfunc) P((Xviwin *, bool_t));
  915. X    register long    n;
  916. X    register long    i;
  917. X
  918. X    if (c1 == K_RARROW) {
  919. X        mvfunc = one_right;
  920. X    } else {
  921. X        mvfunc = one_left;
  922. X    }
  923. X
  924. X    n = LDEF1PRENUM;
  925. X    for (i = 0; i < n; i++) {
  926. X        if (!(*mvfunc)(curwin, FALSE)) {
  927. X        break;
  928. X        }
  929. X    }
  930. X    if (i == 0) {
  931. X        retval = FALSE;
  932. X    } else {
  933. X        curwin->w_set_want_col = TRUE;
  934. X    }
  935. X    break;
  936. X    }
  937. X
  938. X    case '-':
  939. X    skip_spaces = TRUE;
  940. X    /* FALL THROUGH */
  941. X    case 'k':
  942. X    case K_UARROW:
  943. X    case CTRL('P'):
  944. X    if (!oneup(curwin, LDEF1PRENUM)) {
  945. X        retval = FALSE;
  946. X    }
  947. X    break;
  948. X
  949. X    case '+':
  950. X    case '\r':
  951. X    skip_spaces = TRUE;
  952. X    /* FALL THROUGH */
  953. X    case '\n':
  954. X    case 'j':
  955. X    case K_DARROW:
  956. X    case CTRL('N'):
  957. X    if (!onedown(curwin, LDEF1PRENUM)) {
  958. X        retval = FALSE;
  959. X    }
  960. X    break;
  961. X
  962. X    /*
  963. X     * This is a strange motion command that helps make
  964. X     * operators more logical. It is actually implemented,
  965. X     * but not documented in the real 'vi'. This motion
  966. X     * command actually refers to "the current line".
  967. X     * Commands like "dd" and "yy" are really an alternate
  968. X     * form of "d_" and "y_". It does accept a count, so
  969. X     * "d3_" works to delete 3 lines.
  970. X     */
  971. X    case '_':
  972. X    (void) onedown(curwin, LDEF1PRENUM - 1);
  973. X    break;
  974. X
  975. X    case '|':
  976. X    begin_line(curwin, FALSE);
  977. X
  978. X    if (Prenum > 0) {
  979. X        coladvance(curwin, LONG2INT(Prenum - 1));
  980. X    }
  981. X    curwin->w_curswant = Prenum - 1;
  982. X    break;
  983. X
  984. X    case '%':
  985. X    {
  986. X        Posn    *pos = showmatch();
  987. X
  988. X        if (pos == NULL) {
  989. X        retval = FALSE;
  990. X        } else {
  991. X        setpcmark(curwin);
  992. X        move_cursor(curwin, pos->p_line, pos->p_index);
  993. X        curwin->w_set_want_col = TRUE;
  994. X        }
  995. X    }
  996. X    break;
  997. X
  998. X    case '$':
  999. X    while (one_right(curwin, FALSE))
  1000. X        ;
  1001. X    curwin->w_curswant = INT_MAX;
  1002. X        /* so we stay at the end ... */
  1003. X    curwin->w_set_want_col = FALSE;
  1004. X    break;
  1005. X
  1006. X    case '^':
  1007. X    case '0':
  1008. X    begin_line(curwin, c1 == '^');
  1009. X    break;
  1010. X
  1011. X    case 'n':
  1012. X    case 'N':
  1013. X    curwin->w_set_want_col = TRUE;
  1014. X    (void) dosearch(curwin, "", c1);
  1015. X    break;
  1016. X
  1017. X    case '(':
  1018. X    case ')':
  1019. X    case '{':
  1020. X    case '}':
  1021. X    case '[':
  1022. X    case ']':
  1023. X    {
  1024. X        int    dir = FORWARD;
  1025. X    char    *pattern;
  1026. X    Posn    *newpos;
  1027. X
  1028. X    switch (c1) {
  1029. X    case '(':
  1030. X        dir = BACKWARD;
  1031. X        /*FALLTHROUGH*/
  1032. X    case ')':
  1033. X        pattern = Ps(P_sentences);
  1034. X        break;
  1035. X
  1036. X    case '{':
  1037. X        dir = BACKWARD;
  1038. X        /*FALLTHROUGH*/
  1039. X    case '}':
  1040. X        pattern = Ps(P_paragraphs);
  1041. X        break;
  1042. X
  1043. X    case '[':
  1044. X        dir = BACKWARD;
  1045. X        /*FALLTHROUGH*/
  1046. X    case ']':
  1047. X        if (c1 != c2) {
  1048. X        retval = FALSE;
  1049. X        } else {
  1050. X        pattern = Ps(P_sections);
  1051. X        }
  1052. X    }
  1053. X    if (retval) {
  1054. X        curwin->w_set_want_col = TRUE;
  1055. X        newpos = find_pattern(pattern, dir, IDEF1PRENUM);
  1056. X        if (newpos != NULL) {
  1057. X        setpcmark(curwin);
  1058. X        move_cursor(curwin, newpos->p_line, newpos->p_index);
  1059. X        } else {
  1060. X        retval = FALSE;
  1061. X        }
  1062. X    }
  1063. X    break;
  1064. X    }
  1065. X
  1066. X    case '\'':
  1067. X    case '`':
  1068. X    {
  1069. X    Posn    *mark;
  1070. X
  1071. X    mark = getmark(c2, curbuf);
  1072. X    if (mark == NULL) {
  1073. X        retval = FALSE;
  1074. X    } else {
  1075. X        Posn    dest;
  1076. X
  1077. X        /*
  1078. X         * Record posn before re-setting the mark -
  1079. X         * so that we don't accidentally side-effect
  1080. X         * the place we are moving to! What a hack.
  1081. X         */
  1082. X        dest = *mark;
  1083. X
  1084. X        setpcmark(curwin);
  1085. X
  1086. X        move_cursor(curwin, dest.p_line, c1 == '`' ? dest.p_index : 0);
  1087. X        if (c1 == '`') {
  1088. X        mtype = m_nonincl;
  1089. X        } else {
  1090. X        skip_spaces = TRUE;
  1091. X        }
  1092. X    }
  1093. X    break;
  1094. X    }
  1095. X    }
  1096. X
  1097. X    if (retval && skip_spaces) {
  1098. X    begin_line(curwin, TRUE);
  1099. X    }
  1100. X
  1101. X    return(retval);
  1102. X}
  1103. X
  1104. Xstatic bool_t
  1105. Xdo_cmd(c1, c2)
  1106. Xint    c1, c2;
  1107. X{
  1108. X    switch (c1) {
  1109. X    case K_HELP:
  1110. X    do_help(curwin);
  1111. X    break;
  1112. X
  1113. X    case CTRL('R'):
  1114. X    case CTRL('L'):
  1115. X    redraw_screen();
  1116. X    break;
  1117. X
  1118. X    case CTRL('G'):
  1119. X    show_file_info(curwin);
  1120. X    break;
  1121. X
  1122. X    case CTRL(']'):        /* :ta to current identifier */
  1123. X    tagword();
  1124. X    break;
  1125. X
  1126. X    /*
  1127. X     * Some convenient abbreviations...
  1128. X     */
  1129. X    case 'D':
  1130. X    stuff("\"%cd$", cur_yp_name);
  1131. X    break;
  1132. X
  1133. X    case 'Y':
  1134. X    stuff("\"%c%dyy", cur_yp_name, IDEF1PRENUM);
  1135. X    break;
  1136. X
  1137. X    case 'C':
  1138. X    stuff("\"%cc$", cur_yp_name);
  1139. X    break;
  1140. X
  1141. X    case 'S':
  1142. X        stuff("\"%c%dcc", cur_yp_name, IDEF1PRENUM);
  1143. X    break;
  1144. X
  1145. X    /*
  1146. X     * Operators.
  1147. X     */
  1148. X    case 'd':
  1149. X    case 'c':
  1150. X    case 'y':
  1151. X    case '>':
  1152. X    case '<':
  1153. X    case '!':
  1154. X    if (Prenum != 0)
  1155. X        opnum = Prenum;
  1156. X    startop = *curwin->w_cursor;
  1157. X    operator = c1;
  1158. X    break;
  1159. X
  1160. X    case 'p':
  1161. X    case 'P':
  1162. X    Redo.r_mode = r_normal;
  1163. X    do_put(curwin, curwin->w_cursor, (c1 == 'p') ? FORWARD : BACKWARD,
  1164. X                            cur_yp_name);
  1165. X    if (is_digit(cur_yp_name) && cur_yp_name != '0' && cur_yp_name != '9') {
  1166. X        cur_yp_name++;
  1167. X    }
  1168. X    flexclear(&Redo.r_fb);
  1169. X    (void) lformat(&Redo.r_fb, "\"%c%d%c", cur_yp_name, IDEF1PRENUM, c1);
  1170. X    break;
  1171. X
  1172. X    case 's':        /* substitute characters */
  1173. X    start_command(curwin);
  1174. X    replchars(curwin, curwin->w_cursor->p_line,
  1175. X                curwin->w_cursor->p_index, IDEF1PRENUM, "");
  1176. X    updateline(curwin);
  1177. X    Redo.r_mode = r_insert;
  1178. X    flexclear(&Redo.r_fb);
  1179. X    (void) lformat(&Redo.r_fb, "%lds", IDEF1PRENUM);
  1180. X    startinsert(FALSE);
  1181. X    break;
  1182. X
  1183. X    case ':':
  1184. X    case '?':
  1185. X    case '/':
  1186. X    cmd_init(curwin, c1);
  1187. X    break;
  1188. X
  1189. X    case '&':
  1190. X    (void) do_ampersand(curwin, curwin->w_cursor->p_line,
  1191. X                    curwin->w_cursor->p_line, "");
  1192. X    begin_line(curwin, TRUE);
  1193. X    updateline(curwin);
  1194. X    break;
  1195. X
  1196. X    case 'R':
  1197. X    case 'r':
  1198. X    Redo.r_mode = (c1 == 'r') ? r_replace1 : r_insert;
  1199. X    flexclear(&Redo.r_fb);
  1200. X    flexaddch(&Redo.r_fb, c1);
  1201. X    startreplace(c1);
  1202. X    break;
  1203. X
  1204. X    case 'J':
  1205. X    if (!dojoin())
  1206. X        beep(curwin);
  1207. X
  1208. X    Redo.r_mode = r_normal;
  1209. X    flexclear(&Redo.r_fb);
  1210. X    flexaddch(&Redo.r_fb, c1);
  1211. X    update_buffer(curbuf);
  1212. X    break;
  1213. X
  1214. X    case K_CGRAVE:            /* shorthand command */
  1215. X#ifndef    QNX
  1216. X    /*
  1217. X     * We can't use this key on QNX.
  1218. X     */
  1219. X    case CTRL('^'):
  1220. X#endif
  1221. X    do_alt_edit(curwin);
  1222. X    break;
  1223. X
  1224. X    case 'u':
  1225. X    case K_UNDO:
  1226. X    undo(curwin);
  1227. X    break;
  1228. X
  1229. X    case CTRL('Z'):            /* suspend editor */
  1230. X    do_suspend(curwin);
  1231. X    break;
  1232. X
  1233. X    /*
  1234. X     * Buffer handling.
  1235. X     */
  1236. X    case CTRL('T'):            /* shrink window */
  1237. X    resize_window(curwin, - IDEF1PRENUM);
  1238. X    move_cursor_to_window(curwin);
  1239. X    break;
  1240. X
  1241. X    case CTRL('W'):            /* grow window */
  1242. X    resize_window(curwin, IDEF1PRENUM);
  1243. X    break;
  1244. X
  1245. X    case CTRL('O'):    /* make window as large as possible */
  1246. X    resize_window(curwin, INT_MAX);
  1247. X    break;
  1248. X
  1249. X    case 'g':
  1250. X    /*
  1251. X     * Find the next window that the cursor
  1252. X     * can be displayed in; i.e. at least one
  1253. X     * text row is displayed.
  1254. X     */
  1255. X    do {
  1256. X        curwin = next_window(curwin);
  1257. X    } while (curwin->w_nrows < 2);
  1258. X    curbuf = curwin->w_buffer;
  1259. X    move_cursor_to_window(curwin);
  1260. X    wind_goto(curwin);
  1261. X    break;
  1262. X
  1263. X    case '"':
  1264. X    got_name = TRUE;
  1265. X    buffer_name = c2;
  1266. X    break;
  1267. X
  1268. X    case '@':
  1269. X    yp_stuff_input(curwin, c2, TRUE);
  1270. X    break;
  1271. X
  1272. X    /*
  1273. X     * Marks
  1274. X     */
  1275. X    case 'm':
  1276. X    if (!setmark(c2, curbuf, curwin->w_cursor))
  1277. X        beep(curwin);
  1278. X    break;
  1279. X
  1280. X    case 'Z':        /* write, if changed, and exit */
  1281. X    if (c2 != 'Z') {
  1282. X        beep(curwin);
  1283. X        break;
  1284. X    }
  1285. X
  1286. X    /*
  1287. X     * Make like a ":x" command.
  1288. X     */
  1289. X    do_xit(curwin);
  1290. X    break;
  1291. X
  1292. X    case '.':
  1293. X    /*
  1294. X     * '.', meaning redo. As opposed to '.' as a target.
  1295. X     */
  1296. X    stuff("%s", flexgetstr(&Redo.r_fb));
  1297. X    if (Redo.r_mode != r_normal) {
  1298. X        yp_stuff_input(curwin, '<', TRUE);
  1299. X        if (Redo.r_mode == r_insert) {
  1300. X        stuff("%c", ESC);
  1301. X        }
  1302. X    }
  1303. X    break;
  1304. X
  1305. X    default:
  1306. X    beep(curwin);
  1307. X    break;
  1308. X    }
  1309. X
  1310. X    return(FALSE);
  1311. X}
  1312. X
  1313. Xstatic bool_t
  1314. Xdo_badcmd(c1, c2)
  1315. Xint    c1, c2;
  1316. X{
  1317. X    beep(curwin);
  1318. X    return(FALSE);
  1319. X}
  1320. X
  1321. X/*
  1322. X * Handle page motion (control-F or control-B).
  1323. X */
  1324. Xstatic bool_t
  1325. Xdo_page(c1, c2)
  1326. Xregister int    c1, c2;
  1327. X{
  1328. X    long        overlap;
  1329. X    long        n;
  1330. X
  1331. X    /*
  1332. X     * First move the cursor to the top of the screen
  1333. X     * (for ^B), or to the top of the next screen (for ^F).
  1334. X     */
  1335. X    move_cursor(curwin, (c1 == CTRL('B')) ?
  1336. X           curwin->w_topline : curwin->w_botline, 0);
  1337. X
  1338. X    /*
  1339. X     * Cursor could have moved to the lastline of the buffer,
  1340. X     * if the window is at the end of the buffer. Disallow
  1341. X     * the cursor from being outside the buffer's bounds.
  1342. X     */
  1343. X    if (curwin->w_cursor->p_line == curbuf->b_lastline) {
  1344. X    move_cursor(curwin, curbuf->b_lastline->l_prev, 0);
  1345. X    }
  1346. X
  1347. X    /*
  1348. X     * Decide on the amount of overlap to use.
  1349. X     */
  1350. X    if (curwin->w_nrows > 10) {
  1351. X    /*
  1352. X     * At least 10 text lines in window.
  1353. X     */
  1354. X    overlap = 2;
  1355. X    } else if (curwin->w_nrows > 3) {
  1356. X    /*
  1357. X     * Between 3 and 9 text lines in window.
  1358. X     */
  1359. X    overlap = 1;
  1360. X    } else {
  1361. X    /*
  1362. X     * 1 or 2 text lines in window.
  1363. X     */
  1364. X    overlap = 0;
  1365. X    }
  1366. X
  1367. X    /*
  1368. X     * Given the overlap, decide where to move the cursor;
  1369. X     * this will determine the new top line of the screen.
  1370. X     */
  1371. X    if (c1 == CTRL('F')) {
  1372. X    n = - overlap;
  1373. X    n += (LDEF1PRENUM - 1) * (curwin->w_nrows - overlap - 1);
  1374. X    } else {
  1375. X    n = (- LDEF1PRENUM) * (curwin->w_nrows - overlap - 1);
  1376. X    }
  1377. X
  1378. X    if (n > 0) {
  1379. X    (void) onedown(curwin, n);
  1380. X    } else {
  1381. X    (void) oneup(curwin, -n);
  1382. X    }
  1383. X
  1384. X    /*
  1385. X     * Redraw the screen with the cursor at the top.
  1386. X     */
  1387. X    begin_line(curwin, TRUE);
  1388. X    curwin->w_topline = curwin->w_cursor->p_line;
  1389. X    update_window(curwin);
  1390. X
  1391. X    if (c1 == CTRL('B')) {
  1392. X    /*
  1393. X     * And move it to the bottom.
  1394. X     */
  1395. X    move_window_to_cursor(curwin);
  1396. X    cursupdate(curwin);
  1397. X    move_cursor(curwin, curwin->w_botline->l_prev, 0);
  1398. X    begin_line(curwin, TRUE);
  1399. X    }
  1400. X
  1401. X    /*
  1402. X     * Finally, show where we are in the file.
  1403. X     */
  1404. X    show_file_info(curwin);
  1405. X
  1406. X    return(FALSE);
  1407. X}
  1408. X
  1409. Xstatic bool_t
  1410. Xdo_scroll(c1, c2)
  1411. Xint    c1, c2;
  1412. X{
  1413. X    switch (c1) {
  1414. X    case CTRL('D'):
  1415. X    scrollup(curwin, curwin->w_nrows / 2);
  1416. X    (void) onedown(curwin, (long) (curwin->w_nrows / 2));
  1417. X    break;
  1418. X
  1419. X    case CTRL('U'):
  1420. X    scrolldown(curwin, curwin->w_nrows / 2);
  1421. X    (void) oneup(curwin, (long) (curwin->w_nrows / 2));
  1422. X    break;
  1423. X
  1424. X    case CTRL('E'):
  1425. X    scrollup(curwin, (unsigned) IDEF1PRENUM);
  1426. X    break;
  1427. X
  1428. X    case CTRL('Y'):
  1429. X    scrolldown(curwin, (unsigned) IDEF1PRENUM);
  1430. X    break;
  1431. X    }
  1432. X
  1433. X    update_window(curwin);
  1434. X    move_cursor_to_window(curwin);
  1435. X    return(FALSE);
  1436. X}
  1437. X
  1438. X/*
  1439. X * Handle word motion ('w', 'W', 'b', 'B', 'e' or 'E').
  1440. X */
  1441. Xstatic bool_t
  1442. Xdo_word(c1, c2)
  1443. Xregister int    c1, c2;
  1444. X{
  1445. X    register Posn    *(*func) P((Posn *, int, bool_t));
  1446. X    register long    n;
  1447. X    register int    lc;
  1448. X    register int    type;
  1449. X    Posn        pos;
  1450. X
  1451. X    if (is_upper(c1)) {
  1452. X    type = 1;
  1453. X    lc = to_lower(c1);
  1454. X    } else {
  1455. X    type = 0;
  1456. X    lc = c1;
  1457. X    }
  1458. X    curwin->w_set_want_col = TRUE;
  1459. X
  1460. X    switch (lc) {
  1461. X    case 'b':
  1462. X    func = bck_word;
  1463. X    break;
  1464. X
  1465. X    case 'w':
  1466. X    func = fwd_word;
  1467. X    break;
  1468. X
  1469. X    case 'e':
  1470. X    func = end_word;
  1471. X    break;
  1472. X    }
  1473. X
  1474. X    pos = *curwin->w_cursor;
  1475. X
  1476. X    for (n = LDEF1PRENUM; n > 0; n--) {
  1477. X    Posn    *newpos;
  1478. X    bool_t    skip_whites;
  1479. X
  1480. X    /*
  1481. X     * "cw" is a special case; the whitespace after
  1482. X     * the end of the last word involved in the change
  1483. X     * does not get changed. The following code copes
  1484. X     * with this strangeness.
  1485. X     */
  1486. X    if (n == 1 && operator == 'c' && lc == 'w') {
  1487. X        skip_whites = FALSE;
  1488. X        mtype = m_incl;
  1489. X    } else {
  1490. X        skip_whites = TRUE;
  1491. X    }
  1492. X
  1493. X    newpos = (*func)(&pos, type, skip_whites);
  1494. X
  1495. X    if (newpos == NULL) {
  1496. X        return(FALSE);
  1497. X    }
  1498. X
  1499. X    if (n == 1 && lc == 'w' && operator != NOP &&
  1500. X                    newpos->p_line != pos.p_line) {
  1501. X        /*
  1502. X         * We are on the last word to be operated
  1503. X         * upon, and have crossed the line boundary.
  1504. X         * This should not happen, so back up to
  1505. X         * the end of the line the word is on.
  1506. X         */
  1507. X        while (dec(newpos) == mv_SAMELINE)
  1508. X        ;
  1509. X        mtype = m_incl;
  1510. X    }
  1511. X
  1512. X    if (skip_whites == FALSE) {
  1513. X        (void) dec(newpos);
  1514. X    }
  1515. X    pos = *newpos;
  1516. X    }
  1517. X    move_cursor(curwin, pos.p_line, pos.p_index);
  1518. X    return(TRUE);
  1519. X}
  1520. X
  1521. Xstatic bool_t
  1522. Xdo_csearch(c1, c2)
  1523. Xint    c1, c2;
  1524. X{
  1525. X    bool_t    retval = TRUE;
  1526. X    Posn    *pos;
  1527. X    int    dir;
  1528. X
  1529. X    switch (c1) {
  1530. X    case 'T':
  1531. X    case 't':
  1532. X    case 'F':
  1533. X    case 'f':
  1534. X    if (is_upper(c1)) {
  1535. X        dir = BACKWARD;
  1536. X        c1 = to_lower(c1);
  1537. X    } else {
  1538. X        dir = FORWARD;
  1539. X    }
  1540. X
  1541. X    curwin->w_set_want_col = TRUE;
  1542. X    pos = searchc(c2, dir, (c1 == 't'), IDEF1PRENUM);
  1543. X    break;
  1544. X
  1545. X    case ',':
  1546. X    case ';':
  1547. X    /*
  1548. X     * This should be FALSE for a backward motion.
  1549. X     * How do we know it's a backward motion?
  1550. X     *
  1551. X     * Fix it later.
  1552. X     */
  1553. X    mtype = m_incl;
  1554. X    curwin->w_set_want_col = TRUE;
  1555. X    pos = crepsearch(curbuf, c1 == ',', IDEF1PRENUM);
  1556. X    break;
  1557. X    }
  1558. X    if (pos == NULL) {
  1559. X    retval = FALSE;
  1560. X    } else {
  1561. X    move_cursor(curwin, pos->p_line, pos->p_index);
  1562. X    }
  1563. X    return(retval);
  1564. X}
  1565. X
  1566. X/*
  1567. X * Handle adjust window command ('z').
  1568. X */
  1569. Xstatic bool_t
  1570. Xdo_z(c1, c2)
  1571. Xint    c1, c2;
  1572. X{
  1573. X    Line    *lp;
  1574. X    int    l;
  1575. X    int    znum;
  1576. X
  1577. X    switch (c2) {
  1578. X    case '\n':                /* put cursor at top of screen */
  1579. X    case '\r':
  1580. X    znum = 1;
  1581. X    break;
  1582. X
  1583. X    case '.':                /* put cursor in middle of screen */
  1584. X    znum = curwin->w_nrows / 2;
  1585. X    break;
  1586. X
  1587. X    case '-':                /* put cursor at bottom of screen */
  1588. X    znum = curwin->w_nrows - 1;
  1589. X    break;
  1590. X
  1591. X    default:
  1592. X    return(FALSE);
  1593. X    }
  1594. X
  1595. X    if (Prenum > 0) {
  1596. X    do_goto(Prenum);
  1597. X    }
  1598. X    lp = curwin->w_cursor->p_line;
  1599. X    for (l = 0; l < znum && lp != curbuf->b_line0; ) {
  1600. X    l += plines(curwin, lp);
  1601. X    curwin->w_topline = lp;
  1602. X    lp = lp->l_prev;
  1603. X    }
  1604. X    cursupdate(curwin);
  1605. X    update_window(curwin);
  1606. X
  1607. X    return(TRUE);
  1608. X}
  1609. X
  1610. X/*
  1611. X * Handle character delete commands ('x' or 'X').
  1612. X */
  1613. Xstatic bool_t
  1614. Xdo_x(c1, c2)
  1615. Xint    c1, c2;
  1616. X{
  1617. X    Posn    *curp;
  1618. X    Posn    lastpos;
  1619. X    int        nchars;
  1620. X    int        i;
  1621. X
  1622. X    nchars = IDEF1PRENUM;
  1623. X    Redo.r_mode = r_normal;
  1624. X    flexclear(&Redo.r_fb);
  1625. X    (void) lformat(&Redo.r_fb, "%d%c", nchars, c1);
  1626. X    curp = curwin->w_cursor;
  1627. X
  1628. X    if (c1 == 'X') {
  1629. X    for (i = 0; i < nchars && one_left(curwin, FALSE); i++)
  1630. X        ;
  1631. X    nchars = i;
  1632. X    if (nchars == 0) {
  1633. X        beep(curwin);
  1634. X        return(TRUE);
  1635. X    }
  1636. X
  1637. X    } else /* c1 == 'x' */ {
  1638. X    char    *line;
  1639. X
  1640. X    /*
  1641. X     * Ensure that nchars is not too big.
  1642. X     */
  1643. X    line = curp->p_line->l_text + curp->p_index;
  1644. X    for (i = 0; i < nchars && line[i] != '\0'; i++)
  1645. X        ;
  1646. X    nchars = i;
  1647. X
  1648. X    if (curp->p_line->l_text[0] == '\0') {
  1649. X        /*
  1650. X         * Can't do it on a blank line.
  1651. X         */
  1652. X        beep(curwin);
  1653. X        return(TRUE);
  1654. X    }
  1655. X    }
  1656. X
  1657. X    lastpos.p_line = curp->p_line;
  1658. X    lastpos.p_index = curp->p_index + nchars - 1;
  1659. X    yp_push_deleted();
  1660. X    (void) do_yank(curbuf, curp, &lastpos, TRUE, cur_yp_name);
  1661. X    replchars(curwin, curp->p_line, curp->p_index, nchars, "");
  1662. X    if (curp->p_line->l_text[curp->p_index] == '\0') {
  1663. X    (void) one_left(curwin, FALSE);
  1664. X    }
  1665. X    updateline(curwin);
  1666. X    return(TRUE);
  1667. X}
  1668. X
  1669. X/*
  1670. X * Handle home ('H') end of page ('L') and middle line ('M') motion commands.
  1671. X */
  1672. Xstatic bool_t
  1673. Xdo_HLM(c1, c2)
  1674. Xint    c1, c2;
  1675. X{
  1676. X    register bool_t    (*mvfunc) P((Xviwin *, long));
  1677. X    register long    n;
  1678. X
  1679. X    if (c1 == K_HOME) {
  1680. X    c1 = 'H';
  1681. X    }
  1682. X
  1683. X    /*
  1684. X     * Silly to specify a number before 'H' or 'L'
  1685. X     * which would move us off the screen.
  1686. X     */
  1687. X    if (Prenum >= curwin->w_nrows) {
  1688. X    return(FALSE);
  1689. X    }
  1690. X    
  1691. X    move_cursor(curwin, (c1 == 'L') ? curwin->w_botline->l_prev :
  1692. X                        curwin->w_topline, 0);
  1693. X
  1694. X    switch (c1) {
  1695. X    case 'H':
  1696. X    mvfunc = onedown;
  1697. X    n = Prenum - 1;
  1698. X    break;
  1699. X    case 'L':
  1700. X    mvfunc = oneup;
  1701. X    n = Prenum - 1;
  1702. X    break;
  1703. X    case 'M':
  1704. X    mvfunc = onedown;
  1705. X    n = (long) (curwin->w_nrows - 1) / 2;
  1706. X    }
  1707. X
  1708. X    (void) (*mvfunc)(curwin, n);
  1709. X    begin_line(curwin, TRUE);
  1710. X    return(TRUE);
  1711. X}
  1712. X
  1713. X/*
  1714. X * Handle '~' and CTRL('_') commands.
  1715. X */
  1716. Xstatic bool_t
  1717. Xdo_rchar(c1, c2)
  1718. Xint    c1, c2;
  1719. X{
  1720. X    Posn    *cp;
  1721. X    char    *tp;
  1722. X    int        c;
  1723. X    char    newc[2];
  1724. X
  1725. X    Redo.r_mode = r_normal;
  1726. X    flexclear(&Redo.r_fb);
  1727. X    flexaddch(&Redo.r_fb, c1);
  1728. X    cp = curwin->w_cursor;
  1729. X    tp = cp->p_line->l_text;
  1730. X    if (tp[0] == '\0') {
  1731. X    /*
  1732. X     * Can't do it on a blank line.
  1733. X     */
  1734. X    beep(curwin);
  1735. X    return(FALSE);
  1736. X    }
  1737. X    c = tp[cp->p_index];
  1738. X
  1739. X    switch (c1) {
  1740. X    case '~':
  1741. X    newc[0] = is_alpha(c) ?
  1742. X            is_lower(c) ?
  1743. X                to_upper(c)
  1744. X            : to_lower(c)
  1745. X            : c;
  1746. X    break;
  1747. X    case CTRL('_'):            /* flip top bit */
  1748. X#ifdef    TOP_BIT
  1749. X    newc[0] = c ^ TOP_BIT;
  1750. X    if (newc[0] == '\0') {
  1751. X        newc[0] = c;
  1752. X    }
  1753. X#else    /* not TOP_BIT */
  1754. X    beep(curwin);
  1755. X    return(FALSE);
  1756. X#endif    /* TOP_BIT */
  1757. X    }
  1758. X
  1759. X    newc[1] = '\0';
  1760. X    replchars(curwin, cp->p_line, cp->p_index, 1, newc);
  1761. X    updateline(curwin);
  1762. X    (void) one_right(curwin, FALSE);
  1763. X    return(FALSE);
  1764. X}
  1765. X
  1766. X/*
  1767. X * Handle commands which just go into insert mode
  1768. X * ('i', 'a', 'I', 'A', 'o', 'O').
  1769. X */
  1770. Xstatic bool_t
  1771. Xdo_ins(c1, c2)
  1772. Xint    c1, c2;
  1773. X{
  1774. X    bool_t    startpos = TRUE;    /* FALSE means start position moved */
  1775. X
  1776. X    if (!start_command(curwin)) {
  1777. X    return(FALSE);
  1778. X    }
  1779. X
  1780. X    Redo.r_mode = r_insert;
  1781. X    flexclear(&Redo.r_fb);
  1782. X    flexaddch(&Redo.r_fb, c1);
  1783. X
  1784. X    switch (c1) {
  1785. X    case 'o':
  1786. X    case 'O':
  1787. X    if (((c1 == 'o') ? openfwd(FALSE) : openbwd()) == FALSE) {
  1788. X        beep(curwin);
  1789. X        end_command(curwin);
  1790. X        return(FALSE);
  1791. X    }
  1792. X    break;
  1793. X
  1794. X    case 'I':
  1795. X    begin_line(curwin, TRUE);
  1796. X    startpos = FALSE;
  1797. X    break;
  1798. X
  1799. X    case 'A':
  1800. X    while (one_right(curwin, TRUE))
  1801. X        ;
  1802. X    startpos = FALSE;
  1803. X    break;
  1804. X
  1805. X    case 'a':
  1806. X    /*
  1807. X     * 'a' works just like an 'i' on the next character.
  1808. X     */
  1809. X    (void) one_right(curwin, TRUE);
  1810. X    startpos = FALSE;
  1811. X    }
  1812. X
  1813. X    startinsert(startpos);
  1814. X    return(FALSE);
  1815. X}
  1816. X
  1817. X/*
  1818. X * Handle a shift operation. The prenum and operator/operands are
  1819. X * passed, along with the first and last positions to be shifted.
  1820. X */
  1821. Xstatic void
  1822. Xop_shift(op, c1, c2, num, top, bottom)
  1823. Xint    op;
  1824. Xint    c1, c2;
  1825. Xlong    num;
  1826. XPosn    *top;
  1827. XPosn    *bottom;
  1828. X{
  1829. X    /*
  1830. X     * Do the shift.
  1831. X     */
  1832. X    tabinout(op, top->p_line, bottom->p_line);
  1833. X
  1834. X    /*
  1835. X     * Put cursor on first non-white of line; this is good if the
  1836. X     * cursor is in the range of lines specified, which is normally
  1837. X     * will be.
  1838. X     */
  1839. X    begin_line(curwin, TRUE);
  1840. X    update_buffer(curbuf);
  1841. X
  1842. X    /*
  1843. X     * Construct redo buffer.
  1844. X     */
  1845. X    Redo.r_mode = r_normal;
  1846. X    flexclear(&Redo.r_fb);
  1847. X    if (num != 0) {
  1848. X    (void) lformat(&Redo.r_fb, "%c%ld%c%c", op, num, c1, c2);
  1849. X    } else {
  1850. X    (void) lformat(&Redo.r_fb, "%c%c%c", op, c1, c2);
  1851. X    }
  1852. X}
  1853. X
  1854. X/*
  1855. X * op_delete - handle a delete operation
  1856. X * The characters "c1" and "c2" are the target character and
  1857. X * its argument (if it takes one) respectively. E.g. 'f', '/'.
  1858. X * The "num" argument is the numeric prefix.
  1859. X */
  1860. Xstatic void
  1861. Xop_delete(c1, c2, num, top, bottom)
  1862. Xint    c1, c2;
  1863. Xlong    num;
  1864. XPosn    *top;
  1865. XPosn    *bottom;
  1866. X{
  1867. X    long    nlines;
  1868. X    int        n;
  1869. X
  1870. X    /*
  1871. X     * If the target is non-inclusive, move back a character.
  1872. X     * We assume it is okay to do this, and it seems to work,
  1873. X     * so no checking is performed at the moment.
  1874. X     */
  1875. X    if (mtype == m_nonincl) {
  1876. X    (void) dec(bottom);
  1877. X    }
  1878. X
  1879. X    nlines = cntllines(top->p_line, bottom->p_line);
  1880. X
  1881. X    /*
  1882. X     * Do a yank of whatever we're about to delete. If there's too much
  1883. X     * stuff to fit in the yank buffer, disallow the delete, since we
  1884. X     * probably wouldn't have enough memory to do it anyway.
  1885. X     */
  1886. X    yp_push_deleted();
  1887. X    if (!do_yank(curbuf, top, bottom, (mtype != m_line), cur_yp_name)) {
  1888. X    show_error(curwin, "Not enough memory to perform delete");
  1889. X    return;
  1890. X    }
  1891. X
  1892. X    if (mtype == m_line) {
  1893. X    /*
  1894. X     * Put the cursor at the start of the section to be deleted
  1895. X     * so that repllines will correctly update it and the screen
  1896. X     * pointer, and update the screen.
  1897. X     */
  1898. X    move_cursor(curwin, top->p_line, 0);
  1899. X    repllines(curwin, top->p_line, nlines, (Line *) NULL);
  1900. X    begin_line(curwin, TRUE);
  1901. X    } else {
  1902. X    /*
  1903. X     * After a char-based delete, the cursor should always be
  1904. X     * on the character following the last character of the
  1905. X     * section being deleted. The easiest way to achieve this
  1906. X     * is to put it on the character before the section to be
  1907. X     * deleted (which will not be affected), and then move one
  1908. X     * place right afterwards.
  1909. X     */
  1910. X    move_cursor(curwin, top->p_line,
  1911. X                top->p_index - ((top->p_index > 0) ? 1 : 0));
  1912. X
  1913. X    if (top->p_line == bottom->p_line) {
  1914. X        /*
  1915. X         * Delete characters within line.
  1916. X         */
  1917. X        n = (bottom->p_index - top->p_index) + 1;
  1918. X        replchars(curwin, top->p_line, top->p_index, n, "");
  1919. X    } else {
  1920. X        /*
  1921. X         * Character-based delete between lines.
  1922. X         * So we actually have to do three deletes;
  1923. X         * one to delete to the end of the top line,
  1924. X         * one to delete the intervening lines, and
  1925. X         * one to delete up to the target position.
  1926. X         */
  1927. X        if (!start_command(curwin)) {
  1928. X        return;
  1929. X        }
  1930. X
  1931. X        /*
  1932. X         * First delete part of the last line.
  1933. X         */
  1934. X        replchars(curwin, bottom->p_line, 0, bottom->p_index + 1, "");
  1935. X
  1936. X        /*
  1937. X         * Now replace the rest of the top line with the
  1938. X         * remainder of the bottom line.
  1939. X         */
  1940. X        replchars(curwin, top->p_line, top->p_index, INT_MAX,
  1941. X                        bottom->p_line->l_text);
  1942. X
  1943. X        /*
  1944. X         * Finally, delete all lines from (top + 1) to bot,
  1945. X         * inclusive.
  1946. X         */
  1947. X        repllines(curwin, top->p_line->l_next,
  1948. X                cntllines(top->p_line, bottom->p_line) - 1,
  1949. X                (Line *) NULL);
  1950. X
  1951. X        end_command(curwin);
  1952. X    }
  1953. X
  1954. X    if (top->p_index > 0) {
  1955. X        (void) one_right(curwin, FALSE);
  1956. X    }
  1957. X    }
  1958. X
  1959. X    /*
  1960. X     * Construct redo buffer.
  1961. X     */
  1962. X    Redo.r_mode = r_normal;
  1963. X    flexclear(&Redo.r_fb);
  1964. X    if (num != 0) {
  1965. X    (void) lformat(&Redo.r_fb, "d%ld%c%c", num, c1, c2);
  1966. X    } else {
  1967. X    (void) lformat(&Redo.r_fb, "d%c%c", c1, c2);
  1968. X    }
  1969. X
  1970. X    if (mtype != m_line && nlines == 1) {
  1971. X    updateline(curwin);
  1972. X    } else {
  1973. X    update_buffer(curbuf);
  1974. X    }
  1975. X}
  1976. X
  1977. X/*
  1978. X * op_change - handle a change operation
  1979. X */
  1980. Xstatic void
  1981. Xop_change(c1, c2, num, top, bottom)
  1982. Xint    c1, c2;
  1983. Xlong    num;
  1984. XPosn    *top;
  1985. XPosn    *bottom;
  1986. X{
  1987. X    bool_t    doappend;    /* true if we should do append, not insert */
  1988. X
  1989. X    /*
  1990. X     * Start the command here so the initial delete gets
  1991. X     * included in the meta-command and hence undo will
  1992. X     * work properly.
  1993. X     */
  1994. X    if (!start_command(curwin)) {
  1995. X    return;
  1996. X    }
  1997. X
  1998. X    if (mtype == m_line) {
  1999. X    long    nlines;
  2000. X    Line    *lp;
  2001. X
  2002. X    /*
  2003. X     * This is a bit awkward ... for a line-based change, we don't
  2004. X     * actually delete the whole range of lines, but instead leave
  2005. X     * the first line in place and delete its text after the cursor
  2006. X     * position. However, yanking the whole thing is probably okay.
  2007. X     */
  2008. X    yp_push_deleted();
  2009. X    if (!do_yank(curbuf, top, bottom, FALSE, cur_yp_name)) {
  2010. X        show_error(curwin, "Not enough memory to perform change");
  2011. X        return;
  2012. X    }
  2013. X
  2014. X    lp = top->p_line;
  2015. X
  2016. X    nlines = cntllines(lp, bottom->p_line);
  2017. X    if (nlines > 1) {
  2018. X        repllines(curwin, lp->l_next, nlines - 1, (Line *) NULL);
  2019. X    }
  2020. X
  2021. X    move_cursor(curwin, lp, 0);
  2022. X
  2023. X    /*
  2024. X     * This is not right; it won't do the right thing when
  2025. X     * the cursor is in the whitespace of an indented line.
  2026. X     * However, it will do for the moment.
  2027. X     */
  2028. X    begin_line(curwin, TRUE);
  2029. X
  2030. X    replchars(curwin, lp, curwin->w_cursor->p_index,
  2031. X                        strlen(lp->l_text), "");
  2032. X    update_buffer(curbuf);
  2033. X    } else {
  2034. X    /*
  2035. X     * A character-based change really is just a delete and an insert.
  2036. X     * So use the deletion code to make things easier.
  2037. X     */
  2038. X    doappend = (mtype == m_incl) && endofline(bottom);
  2039. X
  2040. X    op_delete(c1, c2, num, top, bottom);
  2041. X
  2042. X    if (doappend) {
  2043. X        (void) one_right(curwin, TRUE);
  2044. X    }
  2045. X    }
  2046. X
  2047. X    Redo.r_mode = r_insert;
  2048. X    flexclear(&Redo.r_fb);
  2049. X    if (num != 0) {
  2050. X    (void) lformat(&Redo.r_fb, "c%ld%c%c", num, c1, c2);
  2051. X    } else {
  2052. X    (void) lformat(&Redo.r_fb, "c%c%c", c1, c2);
  2053. X    }
  2054. X
  2055. X    startinsert(FALSE);
  2056. X}
  2057. X
  2058. Xstatic void
  2059. Xop_yank(top, bottom)
  2060. XPosn    *top;
  2061. XPosn    *bottom;
  2062. X{
  2063. X    long    nlines;
  2064. X
  2065. X    /*
  2066. X     * Report on the number of lines yanked.
  2067. X     */
  2068. X    nlines = cntllines(top->p_line, bottom->p_line);
  2069. X
  2070. X    if (nlines > Pn(P_report)) {
  2071. X    show_message(curwin, "%ld lines yanked", nlines);
  2072. X    }
  2073. X
  2074. X    /*
  2075. X     * If the target is non-inclusive and character-based,
  2076. X     * reduce the final position by one.
  2077. X     */
  2078. X    if (mtype == m_nonincl) {
  2079. X    (void) dec(bottom);
  2080. X    }
  2081. X
  2082. X    (void) do_yank(curbuf, top, bottom, (mtype != m_line), cur_yp_name);
  2083. X}
  2084. X
  2085. Xstatic bool_t
  2086. Xdojoin()
  2087. X{
  2088. X    register Posn    *curr_pos;    /* cursor position (abbreviation) */
  2089. X    register Line    *curr_line;    /* line we started the join on */
  2090. X    char        *nextline;    /* text of subsequent line */
  2091. X    int            join_index;    /* index of start of new text section */
  2092. X    int            size1;        /* size of the first line */
  2093. X    int            size2;        /* size of the second line */
  2094. X
  2095. X    curr_pos = curwin->w_cursor;
  2096. X    curr_line = curr_pos->p_line;
  2097. X
  2098. X    /*
  2099. X     * If we are on the last line, we can't join.
  2100. X     */
  2101. X    if (curr_line->l_next == curbuf->b_lastline)
  2102. X    return(FALSE);
  2103. X
  2104. X    if (!start_command(curwin)) {
  2105. X    return(FALSE);
  2106. X    }
  2107. X
  2108. X    /*
  2109. X     * Move cursor to end of line, and find out
  2110. X     * exactly where we will place the new text.
  2111. X     */
  2112. X    while (one_right(curwin, FALSE))
  2113. X    ;
  2114. X    join_index = curr_pos->p_index;
  2115. X    if (curr_line->l_text[join_index] != '\0')
  2116. X    join_index += 1;
  2117. X
  2118. X    /*
  2119. X     * Copy the text of the next line after the end of the
  2120. X     * current line, but don't copy any initial whitespace.
  2121. X     * Then delete the following line.
  2122. X     */
  2123. X    nextline = curr_line->l_next->l_text;
  2124. X    while (*nextline == ' ' || *nextline == '\t')
  2125. X    nextline++;
  2126. X    size1 = strlen(curr_line->l_text);
  2127. X    size2 = strlen(nextline);
  2128. X
  2129. X    replchars(curwin, curr_line, join_index, 0, nextline);
  2130. X    repllines(curwin, curr_line->l_next, (long) 1, (Line *) NULL);
  2131. X
  2132. X    if (size1 != 0 && size2 != 0) {
  2133. X    /*
  2134. X     * If there is no whitespace on this line,
  2135. X     * insert a single space.
  2136. X     */
  2137. X    if (gchar(curr_pos) != ' ' && gchar(curr_pos) != '\t') {
  2138. X        replchars(curwin, curr_line, curr_pos->p_index + 1, 0, " ");
  2139. X    }
  2140. X
  2141. X    /*
  2142. X     * Make sure the cursor sits on the right character.
  2143. X     */
  2144. X    (void) one_right(curwin, FALSE);
  2145. X    }
  2146. X
  2147. X    end_command(curwin);
  2148. X
  2149. X    update_buffer(curbuf);
  2150. X
  2151. X    return(TRUE);
  2152. X}
  2153. END_OF_FILE
  2154.   if test 41946 -ne `wc -c <'xvi/src/normal.c'`; then
  2155.     echo shar: \"'xvi/src/normal.c'\" unpacked with wrong size!
  2156.   fi
  2157.   # end of 'xvi/src/normal.c'
  2158. fi
  2159. echo shar: End of archive 3 \(of 18\).
  2160. cp /dev/null ark3isdone
  2161. MISSING=""
  2162. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ; do
  2163.     if test ! -f ark${I}isdone ; then
  2164.     MISSING="${MISSING} ${I}"
  2165.     fi
  2166. done
  2167. if test "${MISSING}" = "" ; then
  2168.     echo You have unpacked all 18 archives.
  2169.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2170. else
  2171.     echo You still must unpack the following archives:
  2172.     echo "        " ${MISSING}
  2173. fi
  2174. exit 0
  2175. exit 0 # Just in case...
  2176.