home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / stevie.zoo / stevie.3 < prev    next >
Encoding:
Text File  |  1990-03-14  |  53.8 KB  |  2,749 lines

  1.  
  2. : This is a shar archive.  Extract with sh, not csh.
  3. : The rest of this file will extract:
  4. : linefunc.c main.c mark.c misccmds.c normal.c ops.c
  5. echo extracting - linefunc.c
  6. sed 's/^X//' > linefunc.c << '!EOR!'
  7. X/* $Header: /nw/tony/src/stevie/src/RCS/linefunc.c,v 1.2 89/03/11 22:42:32 tony Exp $
  8. X *
  9. X * Basic line-oriented motions.
  10. X */
  11. X
  12. X#include "stevie.h"
  13. X#include "ops.h"
  14. X
  15. X/*
  16. X * nextline(curr)
  17. X *
  18. X * Return a pointer to the beginning of the next line after the one
  19. X * referenced by 'curr'. Return NULL if there is no next line (at EOF).
  20. X */
  21. X
  22. XLPTR *
  23. Xnextline(curr)
  24. XLPTR    *curr;
  25. X{
  26. X    static    LPTR    next;
  27. X
  28. X    if (curr->linep->next != Fileend->linep) {
  29. X        next.index = 0;
  30. X        next.linep = curr->linep->next;
  31. X        return &next;
  32. X    }
  33. X    return (LPTR *) NULL;
  34. X}
  35. X
  36. X/*
  37. X * prevline(curr)
  38. X *
  39. X * Return a pointer to the beginning of the line before the one
  40. X * referenced by 'curr'. Return NULL if there is no prior line.
  41. X */
  42. X
  43. XLPTR *
  44. Xprevline(curr)
  45. XLPTR    *curr;
  46. X{
  47. X    static    LPTR    prev;
  48. X
  49. X    if (curr->linep->prev != Filetop->linep) {
  50. X        prev.index = 0;
  51. X        prev.linep = curr->linep->prev;
  52. X        return &prev;
  53. X    }
  54. X    return (LPTR *) NULL;
  55. X}
  56. X
  57. X/*
  58. X * coladvance(p,col)
  59. X *
  60. X * Try to advance to the specified column, starting at p.
  61. X */
  62. X
  63. XLPTR *
  64. Xcoladvance(p, col)
  65. XLPTR    *p;
  66. Xregister int    col;
  67. X{
  68. X    static    LPTR    lp;
  69. X    register int    c, in;
  70. X
  71. X    lp.linep = p->linep;
  72. X    lp.index = p->index;
  73. X
  74. X    /* If we're on a blank ('\n' only) line, we can't do anything */
  75. X    if (lp.linep->s[lp.index] == '\0')
  76. X        return &lp;
  77. X    /* try to advance to the specified column */
  78. X    for ( c=0; col-- > 0; c++ ) {
  79. X        /* Count a tab for what it's worth (if list mode not on) */
  80. X        if ( gchar(&lp) == TAB && !P(P_LS) ) {
  81. X            in = ((P(P_TS)-1) - c%P(P_TS));
  82. X            col -= in;
  83. X            c += in;
  84. X        }
  85. X        /* Don't go past the end of */
  86. X        /* the file or the line. */
  87. X        if (inc(&lp)) {
  88. X            dec(&lp);
  89. X            break;
  90. X        }
  91. X    }
  92. X    return &lp;
  93. X}
  94. X
  95. X
  96. X/*
  97. X * nextchar(curr)
  98. X *
  99. X * Return a line pointer to the next character after the
  100. X * one referenced by 'curr'. Return NULL if there is no next one (at EOF).
  101. X * NOTE: this COULD point to a \n or \0 character.
  102. X */
  103. X
  104. XLPTR *
  105. Xnextchar(curr)
  106. XLPTR    *curr;
  107. X{
  108. X    static    LPTR    *next;
  109. X    char    c;
  110. X
  111. X    next = curr;
  112. X    c = CHAR( next );
  113. X    if (c=='\n' || c=='\0')        /* end of line */
  114. X        next = nextline (next);
  115. X    else
  116. X        next->index++;
  117. X
  118. X    return (next);
  119. X}
  120. X
  121. X
  122. X/*
  123. X * prevchar(curr)
  124. X *
  125. X * Return a line pointer to the previous character before the
  126. X * one referenced by 'curr'. Return NULL if there is no previous one.
  127. X * Note: this COULD point to a \n or \0 character.
  128. X */
  129. X
  130. XLPTR *
  131. Xprevchar(curr)
  132. XLPTR    *curr;
  133. X{
  134. X    static    LPTR    *prev;
  135. X    char    c;
  136. X
  137. X    prev = curr;
  138. X    if (prev->index == 0) {        /* beginning of line */
  139. X        prev = prevline (prev);        /* jump back */
  140. X        c = CHAR( prev );
  141. X        while (c!='\n' && c!= '\0') {    /* go to end of line */
  142. X            prev->index++;
  143. X            c = CHAR( prev );
  144. X        }
  145. X    }
  146. X    else
  147. X        prev->index--;
  148. X
  149. X    return (prev);
  150. X}
  151. X
  152. X
  153. !EOR!
  154. echo extracting - main.c
  155. sed 's/^X//' > main.c << '!EOR!'
  156. X/* $Header: /nw/tony/src/stevie/src/RCS/main.c,v 1.12 89/08/02 19:53:27 tony Exp $
  157. X *
  158. X * The main routine and routines to deal with the input buffer.
  159. X */
  160. X
  161. X#include "stevie.h"
  162. X
  163. Xint Rows;        /* Number of Rows and Columns */
  164. Xint Columns;        /* in the current window. */
  165. X
  166. Xchar *Realscreen = NULL;    /* What's currently on the screen, a single */
  167. X                /* array of size Rows*Columns. */
  168. Xchar *Nextscreen = NULL;    /* What's to be put on the screen. */
  169. X
  170. Xchar *Filename = NULL;    /* Current file name */
  171. X
  172. XLPTR *Filemem;        /* Pointer to the first line of the file */
  173. X
  174. XLPTR *Filetop;        /* Line 'above' the start of the file */
  175. X
  176. XLPTR *Fileend;        /* Pointer to the end of the file in Filemem. */
  177. X            /* (It points to the byte AFTER the last byte.) */
  178. X
  179. XLPTR *Topchar;        /* Pointer to the byte in Filemem which is */
  180. X            /* in the upper left corner of the screen. */
  181. X
  182. XLPTR *Botchar;        /* Pointer to the byte in Filemem which is */
  183. X            /* just off the bottom of the screen. */
  184. X
  185. XLPTR *Curschar;        /* Pointer to byte in Filemem at which the */
  186. X            /* cursor is currently placed. */
  187. X
  188. Xint Cursrow, Curscol;    /* Current position of cursor */
  189. X
  190. Xint Cursvcol;        /* Current virtual column, the column number of */
  191. X            /* the file's actual line, as opposed to the */
  192. X            /* column number we're at on the screen.  This */
  193. X            /* makes a difference on lines that span more */
  194. X            /* than one screen line. */
  195. X
  196. Xint Curswant = 0;    /* The column we'd like to be at. This is used */
  197. X            /* try to stay in the same column through up/down */
  198. X            /* cursor motions. */
  199. X
  200. Xbool_t set_want_col;    /* If set, then update Curswant the next time */
  201. X            /* through cursupdate() to the current virtual */
  202. X            /* column. */
  203. X
  204. Xint State = NORMAL;    /* This is the current state of the command */
  205. X            /* interpreter. */
  206. X
  207. Xint Prenum = 0;        /* The (optional) number before a command. */
  208. X
  209. XLPTR *Insstart;        /* This is where the latest insert/append */
  210. X            /* mode started. */
  211. X
  212. Xbool_t Changed = 0;    /* Set to 1 if something in the file has been */
  213. X            /* changed and not written out. */
  214. X
  215. Xchar Redobuff[1024];    /* Each command should stuff characters into this */
  216. X            /* buffer that will re-execute itself. */
  217. X
  218. Xchar Insbuff[1024];    /* Each insertion gets stuffed into this buffer. */
  219. X
  220. Xint Ninsert = 0;    /* Number of characters in the current insertion. */
  221. Xchar *Insptr = NULL;
  222. X
  223. Xbool_t    got_int=FALSE;    /* set to TRUE when an interrupt occurs (if possible) */
  224. X
  225. Xbool_t    interactive = FALSE;    /* set TRUE when main() is ready to roll */
  226. X
  227. Xchar **files;        /* list of input files */
  228. Xint  numfiles;        /* number of input files */
  229. Xint  curfile;        /* number of the current file */
  230. X
  231. Xstatic void
  232. Xusage()
  233. X{
  234. X    fprintf(stderr, "usage: stevie [file ...]\n");
  235. X    fprintf(stderr, "       stevie -t tag\n");
  236. X    fprintf(stderr, "       stevie +[num] file\n");
  237. X    fprintf(stderr, "       stevie +/pat  file\n");
  238. X    exit(1);
  239. X}
  240. X
  241. Xmain(argc,argv)
  242. Xint    argc;
  243. Xchar    *argv[];
  244. X{
  245. X    char    *initstr, *getenv();    /* init string from the environment */
  246. X    char    *tag = NULL;        /* tag from command line */
  247. X    char    *pat = NULL;        /* pattern from command line */
  248. X    int    line = -1;        /* line number from command line */
  249. X
  250. X    /*
  251. X     * Process the command line arguments.
  252. X     */
  253. X    if (argc > 1) {
  254. X        switch (argv[1][0]) {
  255. X        
  256. X        case '-':            /* -t tag */
  257. X            if (argv[1][1] != 't')
  258. X                usage();
  259. X
  260. X            if (argv[2] == NULL)
  261. X                usage();
  262. X
  263. X            Filename = NULL;
  264. X            tag = argv[2];
  265. X            numfiles = 1;
  266. X            break;
  267. X
  268. X        case '+':            /* +n or +/pat */
  269. X            if (argv[1][1] == '/') {
  270. X                if (argv[2] == NULL)
  271. X                    usage();
  272. X                Filename = strsave(argv[2]);
  273. X                pat = &(argv[1][1]);
  274. X                numfiles = 1;
  275. X
  276. X            } else if (isdigit(argv[1][1]) || argv[1][1] == NUL) {
  277. X                if (argv[2] == NULL)
  278. X                    usage();
  279. X                Filename = strsave(argv[2]);
  280. X                numfiles = 1;
  281. X
  282. X                line = (isdigit(argv[1][1])) ?
  283. X                    atoi(&(argv[1][1])) : 0;
  284. X            } else
  285. X                usage();
  286. X
  287. X            break;
  288. X
  289. X        default:            /* must be a file name */
  290. X            Filename = strsave(argv[1]);
  291. X            files = &(argv[1]);
  292. X            numfiles = argc - 1;
  293. X            break;
  294. X        }
  295. X    } else {
  296. X        Filename = NULL;
  297. X        numfiles = 1;
  298. X    }
  299. X    curfile = 0;
  300. X
  301. X     if (numfiles > 1)
  302. X         fprintf(stderr, "%d files to edit\n", numfiles);
  303. X    windinit();
  304. X
  305. X    /*
  306. X     * Allocate LPTR structures for all the various position pointers
  307. X     */
  308. X     if ((Filemem = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
  309. X         (Filetop = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
  310. X         (Fileend = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
  311. X         (Topchar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
  312. X         (Botchar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
  313. X         (Curschar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
  314. X        (Insstart = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
  315. X        (screenalloc() == -1) ) {
  316. X        fprintf(stderr, "Can't allocate data structures\n");
  317. X        windexit(0);
  318. X    }
  319. X
  320. X    filealloc();        /* Initialize Filemem, Filetop, and Fileend */
  321. X
  322. X    screenclear();
  323. X
  324. X    if ((initstr = getenv("EXINIT")) != NULL) {
  325. X        char *lp, buf[128];
  326. X
  327. X        if ((lp = getenv("LINES")) != NULL) {
  328. X            sprintf(buf, "%s lines=%s", initstr, lp);
  329. X            docmdln(buf);
  330. X        } else
  331. X            docmdln(initstr);
  332. X    }
  333. X
  334. X    if (Filename != NULL) {
  335. X        if (readfile(Filename, Filemem, FALSE))
  336. X            filemess("[New File]");
  337. X    } else if (tag == NULL)
  338. X        msg("Empty Buffer");
  339. X
  340. X    setpcmark();
  341. X
  342. X    if (tag) {
  343. X        stuffin(":ta ");
  344. X        stuffin(tag);
  345. X        stuffin("\n");
  346. X
  347. X    } else if (pat) {
  348. X        stuffin(pat);
  349. X        stuffin("\n");
  350. X
  351. X    } else if (line >= 0) {
  352. X        if (line > 0)
  353. X            stuffnum(line);
  354. X        stuffin("G");
  355. X    }
  356. X
  357. X    interactive = TRUE;
  358. X
  359. X    edit();
  360. X
  361. X    windexit(0);
  362. X
  363. X    return 1;        /* shouldn't be reached */
  364. X}
  365. X
  366. X#define    RBSIZE    1024
  367. Xstatic char getcbuff[RBSIZE];
  368. Xstatic char *getcnext = NULL;
  369. X
  370. Xvoid
  371. Xstuffin(s)
  372. Xchar    *s;
  373. X{
  374. X    if (s == NULL) {        /* clear the stuff buffer */
  375. X        getcnext = NULL;
  376. X        return;
  377. X    }
  378. X
  379. X    if (getcnext == NULL) {
  380. X        strcpy(getcbuff,s);
  381. X        getcnext = getcbuff;
  382. X    } else
  383. X        strcat(getcbuff,s);
  384. X}
  385. X
  386. Xvoid
  387. Xstuffnum(n)
  388. Xint    n;
  389. X{
  390. X    char    buf[32];
  391. X
  392. X    sprintf(buf, "%d", n);
  393. X    stuffin(buf);
  394. X}
  395. X
  396. Xint
  397. Xvgetc()
  398. X{
  399. X    register int    c;
  400. X
  401. X    /*
  402. X     * inchar() may map special keys by using stuffin(). If it does
  403. X     * so, it returns -1 so we know to loop here to get a real char.
  404. X     */
  405. X    do {
  406. X        if ( getcnext != NULL ) {
  407. X            int nextc = *getcnext++;
  408. X            if ( *getcnext == NUL ) {
  409. X                *getcbuff = NUL;
  410. X                getcnext = NULL;
  411. X            }
  412. X            return(nextc);
  413. X        }
  414. X        c = inchar();
  415. X    } while (c == -1);
  416. X
  417. X    return c;
  418. X}
  419. X
  420. X/*
  421. X * anyinput
  422. X *
  423. X * Return non-zero if input is pending.
  424. X */
  425. X
  426. Xbool_t
  427. Xanyinput()
  428. X{
  429. X    return (getcnext != NULL);
  430. X}
  431. X
  432. X/*
  433. X * do_mlines() - process mode lines for the current file
  434. X *
  435. X * Returns immediately if the "ml" parameter isn't set.
  436. X */
  437. X#define    NMLINES    5    /* no. of lines at start/end to check for modelines */
  438. X
  439. Xvoid
  440. Xdo_mlines()
  441. X{
  442. X    void    chk_mline();
  443. X    int    i;
  444. X    register LPTR    *p;
  445. X
  446. X    if (!P(P_ML))
  447. X        return;
  448. X
  449. X    p = Filemem;
  450. X    for (i=0; i < NMLINES ;i++) {
  451. X        chk_mline(p->linep->s);
  452. X        if ((p = nextline(p)) == NULL)
  453. X            break;
  454. X    }
  455. X
  456. X    if ((p = prevline(Fileend)) == NULL)
  457. X        return;
  458. X
  459. X    for (i=0; i < NMLINES ;i++) {
  460. X        chk_mline(p->linep->s);
  461. X        if ((p = prevline(p)) == NULL)
  462. X            break;
  463. X    }
  464. X}
  465. X
  466. X/*
  467. X * chk_mline() - check a single line for a mode string
  468. X */
  469. Xstatic void
  470. Xchk_mline(s)
  471. Xregister char    *s;
  472. X{
  473. X    register char    *cs;        /* local copy of any modeline found */
  474. X    register char    *e;
  475. X
  476. X    for (; *s != NUL ;s++) {
  477. X        if (strncmp(s, "vi:", 3) == 0 || strncmp(s, "ex:", 3) == 0) {
  478. X            cs = strsave(s+3);
  479. X            if ((e = strchr(cs, ':')) != NULL) {
  480. X                *e = NUL;
  481. X                stuffin(mkstr(CTRL('o')));
  482. X                docmdln(cs);
  483. X            }
  484. X            free(cs);
  485. X        }
  486. X    }
  487. X}
  488. !EOR!
  489. echo extracting - mark.c
  490. sed 's/^X//' > mark.c << '!EOR!'
  491. X/* $Header: /nw/tony/src/stevie/src/RCS/mark.c,v 1.3 89/03/11 22:42:39 tony Exp $
  492. X *
  493. X * Routines to save and retrieve marks.
  494. X */
  495. X
  496. X#include "stevie.h"
  497. X
  498. X#define    NMARKS    10        /* max. # of marks that can be saved */
  499. X
  500. Xstruct    mark {
  501. X    char    name;
  502. X    LPTR    pos;
  503. X};
  504. X
  505. Xstatic    struct    mark    mlist[NMARKS];
  506. Xstatic    struct    mark    pcmark;        /* previous context mark */
  507. Xstatic    bool_t    pcvalid = FALSE;    /* true if pcmark is valid */
  508. X
  509. X/*
  510. X * setmark(c) - set mark 'c' at current cursor position
  511. X *
  512. X * Returns TRUE on success, FALSE if no room for mark or bad name given.
  513. X */
  514. Xbool_t
  515. Xsetmark(c)
  516. Xregister char    c;
  517. X{
  518. X    register int    i;
  519. X
  520. X    if (!isalpha(c))
  521. X        return FALSE;
  522. X
  523. X    /*
  524. X     * If there is already a mark of this name, then just use the
  525. X     * existing mark entry.
  526. X     */
  527. X    for (i=0; i < NMARKS ;i++) {
  528. X        if (mlist[i].name == c) {
  529. X            mlist[i].pos = *Curschar;
  530. X            return TRUE;
  531. X        }
  532. X    }
  533. X
  534. X    /*
  535. X     * There wasn't a mark of the given name, so find a free slot
  536. X     */
  537. X    for (i=0; i < NMARKS ;i++) {
  538. X        if (mlist[i].name == NUL) {    /* got a free one */
  539. X            mlist[i].name = c;
  540. X            mlist[i].pos = *Curschar;
  541. X            return TRUE;
  542. X        }
  543. X    }
  544. X    return FALSE;
  545. X}
  546. X
  547. X/*
  548. X * setpcmark() - set the previous context mark to the current position
  549. X */
  550. Xvoid
  551. Xsetpcmark()
  552. X{
  553. X    pcmark.pos = *Curschar;
  554. X    pcvalid = TRUE;
  555. X}
  556. X
  557. X/*
  558. X * getmark(c) - find mark for char 'c'
  559. X *
  560. X * Return pointer to LPTR or NULL if no such mark.
  561. X */
  562. XLPTR *
  563. Xgetmark(c)
  564. Xregister char    c;
  565. X{
  566. X    register int    i;
  567. X
  568. X    if (c == '\'' || c == '`')    /* previous context mark */
  569. X        return pcvalid ? &(pcmark.pos) : (LPTR *) NULL;
  570. X
  571. X    for (i=0; i < NMARKS ;i++) {
  572. X        if (mlist[i].name == c)
  573. X            return &(mlist[i].pos);
  574. X    }
  575. X    return (LPTR *) NULL;
  576. X}
  577. X
  578. X/*
  579. X * clrall() - clear all marks
  580. X *
  581. X * Used mainly when trashing the entire buffer during ":e" type commands
  582. X */
  583. Xvoid
  584. Xclrall()
  585. X{
  586. X    register int    i;
  587. X
  588. X    for (i=0; i < NMARKS ;i++)
  589. X        mlist[i].name = NUL;
  590. X    pcvalid = FALSE;
  591. X}
  592. X
  593. X/*
  594. X * clrmark(line) - clear any marks for 'line'
  595. X *
  596. X * Used any time a line is deleted so we don't have marks pointing to
  597. X * non-existent lines.
  598. X */
  599. Xvoid
  600. Xclrmark(line)
  601. Xregister LINE    *line;
  602. X{
  603. X    register int    i;
  604. X
  605. X    for (i=0; i < NMARKS ;i++) {
  606. X        if (mlist[i].pos.linep == line)
  607. X            mlist[i].name = NUL;
  608. X    }
  609. X    if (pcvalid && (pcmark.pos.linep == line))
  610. X        pcvalid = FALSE;
  611. X}
  612. !EOR!
  613. echo extracting - misccmds.c
  614. sed 's/^X//' > misccmds.c << '!EOR!'
  615. X/* $Header: /nw/tony/src/stevie/src/RCS/misccmds.c,v 1.14 89/08/06 09:50:17 tony Exp $
  616. X *
  617. X * Various routines to perform specific editing operations or return
  618. X * useful information about the file.
  619. X */
  620. X
  621. X#include "stevie.h"
  622. X
  623. Xstatic    void    openfwd(), openbwd();
  624. X
  625. Xextern    bool_t    did_ai;
  626. X
  627. X/*
  628. X * opencmd
  629. X *
  630. X * Add a blank line above or below the current line.
  631. X */
  632. X
  633. Xvoid
  634. Xopencmd(dir, can_ai)
  635. Xint    dir;
  636. Xint    can_ai;            /* if true, consider auto-indent */
  637. X{
  638. X    if (dir == FORWARD)
  639. X        openfwd(can_ai);
  640. X    else
  641. X        openbwd(can_ai);
  642. X}
  643. X
  644. Xstatic void
  645. Xopenfwd(can_ai)
  646. Xint    can_ai;
  647. X{
  648. X    register LINE    *l;
  649. X    LPTR    *next;
  650. X    register char    *s;    /* string to be moved to new line, if any */
  651. X    int    newindex = 0;    /* index of the cursor on the new line */
  652. X
  653. X    /*
  654. X     * If we're in insert mode, we need to move the remainder of the
  655. X     * current line onto the new line. Otherwise the new line is left
  656. X     * blank.
  657. X     */
  658. X    if (State == INSERT || State == REPLACE)
  659. X        s = &Curschar->linep->s[Curschar->index];
  660. X    else
  661. X        s = "";
  662. X
  663. X    if ((next = nextline(Curschar)) == NULL)    /* open on last line */
  664. X        next = Fileend;
  665. X
  666. X    /*
  667. X     * By asking for as much space as the prior line had we make sure
  668. X     * that we'll have enough space for any auto-indenting.
  669. X     */
  670. X    if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL)
  671. X        return;
  672. X
  673. X    if (*s != NUL)
  674. X        strcpy(l->s, s);        /* copy string to new line */
  675. X
  676. X    else if (can_ai && P(P_AI) && !anyinput()) {
  677. X        char    *p;
  678. X
  679. X        /*
  680. X         * Copy prior line, and truncate after white space
  681. X         */
  682. X        strcpy(l->s, Curschar->linep->s);
  683. X
  684. X        for (p = l->s; *p == ' ' || *p == TAB ;p++)
  685. X            ;
  686. X        *p = NUL;
  687. X        newindex = p - l->s;
  688. X
  689. X        /*
  690. X         * If we just did an auto-indent, then we didn't type
  691. X         * anything on the prior line, and it should be truncated.
  692. X         */
  693. X        if (did_ai)
  694. X            Curschar->linep->s[0] = NUL;
  695. X
  696. X        did_ai = TRUE;
  697. X    }
  698. X
  699. X    /* truncate current line at cursor */
  700. X    if (State == INSERT || State == REPLACE)
  701. X        *s = NUL;
  702. X            
  703. X
  704. X    Curschar->linep->next = l;    /* link neighbors to new line */
  705. X    next->linep->prev = l;
  706. X
  707. X    l->prev = Curschar->linep;    /* link new line to neighbors */
  708. X    l->next = next->linep;
  709. X
  710. X    if (next == Fileend)            /* new line at end */
  711. X        l->num = Curschar->linep->num + LINEINC;
  712. X
  713. X    else if ((l->prev->num) + 1 == l->next->num)    /* no gap, renumber */
  714. X        renum();
  715. X
  716. X    else {                    /* stick it in the middle */
  717. X        unsigned long    lnum;
  718. X        lnum = ((long)l->prev->num + (long)l->next->num) / 2;
  719. X        l->num = lnum;
  720. X    }
  721. X
  722. X    /*
  723. X     * Get the cursor to the start of the line, so that 'Cursrow'
  724. X     * gets set to the right physical line number for the stuff
  725. X     * that follows...
  726. X     */
  727. X    Curschar->index = 0;
  728. X    cursupdate();
  729. X
  730. X    /*
  731. X     * If we're doing an open on the last logical line, then
  732. X     * go ahead and scroll the screen up. Otherwise, just insert
  733. X     * a blank line at the right place. We use calls to plines()
  734. X     * in case the cursor is resting on a long line.
  735. X     */
  736. X    if (Cursrow + plines(Curschar) == (Rows - 1))
  737. X        scrollup(1);
  738. X    else
  739. X        s_ins(Cursrow+plines(Curschar), 1);
  740. X
  741. X    *Curschar = *nextline(Curschar);    /* cursor moves down */
  742. X    Curschar->index = newindex;
  743. X
  744. X    updatescreen();        /* because Botchar is now invalid... */
  745. X
  746. X    cursupdate();        /* update Cursrow before insert */
  747. X}
  748. X
  749. Xstatic void
  750. Xopenbwd(can_ai)
  751. Xint    can_ai;
  752. X{
  753. X    register LINE    *l;
  754. X    LINE    *prev;
  755. X    int    newindex = 0;
  756. X
  757. X    prev = Curschar->linep->prev;
  758. X
  759. X    if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL)
  760. X        return;
  761. X
  762. X    Curschar->linep->prev = l;    /* link neighbors to new line */
  763. X    prev->next = l;
  764. X
  765. X    l->next = Curschar->linep;    /* link new line to neighbors */
  766. X    l->prev = prev;
  767. X
  768. X    if (can_ai && P(P_AI) && !anyinput()) {
  769. X        char    *p;
  770. X
  771. X        /*
  772. X         * Copy current line, and truncate after white space
  773. X         */
  774. X        strcpy(l->s, Curschar->linep->s);
  775. X
  776. X        for (p = l->s; *p == ' ' || *p == TAB ;p++)
  777. X            ;
  778. X        *p = NUL;
  779. X        newindex = p - l->s;
  780. X
  781. X        did_ai = TRUE;
  782. X    }
  783. X
  784. X    Curschar->linep = Curschar->linep->prev;
  785. X    Curschar->index = newindex;
  786. X
  787. X    if (prev == Filetop->linep)        /* new start of file */
  788. X        Filemem->linep = l;
  789. X
  790. X    renum();    /* keep it simple - we don't do this often */
  791. X
  792. X    cursupdate();            /* update Cursrow before insert */
  793. X    if (Cursrow != 0)
  794. X        s_ins(Cursrow, 1);        /* insert a physical line */
  795. X
  796. X    updatescreen();
  797. X}
  798. X
  799. Xint
  800. Xcntllines(pbegin,pend)
  801. Xregister LPTR    *pbegin, *pend;
  802. X{
  803. X    register LINE    *lp;
  804. X    int    lnum = 1;
  805. X
  806. X    for (lp = pbegin->linep; lp != pend->linep ;lp = lp->next)
  807. X        lnum++;
  808. X
  809. X    return(lnum);
  810. X}
  811. X
  812. X/*
  813. X * plines(p) - return the number of physical screen lines taken by line 'p'
  814. X */
  815. Xint
  816. Xplines(p)
  817. XLPTR    *p;
  818. X{
  819. X    register int    col = 0;
  820. X    register char    *s;
  821. X
  822. X    s = p->linep->s;
  823. X
  824. X    if (*s == NUL)        /* empty line */
  825. X        return 1;
  826. X
  827. X    for (; *s != NUL ;s++) {
  828. X        if ( *s == TAB && !P(P_LS))
  829. X            col += P(P_TS) - (col % P(P_TS));
  830. X        else
  831. X            col += chars[(unsigned)(*s & 0xff)].ch_size;
  832. X    }
  833. X
  834. X    /*
  835. X     * If list mode is on, then the '$' at the end of
  836. X     * the line takes up one extra column.
  837. X     */
  838. X    if (P(P_LS))
  839. X        col += 1;
  840. X    /*
  841. X     * If 'number' mode is on, add another 8.
  842. X     */
  843. X    if (P(P_NU))
  844. X        col += 8;
  845. X
  846. X    return ((col + (Columns-1)) / Columns);
  847. X}
  848. X
  849. Xvoid
  850. Xfileinfo()
  851. X{
  852. X    extern    int    numfiles, curfile;
  853. X    register long    l1, l2;
  854. X
  855. X    if (bufempty()) {
  856. X         l1 = 0;
  857. X         l2 = 1;            /* don't div by zero */
  858. X     } else {
  859. X         l1 = cntllines(Filemem, Curschar);
  860. X         l2 = cntllines(Filemem, Fileend) - 1;
  861. X    }
  862. X
  863. X    if (numfiles > 1)
  864. X        smsg("\"%s\"%s line %ld of %ld -- %ld %% -- (file %d of %d)",
  865. X            (Filename != NULL) ? Filename : "No File",
  866. X            Changed ? " [Modified]" : "",
  867. X            l1, l2, (l1 * 100)/l2,
  868. X            curfile+1, numfiles);
  869. X    else
  870. X        smsg("\"%s\"%s line %ld of %ld -- %ld %% --",
  871. X            (Filename != NULL) ? Filename : "No File",
  872. X            Changed ? " [Modified]" : "",
  873. X            l1, l2, (l1 * 100)/l2);
  874. X}
  875. X
  876. X/*
  877. X * gotoline(n) - return a pointer to line 'n'
  878. X *
  879. X * Returns a pointer to the last line of the file if n is zero, or
  880. X * beyond the end of the file.
  881. X */
  882. XLPTR *
  883. Xgotoline(n)
  884. Xregister int    n;
  885. X{
  886. X    static    LPTR    l;
  887. X
  888. X    l.index = 0;
  889. X
  890. X    if ( n == 0 )
  891. X        l = *prevline(Fileend);
  892. X    else {
  893. X        LPTR    *p;
  894. X
  895. X        for (l = *Filemem; --n > 0 ;l = *p)
  896. X            if ((p = nextline(&l)) == NULL)
  897. X                break;
  898. X    }
  899. X    return &l;
  900. X}
  901. X
  902. Xvoid
  903. Xinschar(c)
  904. Xint    c;
  905. X{
  906. X    register char    *p, *pend;
  907. X
  908. X    /* make room for the new char. */
  909. X    if ( ! canincrease(1) )
  910. X        return;
  911. X
  912. X    if (State != REPLACE) {
  913. X        p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1];
  914. X        pend = &Curschar->linep->s[Curschar->index];
  915. X
  916. X        for (; p > pend ;p--)
  917. X            *p = *(p-1);
  918. X
  919. X        *p = c;
  920. X
  921. X    } else {    /* replace mode */
  922. X        /*
  923. X         * Once we reach the end of the line, we are effectively
  924. X         * inserting new text, so make sure the string terminator
  925. X         * stays out there.
  926. X         */
  927. X        if (gchar(Curschar) == NUL)
  928. X            Curschar->linep->s[Curschar->index+1] = NUL;
  929. X        pchar(Curschar, c);
  930. X    }
  931. X
  932. X    /*
  933. X     * If we're in insert mode and showmatch mode is set, then
  934. X     * check for right parens and braces. If there isn't a match,
  935. X     * then beep. If there is a match AND it's on the screen, then
  936. X     * flash to it briefly. If it isn't on the screen, don't do anything.
  937. X     */
  938. X    if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) {
  939. X        LPTR    *lpos, csave;
  940. X
  941. X        if ((lpos = showmatch()) == NULL)    /* no match, so beep */
  942. X            beep();
  943. X        else if (LINEOF(lpos) >= LINEOF(Topchar)) {
  944. X            updatescreen();        /* show the new char first */
  945. X            csave = *Curschar;
  946. X            *Curschar = *lpos;    /* move to matching char */
  947. X            cursupdate();
  948. X            windgoto(Cursrow, Curscol);
  949. X            pause();        /* brief pause */
  950. X            *Curschar = csave;    /* restore cursor position */
  951. X            cursupdate();
  952. X        }
  953. X    }
  954. X
  955. X    inc(Curschar);
  956. X    CHANGED;
  957. X}
  958. X
  959. Xbool_t
  960. Xdelchar(fixpos)
  961. Xbool_t    fixpos;        /* if TRUE, fix the cursor position when done */
  962. X{
  963. X    register int    i;
  964. X
  965. X    /* Check for degenerate case; there's nothing in the file. */
  966. X    if (bufempty())
  967. X        return FALSE;
  968. X
  969. X    if (lineempty())    /* can't do anything */
  970. X        return FALSE;
  971. X
  972. X    /* Delete the char. at Curschar by shifting everything */
  973. X    /* in the line down. */
  974. X    for ( i=Curschar->index+1; i < Curschar->linep->size ;i++)
  975. X        Curschar->linep->s[i-1] = Curschar->linep->s[i];
  976. X
  977. X    /* If we just took off the last character of a non-blank line, */
  978. X    /* we don't want to end up positioned at the newline. */
  979. X    if (fixpos) {
  980. X        if (gchar(Curschar)==NUL && Curschar->index>0 && State!=INSERT)
  981. X            Curschar->index--;
  982. X    }
  983. X    CHANGED;
  984. X
  985. X    return TRUE;
  986. X}
  987. X
  988. X
  989. Xvoid
  990. Xdelline(nlines, can_update)
  991. Xint    nlines;
  992. Xbool_t    can_update;
  993. X{
  994. X    register LINE    *p, *q;
  995. X    int    doscreen;        /* if true, update the screen */
  996. X
  997. X    doscreen = can_update;
  998. X    /*
  999. X     * There's no point in keeping the screen updated if we're
  1000. X     * deleting more than a screen's worth of lines.
  1001. X     */
  1002. X    if (nlines > (Rows - 1) && can_update) {
  1003. X        doscreen = FALSE;
  1004. X        s_del(Cursrow, Rows-1);    /* flaky way to clear rest of screen */
  1005. X    }
  1006. X
  1007. X    while ( nlines-- > 0 ) {
  1008. X
  1009. X        if (bufempty())            /* nothing to delete */
  1010. X            break;
  1011. X
  1012. X        if (buf1line()) {        /* just clear the line */
  1013. X            Curschar->linep->s[0] = NUL;
  1014. X            Curschar->index = 0;
  1015. X            break;
  1016. X        }
  1017. X
  1018. X        p = Curschar->linep->prev;
  1019. X        q = Curschar->linep->next;
  1020. X
  1021. X        if (p == Filetop->linep) {    /* first line of file so... */
  1022. X            Filemem->linep = q;    /* adjust start of file */
  1023. X            Topchar->linep = q;    /* and screen */
  1024. X        }
  1025. X        p->next = q;
  1026. X        q->prev = p;
  1027. X
  1028. X        clrmark(Curschar->linep);    /* clear marks for the line */
  1029. X
  1030. X        /*
  1031. X         * Delete the correct number of physical lines on the screen
  1032. X         */
  1033. X        if (doscreen)
  1034. X            s_del(Cursrow, plines(Curschar));
  1035. X
  1036. X        /*
  1037. X         * If deleting the top line on the screen, adjust Topchar
  1038. X         */
  1039. X        if (Topchar->linep == Curschar->linep)
  1040. X            Topchar->linep = q;
  1041. X
  1042. X        free(Curschar->linep->s);
  1043. X        free((char *) Curschar->linep);
  1044. X
  1045. X        Curschar->linep = q;
  1046. X        Curschar->index = 0;        /* is this right? */
  1047. X        CHANGED;
  1048. X
  1049. X        /* If we delete the last line in the file, back up */
  1050. X        if ( Curschar->linep == Fileend->linep) {
  1051. X            Curschar->linep = Curschar->linep->prev;
  1052. X            /* and don't try to delete any more lines */
  1053. X            break;
  1054. X        }
  1055. X    }
  1056. X}
  1057. !EOR!
  1058. echo extracting - normal.c
  1059. sed 's/^X//' > normal.c << '!EOR!'
  1060. X/* $Header: /nw/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $
  1061. X *
  1062. X * Contains the main routine for processing characters in command mode.
  1063. X * Communicates closely with the code in ops.c to handle the operators.
  1064. X */
  1065. X
  1066. X#include "stevie.h"
  1067. X#include "ops.h"
  1068. X
  1069. X/*
  1070. X * Generally speaking, every command in normal() should either clear any
  1071. X * pending operator (with CLEAROP), or set the motion type variable.
  1072. X */
  1073. X
  1074. X#define    CLEAROP    (operator=NOP)    /* clear any pending operator */
  1075. X
  1076. Xint    operator = NOP;        /* current pending operator */
  1077. Xint    mtype;            /* type of the current cursor motion */
  1078. Xbool_t    mincl;            /* true if char motion is inclusive */
  1079. XLPTR    startop;        /* cursor pos. at start of operator */
  1080. X
  1081. X/*
  1082. X * Operators can have counts either before the operator, or between the
  1083. X * operator and the following cursor motion as in:
  1084. X *
  1085. X *    d3w or 3dw
  1086. X *
  1087. X * If a count is given before the operator, it is saved in opnum. If
  1088. X * normal() is called with a pending operator, the count in opnum (if
  1089. X * present) overrides any count that came later.
  1090. X */
  1091. Xstatic    int    opnum = 0;
  1092. X
  1093. X#define    DEFAULT1(x)    (((x) == 0) ? 1 : (x))
  1094. X
  1095. X/*
  1096. X * normal(c)
  1097. X *
  1098. X * Execute a command in command mode.
  1099. X *
  1100. X * This is basically a big switch with the cases arranged in rough categories
  1101. X * in the following order:
  1102. X *
  1103. X *    1. File positioning commands
  1104. X *    2. Control commands (e.g. ^G, Z, screen redraw, etc)
  1105. X *    3. Character motions
  1106. X *    4. Search commands (of various kinds)
  1107. X *    5. Edit commands (e.g. J, x, X)
  1108. X *    6. Insert commands (e.g. i, o, O, A)
  1109. X *    7. Operators
  1110. X *    8. Abbreviations (e.g. D, C)
  1111. X *    9. Marks
  1112. X */
  1113. Xvoid
  1114. Xnormal(c)
  1115. Xregister int    c;
  1116. X{
  1117. X    register int    n;
  1118. X    register char    *s;    /* temporary variable for misc. strings */
  1119. X    bool_t    flag = FALSE;
  1120. X    int    type = 0;    /* used in some operations to modify type */
  1121. X    int    dir = FORWARD;    /* search direction */
  1122. X    int    nchar = NUL;
  1123. X    bool_t    finish_op;
  1124. X
  1125. X    /*
  1126. X     * If there is an operator pending, then the command we take
  1127. X     * this time will terminate it. Finish_op tells us to finish
  1128. X     * the operation before returning this time (unless the operation
  1129. X     * was cancelled.
  1130. X     */
  1131. X    finish_op = (operator != NOP);
  1132. X
  1133. X    /*
  1134. X     * If we're in the middle of an operator AND we had a count before
  1135. X     * the operator, then that count overrides the current value of
  1136. X     * Prenum. What this means effectively, is that commands like
  1137. X     * "3dw" get turned into "d3w" which makes things fall into place
  1138. X     * pretty neatly.
  1139. X     */
  1140. X    if (finish_op) {
  1141. X        if (opnum != 0)
  1142. X            Prenum = opnum;
  1143. X    } else
  1144. X        opnum = 0;
  1145. X
  1146. X    u_lcheck();    /* clear the "line undo" buffer if we've moved */
  1147. X
  1148. X    switch (c & 0xff) {
  1149. X
  1150. X    /*
  1151. X     * Screen positioning commands
  1152. X     */
  1153. X    case CTRL('D'):
  1154. X        CLEAROP;
  1155. X        if (Prenum)
  1156. X            P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  1157. X        scrollup(P(P_SS));
  1158. X        onedown(P(P_SS));
  1159. X        updatescreen();
  1160. X        break;
  1161. X
  1162. X    case CTRL('U'):
  1163. X        CLEAROP;
  1164. X        if (Prenum)
  1165. X            P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  1166. X        scrolldown(P(P_SS));
  1167. X        oneup(P(P_SS));
  1168. X        updatescreen();
  1169. X        break;
  1170. X
  1171. X    /*
  1172. X     * This is kind of a hack. If we're moving by one page, the calls
  1173. X     * to stuffin() do exactly the right thing in terms of leaving
  1174. X     * some context, and so on. If a count was given, we don't have
  1175. X     * to worry about these issues.
  1176. X     */
  1177. X    case CTRL('F'):
  1178. X        CLEAROP;
  1179. X        n = DEFAULT1(Prenum);
  1180. X        if (n > 1) {
  1181. X            if ( ! onedown(Rows * n) )
  1182. X                beep();
  1183. X            cursupdate();
  1184. X        } else {
  1185. X            /* screenclear(); */
  1186. X            stuffin("Lz\nM");
  1187. X        }
  1188. X        break;
  1189. X
  1190. X    case CTRL('B'):
  1191. X        CLEAROP;
  1192. X        n = DEFAULT1(Prenum);
  1193. X        if (n > 1) {
  1194. X            if ( ! oneup(Rows * n) )
  1195. X                beep();
  1196. X            cursupdate();
  1197. X        } else {
  1198. X            /* screenclear(); */
  1199. X            stuffin("Hz-M");
  1200. X        }
  1201. X        break;
  1202. X
  1203. X    case CTRL('E'):
  1204. X        CLEAROP;
  1205. X        scrollup(DEFAULT1(Prenum));
  1206. X        updatescreen();
  1207. X        break;
  1208. X
  1209. X    case CTRL('Y'):
  1210. X        CLEAROP;
  1211. X        scrolldown(DEFAULT1(Prenum));
  1212. X        updatescreen();
  1213. X        break;
  1214. X
  1215. X    case 'z':
  1216. X        CLEAROP;
  1217. X        switch (vgetc()) {
  1218. X        case NL:        /* put Curschar at top of screen */
  1219. X        case CR:
  1220. X            *Topchar = *Curschar;
  1221. X            Topchar->index = 0;
  1222. X            updatescreen();
  1223. X            break;
  1224. X
  1225. X        case '.':        /* put Curschar in middle of screen */
  1226. X            n = Rows/2;
  1227. X            goto dozcmd;
  1228. X
  1229. X        case '-':        /* put Curschar at bottom of screen */
  1230. X            n = Rows-1;
  1231. X            /* fall through */
  1232. X
  1233. X        dozcmd:
  1234. X            {
  1235. X                register LPTR    *lp = Curschar;
  1236. X                register int    l = 0;
  1237. X
  1238. X                while ((l < n) && (lp != NULL)) {
  1239. X                    l += plines(lp);
  1240. X                    *Topchar = *lp;
  1241. X                    lp = prevline(lp);
  1242. X                }
  1243. X            }
  1244. X            Topchar->index = 0;
  1245. X            updatescreen();
  1246. X            break;
  1247. X
  1248. X        default:
  1249. X            beep();
  1250. X        }
  1251. X        break;
  1252. X
  1253. X    /*
  1254. X     * Control commands
  1255. X     */
  1256. X    case ':':
  1257. X        CLEAROP;
  1258. X        if ((s = getcmdln(c)) != NULL)
  1259. X            docmdln(s);
  1260. X        break;
  1261. X
  1262. X    case K_HELP:
  1263. X        CLEAROP;
  1264. X        if (help()) {
  1265. X            screenclear();
  1266. X            updatescreen();
  1267. X        }
  1268. X        break;
  1269. X
  1270. X    case CTRL('L'):
  1271. X        CLEAROP;
  1272. X        screenclear();
  1273. X        updatescreen();
  1274. X        break;
  1275. X
  1276. X
  1277. X    case CTRL('O'):            /* ignored */
  1278. X        /*
  1279. X         * A command that's ignored can be useful. We use it at
  1280. X         * times when we want to postpone redraws. By stuffing
  1281. X         * in a control-o, redraws get suspended until the editor
  1282. X         * gets back around to processing input.
  1283. X         */
  1284. X        break;
  1285. X
  1286. X    case CTRL('G'):
  1287. X        CLEAROP;
  1288. X        fileinfo();
  1289. X        break;
  1290. X
  1291. X    case K_CCIRCM:            /* shorthand command */
  1292. X        CLEAROP;
  1293. X#ifdef TAGSTACK
  1294. X        /* If tag stacking compiled in & enabled, this is an untag.
  1295. X         * Otherwise, or if tag stack empty, edit alternate file.
  1296. X         * "untage" is so interpreted by dountag().
  1297. X         */
  1298. X        if (P(P_TG))
  1299. X            stuffin(":untage\n");
  1300. X        else
  1301. X#endif
  1302. X            stuffin(":e #\n");
  1303. X        break;
  1304. X
  1305. X    case 'Z':            /* write, if changed, and exit */
  1306. X        if (vgetc() != 'Z') {
  1307. X            beep();
  1308. X            break;
  1309. X        }
  1310. X        doxit();
  1311. X        break;
  1312. X
  1313. X    /*
  1314. X     * Macro evaluates true if char 'c' is a valid identifier character
  1315. X     */
  1316. X#    define    IDCHAR(c)    (isalpha(c) || isdigit(c) || (c) == '_')
  1317. X
  1318. X    case CTRL(']'):            /* :ta to current identifier */
  1319. X        CLEAROP;
  1320. X        {
  1321. X            char    ch;
  1322. X            LPTR    save;
  1323. X
  1324. X            save = *Curschar;
  1325. X            /*
  1326. X             * First back up to start of identifier. This
  1327. X             * doesn't match the real vi but I like it a
  1328. X             * little better and it shouldn't bother anyone.
  1329. X             */
  1330. X            ch = gchar(Curschar);
  1331. X            while (IDCHAR(ch)) {
  1332. X                if (!oneleft())
  1333. X                    break;
  1334. X                ch = gchar(Curschar);
  1335. X            }
  1336. X            if (!IDCHAR(ch))
  1337. X                oneright();
  1338. X
  1339. X            stuffin(":ta ");
  1340. X            /*
  1341. X             * Now grab the chars in the identifier
  1342. X             */
  1343. X            ch = gchar(Curschar);
  1344. X            while (IDCHAR(ch)) {
  1345. X                stuffin(mkstr(ch));
  1346. X                if (!oneright())
  1347. X                    break;
  1348. X                ch = gchar(Curschar);
  1349. X            }
  1350. X            stuffin("\n");
  1351. X
  1352. X            *Curschar = save;    /* restore, in case of error */
  1353. X        }
  1354. X        break;
  1355. X
  1356. X    /*
  1357. X     * Character motion commands
  1358. X     */
  1359. X    case 'G':
  1360. X        mtype = MLINE;
  1361. X        *Curschar = *gotoline(Prenum);
  1362. X        beginline(TRUE);
  1363. X        break;
  1364. X
  1365. X    case 'H':
  1366. X        mtype = MLINE;
  1367. X        *Curschar = *Topchar;
  1368. X        for (n = Prenum; n && onedown(1) ;n--)
  1369. X            ;
  1370. X        beginline(TRUE);
  1371. X        break;
  1372. X
  1373. X    case 'M':
  1374. X        mtype = MLINE;
  1375. X        *Curschar = *Topchar;
  1376. X        for (n = 0; n < Rows/2 && onedown(1) ;n++)
  1377. X            ;
  1378. X        beginline(TRUE);
  1379. X        break;
  1380. X
  1381. X    case 'L':
  1382. X        mtype = MLINE;
  1383. X        *Curschar = *prevline(Botchar);
  1384. X        for (n = Prenum; n && oneup(1) ;n--)
  1385. X            ;
  1386. X        beginline(TRUE);
  1387. X        break;
  1388. X
  1389. X    case 'l':
  1390. X    case K_RARROW:
  1391. X    case ' ':
  1392. X        mtype = MCHAR;
  1393. X        mincl = FALSE;
  1394. X        n = DEFAULT1(Prenum);
  1395. X        while (n--) {
  1396. X            if ( ! oneright() )
  1397. X                beep();
  1398. X        }
  1399. X        set_want_col = TRUE;
  1400. X        break;
  1401. X
  1402. X    case 'h':
  1403. X    case K_LARROW:
  1404. X    case CTRL('H'):
  1405. X        mtype = MCHAR;
  1406. X        mincl = FALSE;
  1407. X        n = DEFAULT1(Prenum);
  1408. X        while (n--) {
  1409. X            if ( ! oneleft() )
  1410. X                beep();
  1411. X        }
  1412. X        set_want_col = TRUE;
  1413. X        break;
  1414. X
  1415. X    case '-':
  1416. X        flag = TRUE;
  1417. X        /* fall through */
  1418. X
  1419. X    case 'k':
  1420. X    case K_UARROW:
  1421. X    case CTRL('P'):
  1422. X        mtype = MLINE;
  1423. X        if ( ! oneup(DEFAULT1(Prenum)) )
  1424. X            beep();
  1425. X        if (flag)
  1426. X            beginline(TRUE);
  1427. X        break;
  1428. X
  1429. X    case '+':
  1430. X    case CR:
  1431. X    case NL:
  1432. X        flag = TRUE;
  1433. X        /* fall through */
  1434. X
  1435. X    case 'j':
  1436. X    case K_DARROW:
  1437. X    case CTRL('N'):
  1438. X        mtype = MLINE;
  1439. X        if ( ! onedown(DEFAULT1(Prenum)) )
  1440. X            beep();
  1441. X        if (flag)
  1442. X            beginline(TRUE);
  1443. X        break;
  1444. X
  1445. X    /*
  1446. X     * This is a strange motion command that helps make operators
  1447. X     * more logical. It is actually implemented, but not documented
  1448. X     * in the real 'vi'. This motion command actually refers to "the
  1449. X     * current line". Commands like "dd" and "yy" are really an alternate
  1450. X     * form of "d_" and "y_". It does accept a count, so "d3_" works to
  1451. X     * delete 3 lines.
  1452. X     */
  1453. X    case '_':
  1454. X    lineop:
  1455. X        mtype = MLINE;
  1456. X        onedown(DEFAULT1(Prenum)-1);
  1457. X        break;
  1458. X
  1459. X    case '|':
  1460. X        mtype = MCHAR;
  1461. X        mincl = TRUE;
  1462. X        beginline(FALSE);
  1463. X        if (Prenum > 0)
  1464. X            *Curschar = *coladvance(Curschar, Prenum-1);
  1465. X        Curswant = Prenum - 1;
  1466. X        break;
  1467. X        
  1468. X    /*
  1469. X     * Word Motions
  1470. X     */
  1471. X
  1472. X    case 'B':
  1473. X        type = 1;
  1474. X        /* fall through */
  1475. X
  1476. X    case 'b':
  1477. X        mtype = MCHAR;
  1478. X        mincl = FALSE;
  1479. X        set_want_col = TRUE;
  1480. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  1481. X            LPTR    *pos;
  1482. X
  1483. X            if ((pos = bck_word(Curschar, type)) == NULL) {
  1484. X                beep();
  1485. X                CLEAROP;
  1486. X                break;
  1487. X            } else
  1488. X                *Curschar = *pos;
  1489. X        }
  1490. X        break;
  1491. X
  1492. X    case 'W':
  1493. X        type = 1;
  1494. X        /* fall through */
  1495. X
  1496. X    case 'w':
  1497. X        /*
  1498. X         * This is a little strange. To match what the real vi
  1499. X         * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
  1500. X         * This seems impolite at first, but it's really more
  1501. X         * what we mean when we say 'cw'.
  1502. X         */
  1503. X        if (operator == CHANGE)
  1504. X            goto doecmd;
  1505. X
  1506. X        mtype = MCHAR;
  1507. X        mincl = FALSE;
  1508. X        set_want_col = TRUE;
  1509. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  1510. X            LPTR    *pos;
  1511. X
  1512. X            if ((pos = fwd_word(Curschar, type)) == NULL) {
  1513. X                beep();
  1514. X                CLEAROP;
  1515. X                break;
  1516. X            } else
  1517. X                *Curschar = *pos;
  1518. X        }
  1519. X        break;
  1520. X
  1521. X    case 'E':
  1522. X        type = 1;
  1523. X        /* fall through */
  1524. X
  1525. X    case 'e':
  1526. X    doecmd:
  1527. X        mtype = MCHAR;
  1528. X        mincl = TRUE;
  1529. X        set_want_col = TRUE;
  1530. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  1531. X            LPTR    *pos;
  1532. X
  1533. X            /*
  1534. X             * The first motion gets special treatment if we're
  1535. X             * do a 'CHANGE'.
  1536. X             */
  1537. X            if (n == DEFAULT1(Prenum))
  1538. X                pos = end_word(Curschar,type,operator==CHANGE);
  1539. X            else
  1540. X                pos = end_word(Curschar, type, FALSE);
  1541. X
  1542. X            if (pos == NULL) {
  1543. X                beep();
  1544. X                CLEAROP;
  1545. X                break;
  1546. X            } else
  1547. X                *Curschar = *pos;
  1548. X        }
  1549. X        break;
  1550. X
  1551. X    case '$':
  1552. X        mtype = MCHAR;
  1553. X        mincl = TRUE;
  1554. X        while ( oneright() )
  1555. X            ;
  1556. X        Curswant = 999;        /* so we stay at the end */
  1557. X        break;
  1558. X
  1559. X    case '^':
  1560. X        mtype = MCHAR;
  1561. X        mincl = FALSE;
  1562. X        beginline(TRUE);
  1563. X        break;
  1564. X
  1565. X    case '0':
  1566. X        mtype = MCHAR;
  1567. X        mincl = TRUE;
  1568. X        beginline(FALSE);
  1569. X        break;
  1570. X
  1571. X    /*
  1572. X     * Searches of various kinds
  1573. X     */
  1574. X    case '?':
  1575. X    case '/':
  1576. X        s = getcmdln(c);    /* get the search string */
  1577. X
  1578. X        /*
  1579. X         * If they backspaced out of the search command,
  1580. X         * just bag everything.
  1581. X         */
  1582. X        if (s == NULL) {
  1583. X            CLEAROP;
  1584. X            break;
  1585. X        }
  1586. X
  1587. X        mtype = MCHAR;
  1588. X        mincl = FALSE;
  1589. X        set_want_col = TRUE;
  1590. X
  1591. X        /*
  1592. X         * If no string given, pass NULL to repeat the prior search.
  1593. X         * If the search fails, abort any pending operator.
  1594. X         */
  1595. X        if (!dosearch(
  1596. X                (c == '/') ? FORWARD : BACKWARD,
  1597. X                (*s == NUL) ? NULL : s
  1598. X                 ))
  1599. X            CLEAROP;
  1600. X        break;
  1601. X
  1602. X    case 'n':
  1603. X        mtype = MCHAR;
  1604. X        mincl = FALSE;
  1605. X        set_want_col = TRUE;
  1606. X        if (!repsearch(0))
  1607. X            CLEAROP;
  1608. X        break;
  1609. X
  1610. X    case 'N':
  1611. X        mtype = MCHAR;
  1612. X        mincl = FALSE;
  1613. X        set_want_col = TRUE;
  1614. X        if (!repsearch(1))
  1615. X            CLEAROP;
  1616. X        break;
  1617. X
  1618. X    /*
  1619. X     * Character searches
  1620. X     */
  1621. X    case 'T':
  1622. X        dir = BACKWARD;
  1623. X        /* fall through */
  1624. X
  1625. X    case 't':
  1626. X        type = 1;
  1627. X        goto docsearch;
  1628. X
  1629. X    case 'F':
  1630. X        dir = BACKWARD;
  1631. X        /* fall through */
  1632. X
  1633. X    case 'f':
  1634. X    docsearch:
  1635. X        mtype = MCHAR;
  1636. X        mincl = TRUE;
  1637. X        set_want_col = TRUE;
  1638. X        if ((nchar = vgetc()) == ESC)    /* search char */
  1639. X            break;
  1640. X
  1641. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  1642. X            if (!searchc(nchar, dir, type)) {
  1643. X                CLEAROP;
  1644. X                beep();
  1645. X            }
  1646. X        }
  1647. X        break;
  1648. X
  1649. X    case ',':
  1650. X        flag = 1;
  1651. X        /* fall through */
  1652. X
  1653. X    case ';':
  1654. X        mtype = MCHAR;
  1655. X        mincl = TRUE;
  1656. X        set_want_col = TRUE;
  1657. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  1658. X            if (!crepsearch(flag)) {
  1659. X                CLEAROP;
  1660. X                beep();
  1661. X            }
  1662. X        }
  1663. X        break;
  1664. X
  1665. X    case '(':            /* sentence searches */
  1666. X        dir = BACKWARD;
  1667. X        /* fall through */
  1668. X
  1669. X    case ')':
  1670. X        mtype = MCHAR;
  1671. X        mincl = FALSE;
  1672. X        set_want_col = TRUE;
  1673. X        if (findsent(dir) == NULL) {
  1674. X            beep();
  1675. X            CLEAROP;
  1676. X        }
  1677. X        break;
  1678. X
  1679. X    case '{':            /* paragraph searches */
  1680. X        dir = BACKWARD;
  1681. X        /* fall through */
  1682. X
  1683. X    case '}':
  1684. X        mtype = MCHAR;
  1685. X        mincl = FALSE;
  1686. X        set_want_col = TRUE;
  1687. X        if (!findpara(dir)) {
  1688. X            beep();
  1689. X            CLEAROP;
  1690. X        }
  1691. X        break;
  1692. X
  1693. X    case '[':            /* function searches */
  1694. X        dir = BACKWARD;
  1695. X        /* fall through */
  1696. X
  1697. X    case ']':
  1698. X        mtype = MLINE;
  1699. X        set_want_col = TRUE;
  1700. X        if (vgetc() != c) {
  1701. X            beep();
  1702. X            CLEAROP;
  1703. X            break;
  1704. X        }
  1705. X
  1706. X        if (!findfunc(dir)) {
  1707. X            beep();
  1708. X            CLEAROP;
  1709. X        }
  1710. X        break;
  1711. X
  1712. X    case '%':
  1713. X        mtype = MCHAR;
  1714. X        mincl = TRUE;
  1715. X        {
  1716. X            LPTR    *pos;
  1717. X
  1718. X            if ((pos = showmatch()) == NULL) {
  1719. X                beep();
  1720. X                CLEAROP;
  1721. X            } else {
  1722. X                setpcmark();
  1723. X                *Curschar = *pos;
  1724. X                set_want_col = TRUE;
  1725. X            }
  1726. X        }
  1727. X        break;
  1728. X        
  1729. X    /*
  1730. X     * Edits
  1731. X     */
  1732. X    case '.':        /* repeat last change (usually) */
  1733. X        /*
  1734. X         * If a delete is in effect, we let '.' help out the same
  1735. X         * way that '_' helps for some line operations. It's like
  1736. X         * an 'l', but subtracts one from the count and is inclusive.
  1737. X         */
  1738. X        if (operator == DELETE || operator == CHANGE) {
  1739. X            if (Prenum != 0) {
  1740. X                n = DEFAULT1(Prenum) - 1;
  1741. X                while (n--)
  1742. X                    if (! oneright())
  1743. X                        break;
  1744. X            }
  1745. X            mtype = MCHAR;
  1746. X            mincl = TRUE;
  1747. X        } else {            /* a normal 'redo' */
  1748. X            CLEAROP;
  1749. X            stuffin(Redobuff);
  1750. X        }
  1751. X        break;
  1752. X
  1753. X    case 'u':
  1754. X    case K_UNDO:
  1755. X        CLEAROP;
  1756. X        u_undo();
  1757. X        break;
  1758. X
  1759. X    case 'U':
  1760. X        CLEAROP;
  1761. X        u_lundo();
  1762. X        break;
  1763. X
  1764. X    case 'x':
  1765. X        CLEAROP;
  1766. X        if (lineempty())    /* can't do it on a blank line */
  1767. X            beep();
  1768. X        if (Prenum)
  1769. X            stuffnum(Prenum);
  1770. X        stuffin("d.");
  1771. X        break;
  1772. X
  1773. X    case 'X':
  1774. X        CLEAROP;
  1775. X        if (!oneleft())
  1776. X            beep();
  1777. X        else {
  1778. X            strcpy(Redobuff, "X");
  1779. X            u_saveline();
  1780. X            delchar(TRUE);
  1781. X            updateline();
  1782. X        }
  1783. X        break;
  1784. X
  1785. X    case 'r':
  1786. X        CLEAROP;
  1787. X        if (lineempty()) {    /* Nothing to replace */
  1788. X            beep();
  1789. X            break;
  1790. X        }
  1791. X        if ((nchar = vgetc()) == ESC)
  1792. X            break;
  1793. X
  1794. X        if (nchar==CR || nchar==NL) {
  1795. X            stuffin("R\n\033");
  1796. X            break;
  1797. X        }
  1798. X
  1799. X        if (nchar & 0x80) {
  1800. X            beep();
  1801. X            break;
  1802. X        }
  1803. X        u_saveline();
  1804. X
  1805. X        /* Change current character. */
  1806. X        pchar(Curschar, nchar);
  1807. X
  1808. X        /* Save stuff necessary to redo it */
  1809. X        sprintf(Redobuff, "r%c", nchar);
  1810. X
  1811. X        CHANGED;
  1812. X        updateline();
  1813. X        break;
  1814. X
  1815. X    case '~':        /* swap case */
  1816. X        if (!P(P_TO)) {
  1817. X            CLEAROP;
  1818. X            if (lineempty()) {
  1819. X                beep();
  1820. X                break;
  1821. X            }
  1822. X            c = gchar(Curschar);
  1823. X
  1824. X            if (isalpha(c)) {
  1825. X                if (islower(c))
  1826. X                    c = toupper(c);
  1827. X                else
  1828. X                    c = tolower(c);
  1829. X            }
  1830. X            u_saveline();
  1831. X
  1832. X            pchar(Curschar, c);    /* Change current character. */
  1833. X            oneright();
  1834. X
  1835. X            strcpy(Redobuff, "~");
  1836. X
  1837. X            CHANGED;
  1838. X            updateline();
  1839. X        }
  1840. X#ifdef    TILDEOP
  1841. X        else {
  1842. X            if (operator == TILDE)        /* handle '~~' */
  1843. X                goto lineop;
  1844. X            if (Prenum != 0)
  1845. X                opnum = Prenum;
  1846. X            startop = *Curschar;
  1847. X            operator = TILDE;
  1848. X        }
  1849. X#endif
  1850. X
  1851. X        break;
  1852. X
  1853. X    case 'J':
  1854. X        CLEAROP;
  1855. X
  1856. X        u_save(Curschar->linep->prev, Curschar->linep->next->next);
  1857. X
  1858. X        if (!dojoin(TRUE))
  1859. X            beep();
  1860. X
  1861. X        strcpy(Redobuff, "J");
  1862. X        updatescreen();
  1863. X        break;
  1864. X
  1865. X    /*
  1866. X     * Inserts
  1867. X     */
  1868. X    case 'A':
  1869. X        set_want_col = TRUE;
  1870. X        while (oneright())
  1871. X            ;
  1872. X        /* fall through */
  1873. X
  1874. X    case 'a':
  1875. X        CLEAROP;
  1876. X        /* Works just like an 'i'nsert on the next character. */
  1877. X        if (!lineempty())
  1878. X            inc(Curschar);
  1879. X        u_saveline();
  1880. X        startinsert(mkstr(c), FALSE);
  1881. X        break;
  1882. X
  1883. X    case 'I':
  1884. X        beginline(TRUE);
  1885. X        /* fall through */
  1886. X
  1887. X    case 'i':
  1888. X    case K_INSERT:
  1889. X        CLEAROP;
  1890. X        u_saveline();
  1891. X        startinsert(mkstr(c), FALSE);
  1892. X        break;
  1893. X
  1894. X    case 'o':
  1895. X        CLEAROP;
  1896. X        u_save(Curschar->linep, Curschar->linep->next);
  1897. X        opencmd(FORWARD, TRUE);
  1898. X        startinsert("o", TRUE);
  1899. X        break;
  1900. X
  1901. X    case 'O':
  1902. X        CLEAROP;
  1903. X        u_save(Curschar->linep->prev, Curschar->linep);
  1904. X        opencmd(BACKWARD, TRUE);
  1905. X        startinsert("O", TRUE);
  1906. X        break;
  1907. X
  1908. X    case 'R':
  1909. X        CLEAROP;
  1910. X        u_saveline();
  1911. X        startinsert("R", FALSE);
  1912. X        break;
  1913. X
  1914. X    /*
  1915. X     * Operators
  1916. X     */
  1917. X    case 'd':
  1918. X        if (operator == DELETE)        /* handle 'dd' */
  1919. X            goto lineop;
  1920. X        if (Prenum != 0)
  1921. X            opnum = Prenum;
  1922. X        startop = *Curschar;
  1923. X        operator = DELETE;
  1924. X        break;
  1925. X
  1926. X    case 'c':
  1927. X        if (operator == CHANGE)        /* handle 'cc' */
  1928. X            goto lineop;
  1929. X        if (Prenum != 0)
  1930. X            opnum = Prenum;
  1931. X        startop = *Curschar;
  1932. X        operator = CHANGE;
  1933. X        break;
  1934. X
  1935. X    case 'y':
  1936. X        if (operator == YANK)        /* handle 'yy' */
  1937. X            goto lineop;
  1938. X        if (Prenum != 0)
  1939. X            opnum = Prenum;
  1940. X        startop = *Curschar;
  1941. X        operator = YANK;
  1942. X        break;
  1943. X
  1944. X    case '>':
  1945. X        if (operator == RSHIFT)        /* handle >> */
  1946. X            goto lineop;
  1947. X        if (Prenum != 0)
  1948. X            opnum = Prenum;
  1949. X        startop = *Curschar;
  1950. X        operator = RSHIFT;
  1951. X        break;
  1952. X
  1953. X    case '<':
  1954. X        if (operator == LSHIFT)        /* handle << */
  1955. X            goto lineop;
  1956. X        if (Prenum != 0)
  1957. X            opnum = Prenum;
  1958. X        startop = *Curschar;    /* save current position */
  1959. X        operator = LSHIFT;
  1960. X        break;
  1961. X
  1962. X    case '!':
  1963. X        if (operator == FILTER)        /* handle '!!' */
  1964. X            goto lineop;
  1965. X        if (Prenum != 0)
  1966. X            opnum = Prenum;
  1967. X        startop = *Curschar;
  1968. X        operator = FILTER;
  1969. X        break;
  1970. X
  1971. X    case 'p':
  1972. X        doput(FORWARD);
  1973. X        break;
  1974. X
  1975. X    case 'P':
  1976. X        doput(BACKWARD);
  1977. X        break;
  1978. X
  1979. X    /*
  1980. X     * Abbreviations
  1981. X     */
  1982. X    case 'D':
  1983. X        stuffin("d$");
  1984. X        break;
  1985. X
  1986. X    case 'Y':
  1987. X        if (Prenum)
  1988. X            stuffnum(Prenum);
  1989. X        stuffin("yy");
  1990. X        break;
  1991. X
  1992. X    case 'C':
  1993. X        stuffin("c$");
  1994. X        break;
  1995. X
  1996. X    case 's':                /* substitute characters */
  1997. X        if (Prenum)
  1998. X            stuffnum(Prenum);
  1999. X        stuffin("c.");
  2000. X        break;
  2001. X
  2002. X    /*
  2003. X     * Marks
  2004. X     */
  2005. X    case 'm':
  2006. X        CLEAROP;
  2007. X        if (!setmark(vgetc()))
  2008. X            beep();
  2009. X        break;
  2010. X
  2011. X    case '\'':
  2012. X        flag = TRUE;
  2013. X        /* fall through */
  2014. X
  2015. X    case '`':
  2016. X        {
  2017. X            LPTR    mtmp, *mark;
  2018. X
  2019. X            mark = getmark(vgetc());
  2020. X            if (mark == NULL) {
  2021. X                beep();
  2022. X                CLEAROP;
  2023. X            } else {
  2024. X                mtmp = *mark;
  2025. X                setpcmark();
  2026. X                *Curschar = mtmp;
  2027. X                if (flag)
  2028. X                    beginline(TRUE);
  2029. X            }
  2030. X            mtype = flag ? MLINE : MCHAR;
  2031. X            mincl = FALSE;        /* ignored if not MCHAR */
  2032. X            set_want_col = TRUE;
  2033. X        }
  2034. X        break;
  2035. X
  2036. X    default:
  2037. X        CLEAROP;
  2038. X        beep();
  2039. X        break;
  2040. X    }
  2041. X
  2042. X    /*
  2043. X     * If an operation is pending, handle it...
  2044. X     */
  2045. X    if (finish_op) {        /* we just finished an operator */
  2046. X        if (operator == NOP)    /* ... but it was cancelled */
  2047. X            return;
  2048. X
  2049. X        switch (operator) {
  2050. X
  2051. X        case LSHIFT:
  2052. X        case RSHIFT:
  2053. X            doshift(operator, c, nchar, Prenum);
  2054. X            break;
  2055. X
  2056. X        case DELETE:
  2057. X            dodelete(c, nchar, Prenum);
  2058. X            break;
  2059. X
  2060. X        case YANK:
  2061. X            (void) doyank();    /* no redo on yank... */
  2062. X            break;
  2063. X
  2064. X        case CHANGE:
  2065. X            dochange(c, nchar, Prenum);
  2066. X            break;
  2067. X
  2068. X        case FILTER:
  2069. X            dofilter(c, nchar, Prenum);
  2070. X            break;
  2071. X
  2072. X#ifdef    TILDEOP
  2073. X        case TILDE:
  2074. X            dotilde(c, nchar, Prenum);
  2075. X            break;
  2076. X#endif
  2077. X
  2078. X        default:
  2079. X            beep();
  2080. X        }
  2081. X        operator = NOP;
  2082. X    }
  2083. X}
  2084. !EOR!
  2085. echo extracting - ops.c
  2086. sed 's/^X//' > ops.c << '!EOR!'
  2087. X/* $Header: /nw/tony/src/stevie/src/RCS/ops.c,v 1.5 89/08/06 09:50:42 tony Exp $
  2088. X *
  2089. X * Contains routines that implement the operators in vi. Everything in this
  2090. X * file is called only from code in normal.c
  2091. X */
  2092. X
  2093. X#include "stevie.h"
  2094. X#include "ops.h"
  2095. X
  2096. X/*
  2097. X * doshift - handle a shift operation
  2098. X */
  2099. Xvoid
  2100. Xdoshift(op, c1, c2, num)
  2101. Xint    op;
  2102. Xchar    c1, c2;
  2103. Xint    num;
  2104. X{
  2105. X    void    tabinout();
  2106. X    LPTR    top, bot;
  2107. X    int    nlines;
  2108. X    char    opchar;
  2109. X
  2110. X    top = startop;
  2111. X    bot = *Curschar;
  2112. X
  2113. X    if (lt(&bot, &top))
  2114. X        pswap(&top, &bot);
  2115. X
  2116. X    u_save(top.linep->prev, bot.linep->next);
  2117. X
  2118. X    nlines = cntllines(&top, &bot);
  2119. X    *Curschar = top;
  2120. X    tabinout((op == LSHIFT), nlines);
  2121. X
  2122. X    /* construct Redo buff */
  2123. X    opchar = (op == LSHIFT) ? '<' : '>';
  2124. X    if (num != 0)
  2125. X        sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2);
  2126. X    else
  2127. X        sprintf(Redobuff, "%c%c%c", opchar, c1, c2);
  2128. X
  2129. X    /*
  2130. X     * The cursor position afterward is the prior of the two positions.
  2131. X     */
  2132. X    *Curschar = top;
  2133. X
  2134. X    /*
  2135. X     * If we were on the last char of a line that got shifted left,
  2136. X     * then move left one so we aren't beyond the end of the line
  2137. X     */
  2138. X    if (gchar(Curschar) == NUL && Curschar->index > 0)
  2139. X        Curschar->index--;
  2140. X
  2141. X    updatescreen();
  2142. X
  2143. X    if (nlines > P(P_RP))
  2144. X        smsg("%d lines %ced", nlines, opchar);
  2145. X}
  2146. X
  2147. X/*
  2148. X * dodelete - handle a delete operation
  2149. X */
  2150. Xvoid
  2151. Xdodelete(c1, c2, num)
  2152. Xchar    c1, c2;
  2153. Xint    num;
  2154. X{
  2155. X    LPTR    top, bot;
  2156. X    int    nlines;
  2157. X    int    botindex;
  2158. X    register int    n;
  2159. X
  2160. X    /*
  2161. X     * Do a yank of whatever we're about to delete. If there's too much
  2162. X     * stuff to fit in the yank buffer, then get a confirmation before
  2163. X     * doing the delete. This is crude, but simple. And it avoids doing
  2164. X     * a delete of something we can't put back if we want.
  2165. X     */
  2166. X    if (!doyank()) {
  2167. X        msg("yank buffer exceeded: press <y> to delete anyway");
  2168. X        if (vgetc() != 'y') {
  2169. X            msg("delete aborted");
  2170. X            *Curschar = startop;
  2171. X            return;
  2172. X        }
  2173. X    }
  2174. X
  2175. X    top = startop;
  2176. X    bot = *Curschar;
  2177. X
  2178. X    if (lt(&bot, &top))
  2179. X        pswap(&top, &bot);
  2180. X
  2181. X    u_save(top.linep->prev, bot.linep->next);
  2182. X    /* Don't leave even the potential for orphan marks */
  2183. X    clrmark (top.linep);
  2184. X
  2185. X    nlines = cntllines(&top, &bot);
  2186. X    *Curschar = top;
  2187. X    cursupdate();
  2188. X
  2189. X    if (mtype == MLINE) {
  2190. X        delline(nlines, TRUE);
  2191. X    } else {
  2192. X        botindex = -1;
  2193. X        if (!mincl) {
  2194. X            botindex = bot.index;    /* where it WAS */
  2195. X            if (bot.index != 0)
  2196. X                dec(&bot);
  2197. X        }
  2198. X
  2199. X        if (top.linep == bot.linep) {        /* del. within line */
  2200. X            n = bot.index - top.index + 1;
  2201. X            while (n--)
  2202. X                if (!delchar(TRUE))
  2203. X                    break;
  2204. X        } else {                /* del. between lines */
  2205. X            n = Curschar->index;
  2206. X            while (Curschar->index >= n)
  2207. X                if (!delchar(TRUE))
  2208. X                    break;
  2209. X
  2210. X            top = *Curschar;
  2211. X            *Curschar = *nextline(Curschar);
  2212. X            delline(nlines-2, TRUE);
  2213. X            Curschar->index = 0;
  2214. X            n = bot.index + 1;
  2215. X            while (n-- && botindex)
  2216. X                if (!delchar(TRUE))
  2217. X                    break;
  2218. X            *Curschar = top;
  2219. X            (void) dojoin(FALSE);
  2220. X            oneright();    /* we got bumped left up above */
  2221. X        }
  2222. X    }
  2223. X
  2224. X    /* construct Redo buff */
  2225. X    if (num != 0)
  2226. X        sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  2227. X    else
  2228. X        sprintf(Redobuff, "d%c%c", c1, c2);
  2229. X
  2230. X    if (mtype == MCHAR && nlines == 1)
  2231. X        updateline();
  2232. X    else
  2233. X        updatescreen();
  2234. X
  2235. X    if (nlines > P(P_RP))
  2236. X        smsg("%d fewer lines", nlines);
  2237. X}
  2238. X
  2239. X/*
  2240. X * dofilter - handle a filter operation
  2241. X */
  2242. X
  2243. X#define    ITMP    "viXXXXXX"
  2244. X#define    OTMP    "voXXXXXX"
  2245. X
  2246. Xstatic    char    itmp[32];
  2247. Xstatic    char    otmp[32];
  2248. X
  2249. X
  2250. X/*
  2251. X * dofilter - filter lines through a command given by the user
  2252. X *
  2253. X * We use temp files and the system() routine here. This would normally
  2254. X * be done using pipes on a UNIX machine, but this is more portable to
  2255. X * the machines we usually run on. The system() routine needs to be able
  2256. X * to deal with redirection somehow, and should handle things like looking
  2257. X * at the PATH env. variable, and adding reasonable extensions to the
  2258. X * command name given by the user. All reasonable versions of system()
  2259. X * do this.
  2260. X */
  2261. Xvoid
  2262. Xdofilter(c1, c2, num)
  2263. Xchar    c1, c2;
  2264. Xint    num;
  2265. X{
  2266. X    char    *mktemp();
  2267. X    static    char    *lastcmd = NULL;/* the last thing we did */
  2268. X    char    *buff;            /* cmd buffer from getcmdln() */
  2269. X    char    cmdln[200];        /* filtering command line */
  2270. X    LPTR    top, bot;
  2271. X    int    nlines;
  2272. X
  2273. X    top = startop;
  2274. X    bot = *Curschar;
  2275. X
  2276. X    buff = getcmdln('!');
  2277. X
  2278. X    if (buff == NULL)    /* user backed out of the command prompt */
  2279. X        return;
  2280. X
  2281. X    if (*buff == '!') {        /* use the 'last' command */
  2282. X        if (lastcmd == NULL) {
  2283. X            emsg("No previous command");
  2284. X            return;
  2285. X        }
  2286. X        buff = lastcmd;
  2287. X    }
  2288. X
  2289. X    /*
  2290. X     * Remember the current command
  2291. X     */
  2292. X    if (lastcmd != NULL)
  2293. X        free(lastcmd);
  2294. X    lastcmd = strsave(buff);
  2295. X
  2296. X    if (lt(&bot, &top))
  2297. X        pswap(&top, &bot);
  2298. X
  2299. X    u_save(top.linep->prev, bot.linep->next);
  2300. X
  2301. X    nlines = cntllines(&top, &bot);
  2302. X    *Curschar = top;
  2303. X    cursupdate();
  2304. X
  2305. X    /*
  2306. X     * 1. Form temp file names
  2307. X     * 2. Write the lines to a temp file
  2308. X     * 3. Run the filter command on the temp file
  2309. X     * 4. Read the output of the command into the buffer
  2310. X     * 5. Delete the original lines to be filtered
  2311. X     * 6. Remove the temp files
  2312. X     */
  2313. X
  2314. X#ifdef    TMPDIR
  2315. X    strcpy(itmp, TMPDIR);
  2316. X    strcpy(otmp, TMPDIR);
  2317. X#else
  2318. X    itmp[0] = otmp[0] = NUL;
  2319. X#endif
  2320. X    strcat(itmp, ITMP);
  2321. X    strcat(otmp, OTMP);
  2322. X
  2323. X    if (mktemp(itmp) == NULL || mktemp(otmp) == NULL) {
  2324. X        emsg("Can't get temp file names");
  2325. X        return;
  2326. X    }
  2327. X
  2328. X    if (!writeit(itmp, &top, &bot)) {
  2329. X        emsg("Can't create input temp file");
  2330. X        return;
  2331. X    }
  2332. X
  2333. X    sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp);
  2334. X
  2335. X    if (system(cmdln) != 0) {
  2336. X        emsg("Filter command failed");
  2337. X        remove(ITMP);
  2338. X        return;
  2339. X    }
  2340. X
  2341. X    if (readfile(otmp, &bot, TRUE)) {
  2342. X        emsg("Can't read filter output");
  2343. X        return;
  2344. X    }
  2345. X
  2346. X    delline(nlines, TRUE);
  2347. X
  2348. X    remove(itmp);
  2349. X    remove(otmp);
  2350. X
  2351. X    /* construct Redo buff */
  2352. X    if (num != 0)
  2353. X        sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  2354. X    else
  2355. X        sprintf(Redobuff, "d%c%c", c1, c2);
  2356. X
  2357. X    updatescreen();
  2358. X
  2359. X    if (nlines > P(P_RP))
  2360. X        smsg("%d lines filtered", nlines);
  2361. X}
  2362. X
  2363. X#ifdef    TILDEOP
  2364. Xvoid
  2365. Xdotilde(c1, c2, num)
  2366. Xchar    c1, c2;
  2367. Xint    num;
  2368. X{
  2369. X    LPTR    top, bot;
  2370. X    register char    c;
  2371. X
  2372. X    /* construct Redo buff */
  2373. X    if (num != 0)
  2374. X        sprintf(Redobuff, "~%d%c%c", num, c1, c2);
  2375. X    else
  2376. X        sprintf(Redobuff, "~%c%c", c1, c2);
  2377. X
  2378. X    top = startop;
  2379. X    bot = *Curschar;
  2380. X
  2381. X    if (lt(&bot, &top))
  2382. X        pswap(&top, &bot);
  2383. X
  2384. X    u_save(top.linep->prev, bot.linep->next);
  2385. X
  2386. X    if (mtype == MLINE) {
  2387. X        top.index = 0;
  2388. X        bot.index = strlen(bot.linep->s);
  2389. X    } else {
  2390. X        if (!mincl) {
  2391. X            if (bot.index)
  2392. X                bot.index--;
  2393. X        }
  2394. X    }
  2395. X
  2396. X    for (; ltoreq(&top, &bot) ;inc(&top)) {
  2397. X        /*
  2398. X         * Swap case through the range
  2399. X         */
  2400. X        c = gchar(&top);
  2401. X        if (isalpha(c)) {
  2402. X            if (islower(c))
  2403. X                c = toupper(c);
  2404. X            else
  2405. X                c = tolower(c);
  2406. X
  2407. X            pchar(&top, c);        /* Change current character. */
  2408. X            CHANGED;
  2409. X        }
  2410. X    }
  2411. X    *Curschar = startop;
  2412. X    updatescreen();
  2413. X}
  2414. X#endif
  2415. X
  2416. X/*
  2417. X * dochange - handle a change operation
  2418. X */
  2419. Xvoid
  2420. Xdochange(c1, c2, num)
  2421. Xchar    c1, c2;
  2422. Xint    num;
  2423. X{
  2424. X    char    sbuf[16];
  2425. X    bool_t    doappend;    /* true if we should do append, not insert */
  2426. X    bool_t    at_eof;        /* changing through the end of file */
  2427. X    LPTR    top, bot;
  2428. X
  2429. X    top = startop;
  2430. X    bot = *Curschar;
  2431. X
  2432. X    if (lt(&bot, &top))
  2433. X        pswap(&top, &bot);
  2434. X
  2435. X    doappend = endofline(&bot);
  2436. X    at_eof = (bot.linep->next == Fileend->linep);
  2437. X
  2438. X    dodelete(c1, c2, num);
  2439. X
  2440. X    if (mtype == MLINE) {
  2441. X        /*
  2442. X         * If we made a change through the last line of the file,
  2443. X         * then the cursor got backed up, and we need to open a
  2444. X         * new line forward, otherwise we go backward.
  2445. X         */
  2446. X        if (at_eof)
  2447. X            opencmd(FORWARD, FALSE);
  2448. X        else
  2449. X            opencmd(BACKWARD, FALSE);
  2450. X    } else {
  2451. X        if (doappend && !lineempty())
  2452. X            inc(Curschar);
  2453. X    }
  2454. X
  2455. X    if (num)
  2456. X        sprintf(sbuf, "c%d%c%c", num, c1, c2);
  2457. X    else
  2458. X        sprintf(sbuf, "c%c%c", c1, c2);
  2459. X
  2460. X    startinsert(sbuf, mtype == MLINE);
  2461. X}
  2462. X
  2463. X#ifndef    YBSIZE
  2464. X#define    YBSIZE    4096
  2465. X#endif
  2466. X
  2467. Xstatic    char    ybuf[YBSIZE];
  2468. Xstatic    int    ybtype = MBAD;
  2469. X
  2470. Xbool_t
  2471. Xdoyank()
  2472. X{
  2473. X    LPTR    top, bot;
  2474. X    char    *yptr = ybuf;
  2475. X    char    *ybend = &ybuf[YBSIZE-1];
  2476. X    int    nlines;
  2477. X
  2478. X    top = startop;
  2479. X    bot = *Curschar;
  2480. X
  2481. X    if (lt(&bot, &top))
  2482. X        pswap(&top, &bot);
  2483. X
  2484. X    nlines = cntllines(&top, &bot);
  2485. X
  2486. X    ybtype = mtype;            /* set the yank buffer type */
  2487. X
  2488. X    if (mtype == MLINE) {
  2489. X        top.index = 0;
  2490. X        bot.index = strlen(bot.linep->s);
  2491. X        /*
  2492. X         * The following statement checks for the special case of
  2493. X         * yanking a blank line at the beginning of the file. If
  2494. X         * not handled right, we yank an extra char (a newline).
  2495. X         */
  2496. X        if (dec(&bot) == -1) {
  2497. X            ybuf[0] = NUL;
  2498. X            if (operator == YANK)
  2499. X                *Curschar = startop;
  2500. X            return TRUE;
  2501. X        }
  2502. X    } else {
  2503. X        if (!mincl) {
  2504. X            if (bot.index)
  2505. X                bot.index--;
  2506. X            else        /* already first column */
  2507. X                bot = *( prevchar (&bot));
  2508. X        }
  2509. X    }
  2510. X
  2511. X    for (; ltoreq(&top, &bot) ;inc(&top)) {
  2512. X        *yptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  2513. X        if (++yptr >= ybend) {
  2514. X            msg("yank too big for buffer");
  2515. X            ybtype = MBAD;
  2516. X            return FALSE;
  2517. X        }
  2518. X    }
  2519. X
  2520. X    *yptr = NUL;
  2521. X
  2522. X    if (operator == YANK) {    /* restore Curschar if really doing yank */
  2523. X        *Curschar = startop;
  2524. X
  2525. X        if (nlines > P(P_RP))
  2526. X            smsg("%d lines yanked", nlines);
  2527. X    }
  2528. X
  2529. X    return TRUE;
  2530. X}
  2531. X
  2532. X/*
  2533. X * doput(dir)
  2534. X *
  2535. X * Put the yank buffer at the current location, using the direction given
  2536. X * by 'dir'.
  2537. X */
  2538. Xvoid
  2539. Xdoput(dir)
  2540. Xint    dir;
  2541. X{
  2542. X    void    inslines();
  2543. X
  2544. X    if (ybtype == MBAD) {
  2545. X        beep();
  2546. X        return;
  2547. X    }
  2548. X    
  2549. X    u_saveline();
  2550. X
  2551. X    if (ybtype == MLINE)
  2552. X        inslines(Curschar->linep, dir, ybuf);
  2553. X    else {
  2554. X        /*
  2555. X         * If we did a character-oriented yank, and the buffer
  2556. X         * contains multiple lines, the situation is more complex.
  2557. X         * For the moment, we punt, and pretend the user did a
  2558. X         * line-oriented yank. This doesn't actually happen that
  2559. X         * often.
  2560. X         */
  2561. X        if (strchr(ybuf, NL) != NULL)
  2562. X            inslines(Curschar->linep, dir, ybuf);
  2563. X        else {
  2564. X            char    *s;
  2565. X            int    len;
  2566. X
  2567. X            len = strlen(Curschar->linep->s) + strlen(ybuf) + 1;
  2568. X            s = alloc((unsigned) len);
  2569. X            if (!s)  return;
  2570. X            strcpy(s, Curschar->linep->s);
  2571. X            if (dir == FORWARD)
  2572. X                Curschar->index++;
  2573. X            strcpy(s + Curschar->index, ybuf);
  2574. X            strcat(s, &Curschar->linep->s[Curschar->index]);
  2575. X            free(Curschar->linep->s);
  2576. X            Curschar->linep->s = s;
  2577. X            Curschar->linep->size = len;
  2578. X            updateline();
  2579. X        }
  2580. X    }
  2581. X
  2582. X    CHANGED;
  2583. X}
  2584. X
  2585. Xbool_t
  2586. Xdojoin(join_cmd)
  2587. Xbool_t    join_cmd;        /* handling a real "join" command? */
  2588. X{
  2589. X    int    scol;        /* save cursor column */
  2590. X    int    size;        /* size of the joined line */
  2591. X
  2592. X    if (nextline(Curschar) == NULL)        /* on last line */
  2593. X        return FALSE;
  2594. X
  2595. X    if (!canincrease(size = strlen(Curschar->linep->next->s)))
  2596. X        return FALSE;
  2597. X
  2598. X    while (oneright())            /* to end of line */
  2599. X        ;
  2600. X
  2601. X    strcat(Curschar->linep->s, Curschar->linep->next->s);
  2602. X
  2603. X    /*
  2604. X     * Delete the following line. To do this we move the cursor
  2605. X     * there briefly, and then move it back. Don't back up if the
  2606. X     * delete made us the last line.
  2607. X     */
  2608. X    Curschar->linep = Curschar->linep->next;
  2609. X    scol = Curschar->index;
  2610. X
  2611. X    if (nextline(Curschar) != NULL) {
  2612. X        delline(1, TRUE);
  2613. X        Curschar->linep = Curschar->linep->prev;
  2614. X    } else
  2615. X        delline(1, TRUE);
  2616. X
  2617. X    Curschar->index = scol;
  2618. X
  2619. X    if (join_cmd)
  2620. X        oneright();    /* go to first char. of joined line */
  2621. X
  2622. X    if (join_cmd && size != 0) {
  2623. X        /*
  2624. X         * Delete leading white space on the joined line
  2625. X         * and insert a single space.
  2626. X         */
  2627. X        while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB)
  2628. X            delchar(TRUE);
  2629. X        inschar(' ');
  2630. X    }
  2631. X
  2632. X    return TRUE;
  2633. X}
  2634. X
  2635. Xvoid
  2636. Xstartinsert(initstr, startln)
  2637. Xchar    *initstr;
  2638. Xint    startln;    /* if set, insert point really at start of line */
  2639. X{
  2640. X    register char    *p, c;
  2641. X
  2642. X    *Insstart = *Curschar;
  2643. X    if (startln)
  2644. X        Insstart->index = 0;
  2645. X    Ninsert = 0;
  2646. X    Insptr = Insbuff;
  2647. X    for (p=initstr; (c=(*p++))!='\0'; )
  2648. X        *Insptr++ = c;
  2649. X
  2650. X    if (*initstr == 'R')
  2651. X        State = REPLACE;
  2652. X    else
  2653. X        State = INSERT;
  2654. X
  2655. X    if (P(P_MO))
  2656. X        msg((State == INSERT) ? "Insert Mode" : "Replace Mode");
  2657. X}
  2658. X/*
  2659. X * tabinout(inout,num)
  2660. X *
  2661. X * If inout==0, add a tab to the begining of the next num lines.
  2662. X * If inout==1, delete a tab from the beginning of the next num lines.
  2663. X */
  2664. Xstatic void
  2665. Xtabinout(inout, num)
  2666. Xint    inout;
  2667. Xint    num;
  2668. X{
  2669. X    int    ntodo = num;
  2670. X    LPTR    *p;
  2671. X
  2672. X    beginline(FALSE);
  2673. X    while (ntodo-- > 0) {
  2674. X        beginline(FALSE);
  2675. X        if (inout == 0)
  2676. X            inschar(TAB);
  2677. X        else {
  2678. X            if (gchar(Curschar) == TAB)
  2679. X                delchar(TRUE);
  2680. X        }
  2681. X        if ( ntodo > 0 ) {
  2682. X            if ((p = nextline(Curschar)) != NULL)
  2683. X                *Curschar = *p;
  2684. X            else
  2685. X                break;
  2686. X        }
  2687. X    }
  2688. X}
  2689. X
  2690. X/*
  2691. X * inslines(lp, dir, buf)
  2692. X *
  2693. X * Inserts lines in the file from the given buffer. Lines are inserted
  2694. X * before or after "lp" according to the given direction flag. Newlines
  2695. X * in the buffer result in multiple lines being inserted. The cursor
  2696. X * is left on the first of the inserted lines.
  2697. X */
  2698. Xstatic void
  2699. Xinslines(lp, dir, buf)
  2700. XLINE    *lp;
  2701. Xint    dir;
  2702. Xchar    *buf;
  2703. X{
  2704. X    register char    *cp = buf;
  2705. X    register int    len;
  2706. X    char    *ep;
  2707. X    LINE    *l, *nc = NULL;
  2708. X
  2709. X    if (dir == BACKWARD)
  2710. X        lp = lp->prev;
  2711. X
  2712. X    do {
  2713. X        if ((ep = strchr(cp, NL)) == NULL)
  2714. X            len = strlen(cp);
  2715. X        else
  2716. X            len = ep - cp;
  2717. X
  2718. X        l = newline(len);
  2719. X        if (len != 0)
  2720. X            strncpy(l->s, cp, len);
  2721. X        l->s[len] = NUL;
  2722. X
  2723. X        l->next = lp->next;
  2724. X        l->prev = lp;
  2725. X        lp->next->prev = l;
  2726. X        lp->next = l;
  2727. X
  2728. X        if (nc == NULL)
  2729. X            nc = l;
  2730. X
  2731. X        lp = lp->next;
  2732. X
  2733. X        cp = ep + 1;
  2734. X    } while (ep != NULL);
  2735. X
  2736. X    if (dir == BACKWARD)    /* fix the top line in case we were there */
  2737. X        Filemem->linep = Filetop->linep->next;
  2738. X
  2739. X    renum();
  2740. X
  2741. X    updatescreen();
  2742. X    Curschar->linep = nc;
  2743. X    Curschar->index = 0;
  2744. X}
  2745. !EOR!
  2746.  
  2747.  
  2748.