Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

829 lines
22 KiB

  1. /* $Header: /nw/tony/src/stevie/src/RCS/ops.c,v 1.5 89/08/06 09:50:42 tony Exp $
  2. *
  3. * Contains routines that implement the operators in vi. Everything in this
  4. * file is called only from code in normal.c
  5. */
  6. #include "stevie.h"
  7. #include <io.h>
  8. #include "ops.h"
  9. char *lastcmd = NULL;/* the last thing we did */
  10. static void inslines();
  11. static void tabinout();
  12. /*
  13. * doshift - handle a shift operation
  14. */
  15. void
  16. doshift(op, c1, c2, num)
  17. int op;
  18. char c1, c2;
  19. int num;
  20. {
  21. LNPTR top, bot;
  22. int nlines;
  23. char opchar;
  24. top = startop;
  25. bot = *Curschar;
  26. if (lt(&bot, &top))
  27. pswap(&top, &bot);
  28. u_save(top.linep->prev, bot.linep->next);
  29. nlines = cntllines(&top, &bot);
  30. *Curschar = top;
  31. tabinout((op == LSHIFT), nlines);
  32. /* construct Redo buff */
  33. opchar = (char)((op == LSHIFT) ? '<' : '>');
  34. if (num != 0)
  35. sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2);
  36. else
  37. sprintf(Redobuff, "%c%c%c", opchar, c1, c2);
  38. /*
  39. * The cursor position afterward is the prior of the two positions.
  40. */
  41. *Curschar = top;
  42. /*
  43. * If we were on the last char of a line that got shifted left,
  44. * then move left one so we aren't beyond the end of the line
  45. */
  46. if (gchar(Curschar) == NUL && Curschar->index > 0)
  47. Curschar->index--;
  48. updatescreen();
  49. if (nlines > P(P_RP))
  50. smsg("%d lines %ced", nlines, opchar);
  51. }
  52. /*
  53. * dodelete - handle a delete operation
  54. */
  55. void
  56. dodelete(c1, c2, num)
  57. char c1, c2;
  58. int num;
  59. {
  60. LNPTR top, bot;
  61. int nlines;
  62. register int n;
  63. /*
  64. * Do a yank of whatever we're about to delete. If there's too much
  65. * stuff to fit in the yank buffer, then get a confirmation before
  66. * doing the delete. This is crude, but simple. And it avoids doing
  67. * a delete of something we can't put back if we want.
  68. */
  69. if (!doyank()) {
  70. msg("yank buffer exceeded: press <y> to confirm");
  71. if (vgetc() != 'y') {
  72. msg("delete aborted");
  73. *Curschar = startop;
  74. return;
  75. }
  76. }
  77. top = startop;
  78. bot = *Curschar;
  79. if (lt(&bot, &top))
  80. pswap(&top, &bot);
  81. u_save(top.linep->prev, bot.linep->next);
  82. nlines = cntllines(&top, &bot);
  83. *Curschar = top;
  84. cursupdate();
  85. if (mtype == MLINE) {
  86. delline(nlines, TRUE);
  87. } else {
  88. if (!mincl && bot.index != 0)
  89. dec(&bot);
  90. if (top.linep == bot.linep) { /* del. within line */
  91. n = bot.index - top.index + 1;
  92. while (n--)
  93. if (!delchar(TRUE))
  94. break;
  95. } else { /* del. between lines */
  96. n = Curschar->index;
  97. while (Curschar->index >= n)
  98. if (!delchar(TRUE))
  99. break;
  100. top = *Curschar;
  101. *Curschar = *nextline(Curschar);
  102. delline(nlines-2, TRUE);
  103. Curschar->index = 0;
  104. n = bot.index + 1;
  105. while (n--)
  106. if (!delchar(TRUE))
  107. break;
  108. *Curschar = top;
  109. (void) dojoin(FALSE);
  110. oneright(); /* we got bumped left up above */
  111. }
  112. }
  113. /* construct Redo buff */
  114. if (num != 0)
  115. sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  116. else
  117. sprintf(Redobuff, "d%c%c", c1, c2);
  118. if (mtype == MCHAR && nlines == 1)
  119. updateline();
  120. else
  121. updatescreen();
  122. if (nlines > P(P_RP))
  123. smsg("%d fewer lines", nlines);
  124. }
  125. /*
  126. * dofilter - handle a filter operation
  127. */
  128. #define ITMP "viXXXXXX"
  129. #define OTMP "voXXXXXX"
  130. static char itmp[32];
  131. static char otmp[32];
  132. /*
  133. * dofilter - filter lines through a command given by the user
  134. *
  135. * We use temp files and the system() routine here. This would normally
  136. * be done using pipes on a UNIX machine, but this is more portable to
  137. * the machines we usually run on. The system() routine needs to be able
  138. * to deal with redirection somehow, and should handle things like looking
  139. * at the PATH env. variable, and adding reasonable extensions to the
  140. * command name given by the user. All reasonable versions of system()
  141. * do this.
  142. */
  143. void
  144. dofilter(c1, c2, num)
  145. char c1, c2;
  146. int num;
  147. {
  148. char *buff; /* cmd buffer from getcmdln() */
  149. char cmdln[200]; /* filtering command line */
  150. LNPTR top, bot;
  151. int nlines;
  152. top = startop;
  153. bot = *Curschar;
  154. buff = getcmdln('!');
  155. if (buff == NULL) /* user backed out of the command prompt */
  156. return;
  157. if (*buff == '!') { /* use the 'last' command */
  158. if (lastcmd == NULL) {
  159. emsg("No previous command");
  160. return;
  161. }
  162. buff = lastcmd;
  163. }
  164. /*
  165. * Remember the current command
  166. */
  167. if (lastcmd != NULL)
  168. free(lastcmd);
  169. lastcmd = strsave(buff);
  170. if (lt(&bot, &top))
  171. pswap(&top, &bot);
  172. u_save(top.linep->prev, bot.linep->next);
  173. nlines = cntllines(&top, &bot);
  174. *Curschar = top;
  175. cursupdate();
  176. /*
  177. * 1. Form temp file names
  178. * 2. Write the lines to a temp file
  179. * 3. Run the filter command on the temp file
  180. * 4. Read the output of the command into the buffer
  181. * 5. Delete the original lines to be filtered
  182. * 6. Remove the temp files
  183. */
  184. #ifdef TMPDIR
  185. strcpy(itmp, TMPDIR);
  186. strcpy(otmp, TMPDIR);
  187. #else
  188. itmp[0] = otmp[0] = NUL;
  189. #endif
  190. strcat(itmp, ITMP);
  191. strcat(otmp, OTMP);
  192. if (_mktemp(itmp) == NULL || _mktemp(otmp) == NULL) {
  193. emsg("Can't get temp file names");
  194. return;
  195. }
  196. if (!writeit(itmp, &top, &bot)) {
  197. emsg("Can't create input temp file");
  198. return;
  199. }
  200. sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp);
  201. if (system(cmdln) != 0) {
  202. emsg("Filter command failed");
  203. remove(ITMP);
  204. return;
  205. }
  206. if (readfile(otmp, &bot, TRUE)) {
  207. emsg("Can't read filter output");
  208. return;
  209. }
  210. delline(nlines, TRUE);
  211. remove(itmp);
  212. remove(otmp);
  213. /* construct Redo buff */
  214. if (num != 0)
  215. sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  216. else
  217. sprintf(Redobuff, "d%c%c", c1, c2);
  218. updatescreen();
  219. if (nlines > P(P_RP))
  220. smsg("%d lines filtered", nlines);
  221. }
  222. #ifdef TILDEOP
  223. void
  224. dotilde(c1, c2, num)
  225. char c1, c2;
  226. int num;
  227. {
  228. LNPTR top, bot;
  229. register char c;
  230. /* construct Redo buff */
  231. if (num != 0)
  232. sprintf(Redobuff, "~%d%c%c", num, c1, c2);
  233. else
  234. sprintf(Redobuff, "~%c%c", c1, c2);
  235. top = startop;
  236. bot = *Curschar;
  237. if (lt(&bot, &top))
  238. pswap(&top, &bot);
  239. u_save(top.linep->prev, bot.linep->next);
  240. if (mtype == MLINE) {
  241. top.index = 0;
  242. bot.index = strlen(bot.linep->s);
  243. } else {
  244. if (!mincl) {
  245. if (bot.index)
  246. bot.index--;
  247. }
  248. }
  249. for (; ltoreq(&top, &bot) ;inc(&top)) {
  250. /*
  251. * Swap case through the range
  252. */
  253. c = (char)gchar(&top);
  254. if (isalpha(c)) {
  255. if (islower(c))
  256. c = (char)toupper(c);
  257. else
  258. c = (char)tolower(c);
  259. pchar(&top, c); /* Change current character. */
  260. CHANGED;
  261. }
  262. }
  263. *Curschar = startop;
  264. updatescreen();
  265. }
  266. #endif
  267. /*
  268. * dochange - handle a change operation
  269. */
  270. void
  271. dochange(c1, c2, num)
  272. char c1, c2;
  273. int num;
  274. {
  275. char sbuf[16];
  276. bool_t doappend; /* true if we should do append, not insert */
  277. bool_t at_eof; /* changing through the end of file */
  278. LNPTR top, bot;
  279. top = startop;
  280. bot = *Curschar;
  281. if (lt(&bot, &top))
  282. pswap(&top, &bot);
  283. doappend = endofline(&bot);
  284. at_eof = (bot.linep->next == Fileend->linep);
  285. dodelete(c1, c2, num);
  286. if (mtype == MLINE) {
  287. /*
  288. * If we made a change through the last line of the file,
  289. * then the cursor got backed up, and we need to open a
  290. * new line forward, otherwise we go backward.
  291. */
  292. if (at_eof)
  293. opencmd(FORWARD, FALSE);
  294. else
  295. opencmd(BACKWARD, FALSE);
  296. } else {
  297. if (doappend && !lineempty())
  298. inc(Curschar);
  299. }
  300. if (num)
  301. sprintf(sbuf, "c%d%c%c", num, c1, c2);
  302. else
  303. sprintf(sbuf, "c%c%c", c1, c2);
  304. startinsert(sbuf, mtype == MLINE);
  305. }
  306. /*
  307. * docasechange - handle a case change operation
  308. */
  309. void
  310. docasechange(char c1, char c2, int num, bool_t fToUpper)
  311. {
  312. LNPTR top, bot;
  313. register char c;
  314. /* construct Redo buff */
  315. if (num != 0)
  316. sprintf(Redobuff, "%c%d%c%c", fToUpper ? 'V' : 'v',num, c1, c2);
  317. else
  318. sprintf(Redobuff, "%c%c%c", fToUpper ? 'V' : 'v',c1, c2);
  319. top = startop;
  320. bot = *Curschar;
  321. if (lt(&bot, &top))
  322. pswap(&top, &bot);
  323. u_save(top.linep->prev, bot.linep->next);
  324. if (mtype == MLINE) {
  325. top.index = 0;
  326. bot.index = strlen(bot.linep->s);
  327. } else {
  328. if (!mincl) {
  329. if (bot.index)
  330. bot.index--;
  331. }
  332. }
  333. for (; ltoreq(&top, &bot) ;inc(&top)) {
  334. /*
  335. * change case through the range
  336. */
  337. c = (char)gchar(&top);
  338. if (isalpha(c)) {
  339. c = fToUpper ? (char)toupper(c) : (char)tolower(c);
  340. pchar(&top, c); /* Change current character. */
  341. CHANGED;
  342. }
  343. }
  344. *Curschar = startop;
  345. updatescreen();
  346. }
  347. #define YBSLOP 2048 // yank buffer initial and incr size
  348. char *YankBuffers[27];
  349. int CurrentYBSize[27];
  350. int ybtype[27];
  351. void
  352. inityank()
  353. {
  354. int i;
  355. for(i=0; i<27; i++) {
  356. ybtype[i] = MBAD;
  357. if((YankBuffers[i] = malloc(CurrentYBSize[i] = YBSLOP)) == NULL) {
  358. fprintf(stderr,"Cannot allocate initial yank buffers\n");
  359. windexit(1);
  360. }
  361. YankBuffers[i][0] = '\0';
  362. }
  363. }
  364. void GetBufferIndex(int *index,bool_t *append)
  365. {
  366. int i;
  367. bool_t a = FALSE;
  368. if(namedbuff == -1) {
  369. i = 26;
  370. } else if(islower(namedbuff)) {
  371. i = namedbuff - (int)'a';
  372. } else {
  373. i = namedbuff - (int)'A';
  374. a = TRUE;
  375. }
  376. *index = i;
  377. *append = a;
  378. return;
  379. }
  380. bool_t
  381. doyank()
  382. {
  383. LNPTR top, bot;
  384. char *ybuf;
  385. char *ybstart;
  386. char *ybend;
  387. char *yptr;
  388. int nlines;
  389. int buffindex;
  390. bool_t buffappend;
  391. GetBufferIndex(&buffindex,&buffappend);
  392. namedbuff = -1;
  393. if(!buffappend) {
  394. // the given buffer may have grown huge. Shrink it here. The
  395. // realloc should never fail because the buffer is either being
  396. // shrunk or is staying the same size.
  397. YankBuffers[buffindex] = ralloc(YankBuffers[buffindex],YBSLOP);
  398. CurrentYBSize[buffindex] = YBSLOP;
  399. }
  400. ybuf = YankBuffers[buffindex];
  401. ybstart = ybuf;
  402. yptr = ybstart;
  403. if(buffappend) {
  404. yptr += strlen(ybstart);
  405. }
  406. ybend = &ybuf[CurrentYBSize[buffindex]-1];
  407. top = startop;
  408. bot = *Curschar;
  409. if (lt(&bot, &top))
  410. pswap(&top, &bot);
  411. nlines = cntllines(&top, &bot);
  412. ybtype[buffindex] = mtype; /* set the yank buffer type */
  413. if (mtype == MLINE) {
  414. top.index = 0;
  415. bot.index = strlen(bot.linep->s);
  416. /*
  417. * The following statement checks for the special case of
  418. * yanking a blank line at the beginning of the file. If
  419. * not handled right, we yank an extra char (a newline).
  420. */
  421. if (dec(&bot) == -1) {
  422. *yptr = NUL;
  423. if (operator == YANK)
  424. *Curschar = startop;
  425. return TRUE;
  426. }
  427. } else {
  428. if (!mincl) {
  429. if (bot.index)
  430. bot.index--;
  431. }
  432. }
  433. for (; ltoreq(&top, &bot) ;inc(&top)) {
  434. // See if we've filled the buffer as currently
  435. // allocated. If so, reallocate the buffer and
  436. // update pointers accordingly before we store the
  437. // current character. This is necessary because we will
  438. // always be storing at least one more char (the NUL)
  439. // and probably more.
  440. if(yptr == ybend) {
  441. ybstart = ralloc(ybuf,CurrentYBSize[buffindex] + YBSLOP);
  442. if(ybstart == NULL) {
  443. ybtype[buffindex] = MBAD;
  444. return(FALSE);
  445. }
  446. CurrentYBSize[buffindex] += YBSLOP;
  447. yptr += ybstart - ybuf;
  448. ybend = &ybstart[CurrentYBSize[buffindex] - 1];
  449. ybuf = ybstart;
  450. YankBuffers[buffindex] = ybuf;
  451. }
  452. *yptr++ = (char)((gchar(&top) != NUL) ? gchar(&top) : NL);
  453. }
  454. *yptr = NUL;
  455. if (operator == YANK) { /* restore Curschar if really doing yank */
  456. *Curschar = startop;
  457. if (nlines > P(P_RP))
  458. smsg("%d lines yanked", nlines);
  459. }
  460. return TRUE;
  461. }
  462. /*
  463. * doput(dir)
  464. *
  465. * Put the yank buffer at the current location, using the direction given
  466. * by 'dir'.
  467. */
  468. void
  469. doput(dir)
  470. int dir;
  471. {
  472. int buffindex;
  473. bool_t buffappend;
  474. char *ybuf;
  475. int nb = namedbuff;
  476. GetBufferIndex(&buffindex,&buffappend);
  477. namedbuff = -1;
  478. ybuf = YankBuffers[buffindex];
  479. if (ybtype[buffindex] == MBAD) {
  480. char msgbuff[30];
  481. sprintf(msgbuff,"Nothing in register %c",nb);
  482. emsg(msgbuff);
  483. return;
  484. }
  485. u_saveline();
  486. if (ybtype[buffindex] == MLINE)
  487. inslines(Curschar->linep, dir, ybuf);
  488. else {
  489. /*
  490. * If we did a character-oriented yank, and the buffer
  491. * contains multiple lines, the situation is more complex.
  492. * For the moment, we punt, and pretend the user did a
  493. * line-oriented yank. This doesn't actually happen that
  494. * often.
  495. */
  496. if (strchr(ybuf, NL) != NULL)
  497. inslines(Curschar->linep, dir, ybuf);
  498. else {
  499. char *s;
  500. int len;
  501. len = strlen(Curschar->linep->s) + strlen(ybuf) + 1;
  502. s = alloc((unsigned) len);
  503. strcpy(s, Curschar->linep->s);
  504. if (dir == FORWARD)
  505. Curschar->index++;
  506. strcpy(s + Curschar->index, ybuf);
  507. strcat(s, &Curschar->linep->s[Curschar->index]);
  508. free(Curschar->linep->s);
  509. Curschar->linep->s = s;
  510. Curschar->linep->size = len;
  511. updateline();
  512. }
  513. }
  514. CHANGED;
  515. }
  516. bool_t
  517. dojoin(join_cmd)
  518. bool_t join_cmd; /* handling a real "join" command? */
  519. {
  520. int scol; /* save cursor column */
  521. int size; /* size of the joined line */
  522. if (nextline(Curschar) == NULL) /* on last line */
  523. return FALSE;
  524. if (!canincrease(size = strlen(Curschar->linep->next->s)))
  525. return FALSE;
  526. while (oneright()) /* to end of line */
  527. ;
  528. strcat(Curschar->linep->s, Curschar->linep->next->s);
  529. /*
  530. * Delete the following line. To do this we move the cursor
  531. * there briefly, and then move it back. Don't back up if the
  532. * delete made us the last line.
  533. */
  534. Curschar->linep = Curschar->linep->next;
  535. scol = Curschar->index;
  536. if (nextline(Curschar) != NULL) {
  537. delline(1, TRUE);
  538. Curschar->linep = Curschar->linep->prev;
  539. } else
  540. delline(1, TRUE);
  541. Curschar->index = scol;
  542. if (join_cmd)
  543. oneright(); /* go to first char. of joined line */
  544. if (join_cmd && size != 0) {
  545. /*
  546. * Delete leading white space on the joined line
  547. * and insert a single space.
  548. */
  549. while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB)
  550. delchar(TRUE);
  551. inschar(' ');
  552. }
  553. return TRUE;
  554. }
  555. void
  556. startinsert(initstr, startln)
  557. char *initstr;
  558. int startln; /* if set, insert point really at start of line */
  559. {
  560. register char *p, c;
  561. *Insstart = *Curschar;
  562. if (startln)
  563. Insstart->index = 0;
  564. Ninsert = 0;
  565. Insptr = Insbuff;
  566. for (p=initstr; (c=(*p++))!='\0'; ) {
  567. *Insptr++ = c;
  568. Ninsert++;
  569. }
  570. if (*initstr == 'R')
  571. State = REPLACE;
  572. else
  573. State = INSERT;
  574. if (P(P_MO))
  575. msg((State == INSERT) ? "Insert Mode" : "Replace Mode");
  576. }
  577. /*
  578. * tabinout(inout,num)
  579. *
  580. * If inout==0, add a tab to the begining of the next num lines.
  581. * If inout==1, delete a tab from the beginning of the next num lines.
  582. */
  583. static void
  584. tabinout(inout, num)
  585. int inout;
  586. int num;
  587. {
  588. int ntodo = num;
  589. int c;
  590. int col;
  591. LNPTR *p;
  592. while (ntodo-- > 0) {
  593. beginline(FALSE);
  594. /*
  595. * eat leading space, calc the column of first non-white
  596. */
  597. col = 0;
  598. while ((c = gchar(Curschar)) == ' ' || c == TAB) {
  599. if (c == ' ') {
  600. ++col;
  601. } else {
  602. col += P(P_TS);
  603. col -= (col % P(P_TS));
  604. }
  605. delchar(TRUE);
  606. }
  607. /*
  608. * add or subtract shiftwidth spaces
  609. */
  610. if (inout == 0) {
  611. col += P(P_SW);
  612. } else {
  613. col -= P(P_SW);
  614. }
  615. if (col < 0) {
  616. col = 0;
  617. }
  618. /*
  619. * insert space, using TABS if hardtabs is set
  620. */
  621. while (col % P(P_TS)) {
  622. inschar(' ');
  623. col--;
  624. }
  625. if (P(P_HT)) {
  626. while (col) {
  627. inschar(TAB);
  628. col -= P(P_TS);
  629. }
  630. } else {
  631. while (col--) {
  632. inschar(' ');
  633. }
  634. }
  635. /*
  636. * next line
  637. */
  638. if ( ntodo > 0 ) {
  639. if ((p = nextline(Curschar)) != NULL) {
  640. *Curschar = *p;
  641. }
  642. }
  643. }
  644. }
  645. /*
  646. * inslines(lp, dir, buf)
  647. *
  648. * Inserts lines in the file from the given buffer. Lines are inserted
  649. * before or after "lp" according to the given direction flag. Newlines
  650. * in the buffer result in multiple lines being inserted. The cursor
  651. * is left on the first of the inserted lines.
  652. */
  653. static void
  654. inslines(lp, dir, buf)
  655. LINE *lp;
  656. int dir;
  657. char *buf;
  658. {
  659. register char *cp = buf;
  660. register size_t len;
  661. char *ep;
  662. LINE *l, *nc = NULL;
  663. if (dir == BACKWARD)
  664. lp = lp->prev;
  665. do {
  666. if ((ep = strchr(cp, NL)) == NULL)
  667. len = strlen(cp);
  668. else
  669. len = (size_t)(ep - cp);
  670. l = newline(len);
  671. if (len != 0)
  672. strncpy(l->s, cp, len);
  673. l->s[len] = NUL;
  674. l->next = lp->next;
  675. l->prev = lp;
  676. lp->next->prev = l;
  677. lp->next = l;
  678. if (nc == NULL)
  679. nc = l;
  680. lp = lp->next;
  681. cp = ep + 1;
  682. } while (ep != NULL);
  683. if (dir == BACKWARD) /* fix the top line in case we were there */
  684. Filemem->linep = Filetop->linep->next;
  685. renum();
  686. updatescreen();
  687. Curschar->linep = nc;
  688. Curschar->index = 0;
  689. }