home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2195 < prev    next >
Internet Message Format  |  1990-12-28  |  78KB

  1. From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall)
  2. Newsgroups: alt.sources
  3. Subject: Elvis 1.4, part 4 of 8
  4. Message-ID: <828@pdxgate.UUCP>
  5. Date: 3 Dec 90 21:31:07 GMT
  6.  
  7. # --------------------------- cut here ----------------------------
  8. # This is a shar archive.  To unpack it, save it to a file, and delete
  9. # anything above the "cut here" line.  Then run sh on the file.
  10. #
  11. # -rw-r--r--  1 kirkenda     9230 Dec  2 17:57 blk.c
  12. # -rw-r--r--  1 kirkenda    23975 Dec  2 17:57 cmd1.c
  13. # -rw-r--r--  1 kirkenda    16797 Dec  2 17:57 cmd2.c
  14. # -rw-r--r--  1 kirkenda     7951 Dec  2 17:57 config.h
  15. # -rw-r--r--  1 kirkenda    14196 Dec  2 17:57 curses.c
  16. #
  17.  
  18. if test -f blk.c -a "$1" != -f
  19. then
  20. echo Will not overwrite blk.c
  21. else
  22. echo Extracting blk.c
  23. sed 's/^X//' >blk.c <<\eof
  24. X/* blk.c */
  25. X
  26. X/* Author:
  27. X *    Steve Kirkendall
  28. X *    14407 SW Teal Blvd. #C
  29. X *    Beaverton, OR 97005
  30. X *    kirkenda@cs.pdx.edu
  31. X */
  32. X
  33. X
  34. X/* This file contains the functions that get/put blocks from the temp file.
  35. X * It also contains the "do" and "undo" functions.
  36. X */
  37. X
  38. X#include "config.h"
  39. X#include "vi.h"
  40. X
  41. X#ifndef NBUFS
  42. X# define NBUFS    5        /* must be at least 3 -- more is better */
  43. X#endif
  44. X
  45. Xextern long lseek();
  46. X
  47. X/*------------------------------------------------------------------------*/
  48. X
  49. XBLK        hdr;        /* buffer for the header block */
  50. X
  51. Xstatic int    b4cnt;        /* used to count context of beforedo/afterdo */
  52. Xstatic struct _blkbuf
  53. X{
  54. X    BLK        buf;        /* contents of a text block */
  55. X    unsigned short    logical;    /* logical block number */
  56. X    int        dirty;        /* must the buffer be rewritten? */
  57. X}
  58. X        blk[NBUFS],    /* buffers for text[?] blocks */
  59. X        *toonew,    /* buffer which shouldn't be recycled yet */
  60. X        *newtoo,    /* another buffer which should be recycled */
  61. X        *recycle = blk;    /* next block to be recycled */
  62. X
  63. X
  64. X
  65. X
  66. X
  67. X/* This function wipes out all buffers */
  68. Xvoid blkinit()
  69. X{
  70. X    int    i;
  71. X
  72. X    for (i = 0; i < NBUFS; i++)
  73. X    {
  74. X        blk[i].logical = 0;
  75. X        blk[i].dirty = FALSE;
  76. X    }
  77. X    for (i = 0; i < MAXBLKS; i++)
  78. X    {
  79. X        hdr.n[i] = 0;
  80. X    }
  81. X}
  82. X
  83. X/* This function allocates a buffer and fills it with a given block's text */
  84. XBLK *blkget(logical)
  85. X    int    logical;    /* logical block number to fetch */
  86. X{
  87. X    REG struct _blkbuf    *this;    /* used to step through blk[] */
  88. X    REG int    i;
  89. X
  90. X    /* if logical is 0, just return the hdr buffer */
  91. X    if (logical == 0)
  92. X    {
  93. X        return &hdr;
  94. X    }
  95. X
  96. X    /* see if we have that block in mem already */
  97. X    for (this = blk; this < &blk[NBUFS]; this++)
  98. X    {
  99. X        if (this->logical == logical)
  100. X        {
  101. X            newtoo = toonew;
  102. X            toonew = this;
  103. X            return &this->buf;
  104. X        }
  105. X    }
  106. X
  107. X    /* choose a block to be recycled */
  108. X    do
  109. X    {
  110. X        this = recycle++;
  111. X        if (recycle == &blk[NBUFS])
  112. X        {
  113. X            recycle = blk;
  114. X        }
  115. X    } while (this == toonew || this == newtoo);
  116. X
  117. X    /* if it contains a block, flush that block */
  118. X    blkflush(this);
  119. X
  120. X    /* fill this buffer with the desired block */
  121. X    this->logical = logical;
  122. X    if (hdr.n[logical])
  123. X    {
  124. X        /* it has been used before - fill it from tmp file */
  125. X        lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
  126. X        if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
  127. X        {
  128. X            msg("Error reading back from tmp file!");
  129. X        }
  130. X    }
  131. X    else
  132. X    {
  133. X        /* it is new - zero it */
  134. X        for (i = 0; i < BLKSIZE; i++)
  135. X        {
  136. X            this->buf.c[i] = 0;
  137. X        }
  138. X    }
  139. X
  140. X    /* This isn't really a change, but it does potentially invalidate
  141. X     * the kinds of shortcuts that the "changes" variable is supposed
  142. X     * to protect us from... so count it as a change.
  143. X     */
  144. X    changes++;
  145. X
  146. X    /* mark it as being "not dirty" */
  147. X    this->dirty = 0;
  148. X
  149. X    /* return it */
  150. X    newtoo = toonew;
  151. X    toonew = this;
  152. X    return &this->buf;
  153. X}
  154. X
  155. X
  156. X
  157. X/* This function writes a block out to the temporary file */
  158. Xvoid blkflush(this)
  159. X    REG struct _blkbuf    *this;    /* the buffer to flush */
  160. X{
  161. X    long        seekpos;    /* seek position of the new block */
  162. X    unsigned short    physical;    /* physical block number */
  163. X
  164. X    /* if its empty (an orphan blkadd() maybe?) then make it dirty */
  165. X    if (this->logical && !*this->buf.c)
  166. X    {
  167. X        blkdirty(&this->buf);
  168. X    }
  169. X
  170. X    /* if it's an empty buffer or a clean version is on disk, quit */
  171. X    if (!this->logical || hdr.n[this->logical] && !this->dirty)
  172. X    {
  173. X        return;
  174. X    }
  175. X
  176. X    /* find a free place in the file */
  177. X#ifndef NO_RECYCLE
  178. X    seekpos = allocate();
  179. X    lseek(tmpfd, seekpos, 0);
  180. X#else
  181. X    seekpos = lseek(tmpfd, 0L, 2);
  182. X#endif
  183. X    physical = seekpos / BLKSIZE;
  184. X
  185. X    /* put the block there */
  186. X    if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
  187. X    {
  188. X        msg("Trouble writing to tmp file");
  189. X    }
  190. X    this->dirty = FALSE;
  191. X
  192. X    /* update the header so it knows we put it there */
  193. X    hdr.n[this->logical] = physical;
  194. X}
  195. X
  196. X
  197. X/* This function sets a block's "dirty" flag or deletes empty blocks */
  198. Xvoid blkdirty(bp)
  199. X    BLK    *bp;    /* buffer returned by blkget() */
  200. X{
  201. X    REG int        i, j;
  202. X    REG char    *scan;
  203. X    REG int        k;
  204. X
  205. X    /* find the buffer */
  206. X    for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
  207. X    {
  208. X    }
  209. X#ifdef DEBUG
  210. X    if (i >= NBUFS)
  211. X    {
  212. X        msg("blkdirty() called with unknown buffer at 0x%lx", bp);
  213. X        return;
  214. X    }
  215. X    if (blk[i].logical == 0)
  216. X    {
  217. X        msg("blkdirty called with freed buffer");
  218. X        return;
  219. X    }
  220. X#endif
  221. X
  222. X    /* if this block ends with line# INFINITY, then it must have been
  223. X     * allocated unnecessarily during tmpstart().  Forget it.
  224. X     */
  225. X    if (lnum[blk[i].logical] == INFINITY)
  226. X    {
  227. X#ifdef DEBUG
  228. X        if (blk[i].buf.c[0])
  229. X        {
  230. X            msg("bkldirty called with non-empty extra BLK");
  231. X        }
  232. X#endif
  233. X        blk[i].logical = 0;
  234. X        blk[i].dirty = FALSE;
  235. X        return;
  236. X    }
  237. X
  238. X    /* count lines in this block */
  239. X    for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
  240. X    {
  241. X        if (*scan == '\n')
  242. X        {
  243. X            j++;
  244. X        }
  245. X    }
  246. X
  247. X    /* adjust lnum, if necessary */
  248. X    k = blk[i].logical;
  249. X    j += (lnum[k - 1] - lnum[k]);
  250. X    if (j != 0)
  251. X    {
  252. X        nlines += j;
  253. X        while (k < MAXBLKS && lnum[k] != INFINITY)
  254. X        {
  255. X            lnum[k++] += j;
  256. X        }
  257. X    }
  258. X
  259. X    /* if it still has text, mark it as dirty */
  260. X    if (*bp->c)
  261. X    {
  262. X        blk[i].dirty = TRUE;
  263. X    }
  264. X    else /* empty block, so delete it */
  265. X    {
  266. X        /* adjust the cache */
  267. X        k = blk[i].logical;
  268. X        for (j = 0; j < NBUFS; j++)
  269. X        {
  270. X            if (blk[j].logical >= k)
  271. X            {
  272. X                blk[j].logical--;
  273. X            }
  274. X        }
  275. X
  276. X        /* delete it from hdr.n[] and lnum[] */
  277. X        blk[i].logical = 0;
  278. X        blk[i].dirty = FALSE;
  279. X        while (k < MAXBLKS - 1)
  280. X        {
  281. X            hdr.n[k] = hdr.n[k + 1];
  282. X            lnum[k] = lnum[k + 1];
  283. X            k++;
  284. X        }
  285. X        hdr.n[MAXBLKS - 1] = 0;
  286. X        lnum[MAXBLKS - 1] = INFINITY;
  287. X    }
  288. X}
  289. X
  290. X
  291. X/* insert a new block into hdr, and adjust the cache */
  292. XBLK *blkadd(logical)
  293. X    int    logical;    /* where to insert the new block */
  294. X{
  295. X    REG int    i;
  296. X
  297. X    /* adjust hdr and lnum[] */
  298. X    for (i = MAXBLKS - 1; i > logical; i--)
  299. X    {
  300. X        hdr.n[i] = hdr.n[i - 1];
  301. X        lnum[i] = lnum[i - 1];
  302. X    }
  303. X    hdr.n[logical] = 0;
  304. X    lnum[logical] = lnum[logical - 1];
  305. X
  306. X    /* adjust the cache */
  307. X    for (i = 0; i < NBUFS; i++)
  308. X    {
  309. X        if (blk[i].logical >= logical)
  310. X        {
  311. X            blk[i].logical++;
  312. X        }
  313. X    }
  314. X
  315. X    /* return the new block, via blkget() */
  316. X    return blkget(logical);
  317. X}
  318. X
  319. X
  320. X/* This function forces all dirty blocks out to disk */
  321. Xvoid blksync()
  322. X{
  323. X    int    i;
  324. X
  325. X    for (i = 0; i < NBUFS; i++)
  326. X    {
  327. X        /* blk[i].dirty = TRUE; */
  328. X        blkflush(&blk[i]);
  329. X    }
  330. X    if (*o_sync)
  331. X    {
  332. X        sync();
  333. X    }
  334. X}
  335. X
  336. X/*------------------------------------------------------------------------*/
  337. X
  338. Xstatic MARK    undocurs;    /* where the cursor should go if undone */
  339. Xstatic long    oldnlines;
  340. Xstatic long    oldlnum[MAXBLKS];
  341. X
  342. X
  343. X/* This function should be called before each command that changes the text.
  344. X * It defines the state that undo() will reset the file to.
  345. X */
  346. Xvoid beforedo(forundo)
  347. X    int        forundo;    /* boolean: is this for an undo? */
  348. X{
  349. X    REG int        i;
  350. X    REG long    l;
  351. X
  352. X    /* if this is a nested call to beforedo, quit! Use larger context */
  353. X    if (b4cnt++ > 0)
  354. X    {
  355. X        return;
  356. X    }
  357. X
  358. X    /* force all block buffers to disk */
  359. X    blksync();
  360. X
  361. X#ifndef NO_RECYCLE
  362. X    /* perform garbage collection on blocks from tmp file */
  363. X    garbage();
  364. X#endif
  365. X
  366. X    /* force the header out to disk */
  367. X    lseek(tmpfd, 0L, 0);
  368. X    if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
  369. X    {
  370. X        msg("Trouble writing header to tmp file ");
  371. X    }
  372. X
  373. X    /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
  374. X    if (forundo)
  375. X    {
  376. X        for (i = 0; i < MAXBLKS; i++)
  377. X        {
  378. X            l = lnum[i];
  379. X            lnum[i] = oldlnum[i];
  380. X            oldlnum[i] = l;
  381. X        }
  382. X        l = nlines;
  383. X        nlines = oldnlines;
  384. X        oldnlines = l;
  385. X    }
  386. X    else
  387. X    {
  388. X        for (i = 0; i < MAXBLKS; i++)
  389. X        {
  390. X            oldlnum[i] = lnum[i];
  391. X        }
  392. X        oldnlines = nlines;
  393. X    }
  394. X
  395. X    /* save the cursor position */
  396. X    undocurs = cursor;
  397. X
  398. X    /* upon return, the calling function continues and makes changes... */
  399. X}
  400. X
  401. X/* This function marks the end of a (nested?) change to the file */
  402. Xvoid afterdo()
  403. X{
  404. X    if (--b4cnt)
  405. X    {
  406. X        /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
  407. X         * pairs incorrectly.  If it is decremented to often, then
  408. X         * keep b4cnt sane but don't do anything else.
  409. X         */
  410. X        if (b4cnt < 0)
  411. X            b4cnt = 0;
  412. X
  413. X        return;
  414. X    }
  415. X
  416. X    /* make sure the cursor wasn't left stranded in deleted text */
  417. X    if (markline(cursor) > nlines)
  418. X    {
  419. X        cursor = MARK_LAST;
  420. X    }
  421. X    /* NOTE: it is still possible that markidx(cursor) is after the
  422. X     * end of a line, so the Vi mode will have to take care of that
  423. X     * itself */
  424. X
  425. X    /* if a significant change has been made to this file, then set the
  426. X     * MODIFIED flag.
  427. X     */
  428. X    if (significant)
  429. X    {
  430. X        setflag(file, MODIFIED);
  431. X    }    
  432. X}
  433. X
  434. X/* This function cuts short the current set of changes.  It is called after
  435. X * a SIGINT.
  436. X */
  437. Xvoid abortdo()
  438. X{
  439. X    /* finish the operation immediately. */
  440. X    if (b4cnt > 0)
  441. X    {
  442. X        b4cnt = 1;
  443. X        afterdo();
  444. X    }
  445. X
  446. X    /* in visual mode, the screen is probably screwed up */
  447. X    if (mode == MODE_COLON)
  448. X    {
  449. X        mode = MODE_VI;
  450. X    }
  451. X    if (mode == MODE_VI)
  452. X    {
  453. X        redraw(MARK_UNSET, FALSE);
  454. X    }
  455. X}
  456. X
  457. X/* This function discards all changes made since the last call to beforedo() */
  458. Xint undo()
  459. X{
  460. X    BLK        oldhdr;
  461. X
  462. X    /* if beforedo() has never been run, fail */
  463. X    if (!tstflag(file, MODIFIED))
  464. X    {
  465. X        msg("You haven't modified this file yet.");
  466. X        return FALSE;
  467. X    }
  468. X
  469. X    /* read the old header form the tmp file */
  470. X    lseek(tmpfd, 0L, 0);
  471. X    if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
  472. X    {
  473. X        msg("Trouble rereading the old header from tmp file");
  474. X    }
  475. X
  476. X    /* "do" the changed version, so we can undo the "undo" */
  477. X    cursor = undocurs;
  478. X    beforedo(TRUE);
  479. X    afterdo();
  480. X
  481. X    /* wipe out the block buffers - we can't assume they're correct */
  482. X    blkinit();
  483. X
  484. X    /* use the old header -- and therefore the old text blocks */
  485. X    hdr = oldhdr;
  486. X
  487. X    /* This is a change */
  488. X    changes++;
  489. X
  490. X    return TRUE;
  491. X}
  492. eof
  493. if test `wc -c <blk.c` -ne 9230
  494. then
  495. echo blk.c damaged!
  496. fi
  497. fi
  498.  
  499. if test -f cmd1.c -a "$1" != -f
  500. then
  501. echo Will not overwrite cmd1.c
  502. else
  503. echo Extracting cmd1.c
  504. sed 's/^X//' >cmd1.c <<\eof
  505. X/* cmd1.c */
  506. X
  507. X/* Author:
  508. X *    Steve Kirkendall
  509. X *    14407 SW Teal Blvd. #C
  510. X *    Beaverton, OR 97005
  511. X *    kirkenda@cs.pdx.edu
  512. X */
  513. X
  514. X
  515. X/* This file contains some of the EX commands - mostly ones that deal with
  516. X * files, options, etc. -- anything except text.
  517. X */
  518. X
  519. X#include "config.h"
  520. X#include <ctype.h>
  521. X#include "vi.h"
  522. X#include "regexp.h"
  523. X
  524. X#if MSDOS
  525. X#define    DATE __DATE__
  526. X#endif
  527. X
  528. X#ifdef DEBUG
  529. X/* print the selected lines with info on the blocks */
  530. X/*ARGSUSED*/
  531. Xvoid cmd_debug(frommark, tomark, cmd, bang, extra)
  532. X    MARK    frommark;
  533. X    MARK    tomark;
  534. X    CMD    cmd;
  535. X    int    bang;
  536. X    char    *extra;
  537. X{
  538. X    REG char    *scan;
  539. X    REG long    l;
  540. X    REG int        i;
  541. X    int        len;
  542. X
  543. X    /* scan lnum[] to determine which block its in */
  544. X    l = markline(frommark);
  545. X    for (i = 1; l > lnum[i]; i++)
  546. X    {
  547. X    }
  548. X
  549. X    do
  550. X    {
  551. X        /* fetch text of the block containing that line */
  552. X        scan = blkget(i)->c;
  553. X
  554. X        /* calculate its length */
  555. X        if (scan[BLKSIZE - 1])
  556. X        {
  557. X            len = BLKSIZE;
  558. X        }
  559. X        else
  560. X        {
  561. X            len = strlen(scan);
  562. X        }
  563. X
  564. X        /* print block stats */
  565. X        msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
  566. X            i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
  567. X        msg("##### len=%d, buf=0x%lx, %sdirty",
  568. X            len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
  569. X        if (bang)
  570. X        {
  571. X            while (--len >= 0)
  572. X            {
  573. X                addch(*scan);
  574. X                scan++;
  575. X            }
  576. X        }
  577. X        exrefresh();
  578. X
  579. X        /* next block */
  580. X        i++;
  581. X    } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
  582. X}
  583. X
  584. X
  585. X/* This function checks a lot of conditions to make sure they aren't screwy */
  586. X/*ARGSUSED*/
  587. Xvoid cmd_validate(frommark, tomark, cmd, bang, extra)
  588. X    MARK    frommark;
  589. X    MARK    tomark;
  590. X    CMD    cmd;
  591. X    int    bang;
  592. X    char    *extra;
  593. X{
  594. X    char    *scan;
  595. X    int    i;
  596. X    int    nlcnt;    /* used to count newlines */
  597. X    int    len;    /* counts non-NUL characters */
  598. X
  599. X    /* check lnum[0] */
  600. X    if (lnum[0] != 0L)
  601. X    {
  602. X        msg("lnum[0] = %ld", lnum[0]);
  603. X    }
  604. X
  605. X    /* check each block */
  606. X    for (i = 1; lnum[i] <= nlines; i++)
  607. X    {
  608. X        scan = blkget(i)->c;
  609. X        if (scan[BLKSIZE - 1])
  610. X        {
  611. X            msg("block %d has no NUL at the end", i);
  612. X        }
  613. X        else
  614. X        {
  615. X            for (nlcnt = len = 0; *scan; scan++, len++)
  616. X            {
  617. X                if (*scan == '\n')
  618. X                {
  619. X                    nlcnt++;
  620. X                }
  621. X            }
  622. X            if (scan[-1] != '\n')
  623. X            {
  624. X                msg("block %d doesn't end with '\\n' (length %d)", i, len);
  625. X            }
  626. X            if (bang || nlcnt != lnum[i] - lnum[i - 1])
  627. X            {
  628. X                msg("block %d (line %ld?) has %d lines, but should have %ld",
  629. X                    i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
  630. X            }
  631. X        }
  632. X        exrefresh();
  633. X    }
  634. X
  635. X    /* check lnum again */
  636. X    if (lnum[i] != INFINITY)
  637. X    {
  638. X        msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
  639. X            i, hdr.n[i], i, lnum[i]);
  640. X    }
  641. X
  642. X    msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
  643. X}
  644. X#endif /* DEBUG */
  645. X
  646. X
  647. X/*ARGSUSED*/
  648. Xvoid cmd_mark(frommark, tomark, cmd, bang, extra)
  649. X    MARK    frommark;
  650. X    MARK    tomark;
  651. X    CMD    cmd;
  652. X    int    bang;
  653. X    char    *extra;
  654. X{
  655. X    /* validate the name of the mark */
  656. X    if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
  657. X    {
  658. X        msg("Invalid mark name");
  659. X        return;
  660. X    }
  661. X
  662. X    mark[*extra - 'a'] = tomark;
  663. X}
  664. X
  665. X/*ARGSUSED*/
  666. Xvoid cmd_write(frommark, tomark, cmd, bang, extra)
  667. X    MARK    frommark;
  668. X    MARK    tomark;
  669. X    CMD    cmd;
  670. X    int    bang;
  671. X    char    *extra;
  672. X{
  673. X    int        fd;
  674. X    int        append;    /* boolean: write in "append" mode? */
  675. X    REG long    l;
  676. X    REG char    *scan;
  677. X    REG int        i;
  678. X
  679. X    /* if all lines are to be written, use tmpsave() */
  680. X    if (frommark == MARK_FIRST && tomark == MARK_LAST)
  681. X    {
  682. X        tmpsave(extra, bang);
  683. X        return;
  684. X    }
  685. X
  686. X    /* see if we're going to do this in append mode or not */
  687. X    append = FALSE;
  688. X    if (extra[0] == '>' && extra[1] == '>')
  689. X    {
  690. X        extra += 2;
  691. X        append = TRUE;
  692. X    }
  693. X
  694. X    /* either the file must not exist, or we must have a ! or be appending */
  695. X    if (access(extra, 0) == 0 && !bang && !append)
  696. X    {
  697. X        msg("File already exists - Use :w! to overwrite");
  698. X        return;
  699. X    }
  700. X
  701. X    /* else do it line-by-line, like cmd_print() */
  702. X    if (append)
  703. X    {
  704. X#ifdef O_APPEND
  705. X        fd = open(extra, O_WRONLY|O_APPEND);
  706. X#else
  707. X        fd = open(extra, O_WRONLY);
  708. X        if (fd >= 0)
  709. X        {
  710. X            lseek(fd, 0L, 2);
  711. X        }
  712. X#endif
  713. X    }
  714. X    else
  715. X    {
  716. X        fd = -1; /* so we know the file isn't open yet */
  717. X    }
  718. X
  719. X    if (fd < 0)
  720. X    {
  721. X        fd = creat(extra, FILEPERMS);
  722. X        if (fd < 0)
  723. X        {
  724. X            msg("Can't write to \"%s\"", extra);
  725. X            return;
  726. X        }
  727. X    }
  728. X    for (l = markline(frommark); l <= markline(tomark); l++)
  729. X    {
  730. X        /* get the next line */
  731. X        scan = fetchline(l);
  732. X        i = strlen(scan);
  733. X        scan[i++] = '\n';
  734. X
  735. X        /* print the line */
  736. X        twrite(fd, scan, i);
  737. X    }
  738. X    close(fd);
  739. X}    
  740. X
  741. X
  742. X/*ARGSUSED*/
  743. Xvoid cmd_shell(frommark, tomark, cmd, bang, extra)
  744. X    MARK    frommark, tomark;
  745. X    CMD    cmd;
  746. X    int    bang;
  747. X    char    *extra;
  748. X{
  749. X    static char    prevextra[80];
  750. X
  751. X    /* special case: ":sh" means ":!sh" */
  752. X    if (cmd == CMD_SHELL)
  753. X    {
  754. X        extra = o_shell;
  755. X        frommark = tomark = 0L;
  756. X    }
  757. X
  758. X    /* if extra is "!", substute previous command */
  759. X    if (*extra == '!')
  760. X    {
  761. X        if (!*prevextra)
  762. X        {
  763. X            msg("No previous shell command to substitute for '!'");
  764. X            return;
  765. X        }
  766. X        extra = prevextra;
  767. X    }
  768. X    else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
  769. X    {
  770. X        strcpy(prevextra, extra);
  771. X    }
  772. X
  773. X    /* if no lines were specified, just run the command */
  774. X    suspend_curses();
  775. X    if (frommark == 0L)
  776. X    {
  777. X        system(extra);
  778. X    }
  779. X    else /* pipe lines from the file through the command */
  780. X    {
  781. X        filter(frommark, tomark, extra);
  782. X    }
  783. X
  784. X    /* resume curses quietly for MODE_EX, but noisily otherwise */
  785. X    resume_curses(mode == MODE_EX);
  786. X}
  787. X
  788. X
  789. X/*ARGSUSED*/
  790. Xvoid cmd_global(frommark, tomark, cmd, bang, extra)
  791. X    MARK    frommark, tomark;
  792. X    CMD    cmd;
  793. X    int    bang;
  794. X    char    *extra;    /* rest of the command line */
  795. X{
  796. X    char    *cmdptr;    /* the command from the command line */
  797. X    char    cmdln[100];    /* copy of the command from the command line */
  798. X    char    *line;        /* a line from the file */
  799. X    long    l;        /* used as a counter to move through lines */
  800. X    long    lqty;        /* quantity of lines to be scanned */
  801. X    long    nchanged;    /* number of lines changed */
  802. X    regexp    *re;        /* the compiled search expression */
  803. X
  804. X    /* can't nest global commands */
  805. X    if (doingglobal)
  806. X    {
  807. X        msg("Can't nest global commands.");
  808. X        rptlines = -1L;
  809. X        return;
  810. X    }
  811. X
  812. X    /* ":g! ..." is the same as ":v ..." */
  813. X    if (bang)
  814. X    {
  815. X        cmd = CMD_VGLOBAL;
  816. X    }
  817. X
  818. X    /* make sure we got a search pattern */
  819. X    if (*extra != '/' && *extra != '?')
  820. X    {
  821. X        msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
  822. X        return;
  823. X    }
  824. X
  825. X    /* parse & compile the search pattern */
  826. X    cmdptr = parseptrn(extra);
  827. X    if (!extra[1])
  828. X    {
  829. X        msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
  830. X        return;
  831. X    }
  832. X    re = regcomp(extra + 1);
  833. X    if (!re)
  834. X    {
  835. X        /* regcomp found & described an error */
  836. X        return;
  837. X    }
  838. X
  839. X    /* for each line in the range */
  840. X    doingglobal = TRUE;
  841. X    ChangeText
  842. X    {
  843. X        /* NOTE: we have to go through the lines in a forward order,
  844. X         * otherwise "g/re/p" would look funny.  *BUT* for "g/re/d"
  845. X         * to work, simply adding 1 to the line# on each loop won't
  846. X         * work.  The solution: count lines relative to the end of
  847. X         * the file.  Think about it.
  848. X         */
  849. X        for (l = nlines - markline(frommark),
  850. X            lqty = markline(tomark) - markline(frommark) + 1L,
  851. X            nchanged = 0L;
  852. X             lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
  853. X             l--, lqty--)
  854. X        {
  855. X            /* fetch the line */
  856. X            line = fetchline(nlines - l);
  857. X
  858. X            /* if it contains the search pattern... */
  859. X            if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
  860. X            {
  861. X                /* move the cursor to that line */
  862. X                cursor = MARK_AT_LINE(nlines - l);
  863. X
  864. X                /* do the ex command (without mucking up
  865. X                 * the original copy of the command line)
  866. X                 */
  867. X                strcpy(cmdln, cmdptr);
  868. X                rptlines = 0L;
  869. X                doexcmd(cmdln);
  870. X                nchanged += rptlines;
  871. X            }
  872. X        }
  873. X    }
  874. X    doingglobal = FALSE;
  875. X
  876. X    /* free the regexp */
  877. X    free(re);
  878. X
  879. X    /* Reporting...*/
  880. X    rptlines = nchanged;
  881. X}
  882. X
  883. X
  884. X/*ARGSUSED*/
  885. Xvoid cmd_file(frommark, tomark, cmd, bang, extra)
  886. X    MARK    frommark, tomark;
  887. X    CMD    cmd;
  888. X    int    bang;
  889. X    char    *extra;
  890. X{
  891. X#ifndef CRUNCH
  892. X    /* if we're given a new filename, use it as this file's name */
  893. X    if (extra && *extra)
  894. X    {
  895. X        strcpy(origname, extra);
  896. X    }
  897. X#endif
  898. X    if (cmd == CMD_FILE)
  899. X    {
  900. X        msg("\"%s\" %s%s %ld lines,  line %ld [%ld%%]",
  901. X            *origname ? origname : "[NO FILE]",
  902. X            tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
  903. X            tstflag(file, READONLY) ? "[READONLY]" : "",
  904. X            nlines,
  905. X            markline(frommark),
  906. X            markline(frommark) * 100 / nlines);
  907. X    }
  908. X    else if (markline(frommark) == markline(tomark))
  909. X    {
  910. X        msg("%ld", markline(frommark));
  911. X    }
  912. X    else
  913. X    {
  914. X        msg("range \"%ld,%ld\" contains %ld lines",
  915. X            markline(frommark),
  916. X            markline(tomark),
  917. X            markline(tomark) - markline(frommark) + 1L);
  918. X    }
  919. X}
  920. X
  921. X
  922. X/*ARGSUSED*/
  923. Xvoid cmd_edit(frommark, tomark, cmd, bang, extra)
  924. X    MARK    frommark, tomark;
  925. X    CMD    cmd;
  926. X    int    bang;
  927. X    char    *extra;
  928. X{
  929. X    long    line = 1L;    /* might be set to prevline */
  930. X
  931. X    /* Editing previous file?  Then start at previous line */
  932. X    if (!strcmp(extra, prevorig))
  933. X    {
  934. X        line = prevline;
  935. X    }
  936. X
  937. X#ifndef CRUNCH
  938. X    /* if we were given an explicit starting line, then start there */
  939. X    if (*extra == '+')
  940. X    {
  941. X        for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
  942. X        {
  943. X            line *= 10L;
  944. X            line += (*extra - '0');
  945. X        }
  946. X        while (isascii(*extra) && isspace(*extra))
  947. X        {
  948. X            extra++;
  949. X        }
  950. X    }
  951. X#endif /* not CRUNCH */
  952. X
  953. X    /* switch files */
  954. X    if (tmpabort(bang))
  955. X    {
  956. X        tmpstart(extra);
  957. X        if (line <= nlines && line >= 1L)
  958. X        {
  959. X            cursor = MARK_AT_LINE(line);
  960. X        }
  961. X    }
  962. X    else
  963. X    {
  964. X        msg("Use edit! to abort changes, or w to save changes");
  965. X
  966. X        /* so we can say ":e!#" next time... */
  967. X        strcpy(prevorig, extra);
  968. X        prevline = 1L;
  969. X    }
  970. X}
  971. X
  972. X/* This code is also used for rewind -- GB */
  973. X
  974. X/*ARGSUSED*/
  975. Xvoid cmd_next(frommark, tomark, cmd, bang, extra)
  976. X    MARK    frommark, tomark;
  977. X    CMD    cmd;
  978. X    int    bang;
  979. X    char    *extra;
  980. X{
  981. X    int    i, j;
  982. X    char    *scan;
  983. X    char    *build;
  984. X
  985. X    /* if extra stuff given, use ":args" to define a new args list */
  986. X    if (cmd == CMD_NEXT && extra && *extra)
  987. X    {
  988. X        cmd_args(frommark, tomark, cmd, bang, extra);
  989. X    }
  990. X
  991. X    /* move to the next arg */
  992. X    if (cmd == CMD_NEXT)
  993. X    {
  994. X        i = argno + 1;
  995. X    }
  996. X    else if (cmd == CMD_PREVIOUS)
  997. X    {
  998. X        i = argno - 1;
  999. X    }
  1000. X    else /* cmd == CMD_REWIND */
  1001. X    {
  1002. X        i = 0;
  1003. X    }    
  1004. X    if (i < 0 || i >= nargs)
  1005. X    {
  1006. X        msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
  1007. X        return;
  1008. X    }
  1009. X
  1010. X    /* find & isolate the name of the file to edit */
  1011. X    for (j = i, scan = args; j > 0; j--)
  1012. X    {
  1013. X        while(!isascii(*scan) || !isspace(*scan))
  1014. X        {
  1015. X            scan++;
  1016. X        }
  1017. X        while (isascii(*scan) && isspace(*scan))
  1018. X        {
  1019. X            scan++;
  1020. X        }
  1021. X    }
  1022. X    for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
  1023. X    {
  1024. X        *build++ = *scan++;
  1025. X    }
  1026. X    *build = '\0';
  1027. X
  1028. X    /* switch to the next file */
  1029. X    if (tmpabort(bang))
  1030. X    {
  1031. X        tmpstart(tmpblk.c);
  1032. X        argno = i;
  1033. X    }
  1034. X    else
  1035. X    {
  1036. X        msg("Use :%s! to abort changes, or w to save changes",
  1037. X            cmd == CMD_NEXT ? "next" :
  1038. X            cmd == CMD_PREVIOUS ? "previous" :
  1039. X                    "rewind");
  1040. X    }
  1041. X}
  1042. X
  1043. X/* also called from :wq -- always writes back in this case */
  1044. X
  1045. X/*ARGSUSED*/
  1046. Xvoid cmd_xit(frommark, tomark, cmd, bang, extra)
  1047. X    MARK    frommark, tomark;
  1048. X    CMD    cmd;
  1049. X    int    bang;
  1050. X    char    *extra;
  1051. X{
  1052. X    static long    whenwarned;    /* when the user was last warned of extra files */
  1053. X    int        oldflag;
  1054. X
  1055. X    /* if there are more files to edit, then warn user */
  1056. X    if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
  1057. X    {
  1058. X        msg("More files to edit -- Use \":n\" to go to next file");
  1059. X        whenwarned = changes;
  1060. X        return;
  1061. X    }
  1062. X
  1063. X    if (cmd == CMD_QUIT)
  1064. X    {
  1065. X        if (tmpabort(bang))
  1066. X        {
  1067. X            mode = MODE_QUIT;
  1068. X        }
  1069. X        else
  1070. X        {
  1071. X            msg("Use q! to abort changes, or wq to save changes");
  1072. X        }
  1073. X    }
  1074. X    else
  1075. X    {
  1076. X        /* else try to save this file */
  1077. X        oldflag = tstflag(file, MODIFIED);
  1078. X        if (cmd == CMD_WQUIT)
  1079. X            setflag(file, MODIFIED);
  1080. X        if (tmpend(bang))
  1081. X        {
  1082. X            mode = MODE_QUIT;
  1083. X        }
  1084. X        else
  1085. X        {
  1086. X            msg("Could not save file -- use quit! to abort changes, or w filename");
  1087. X        }
  1088. X        if (!oldflag)
  1089. X            clrflag(file, MODIFIED);
  1090. X    }
  1091. X}
  1092. X
  1093. X
  1094. X/*ARGSUSED*/
  1095. Xvoid cmd_args(frommark, tomark, cmd, bang, extra)
  1096. X    MARK    frommark, tomark;
  1097. X    CMD    cmd;
  1098. X    int    bang;
  1099. X    char    *extra;
  1100. X{
  1101. X    char    *scan;
  1102. X    char    *eow;
  1103. X    int    col;
  1104. X    int    arg;
  1105. X    int    addcols;
  1106. X    int    scrolled = 0;
  1107. X
  1108. X    /* if no extra names given, or just current name, then report the args
  1109. X     * we have now.
  1110. X     */
  1111. X    if (!extra || !*extra)
  1112. X    {
  1113. X        for (scan = args, col=arg=0; *scan; )
  1114. X        {
  1115. X            while (*scan && isascii(*scan) && isspace(*scan))
  1116. X                scan++;
  1117. X            eow = scan;
  1118. X            while (*eow && (!isascii(*++eow) || !isspace(*eow)))
  1119. X                ;
  1120. X            if (arg == argno)
  1121. X                addcols = 2;
  1122. X            else
  1123. X                addcols = 0;    
  1124. X            if (col+addcols+(int)(eow-scan)+1>=COLS)
  1125. X            {
  1126. X                addch('\n');
  1127. X                scrolled=1;
  1128. X                col=0;
  1129. X            }
  1130. X            else if (arg)
  1131. X            {    qaddch(' ');
  1132. X                col++;
  1133. X            }
  1134. X            if (arg == argno)
  1135. X                qaddch('[');
  1136. X            while (scan < eow)
  1137. X            {    qaddch(*scan++);
  1138. X                col++;
  1139. X            }
  1140. X            if (arg == argno)
  1141. X                qaddch(']');    
  1142. X            arg++;    
  1143. X            col+=addcols;
  1144. X        }
  1145. X        /* write a trailing newline */
  1146. X        if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
  1147. X            addch('\n');
  1148. X        exrefresh();    
  1149. X    }
  1150. X    else /* new args list given */
  1151. X    {
  1152. X        strcpy(args, extra);
  1153. X        argno = -1; /* before the first, so :next will go to first */
  1154. X
  1155. X        /* count the names */
  1156. X        for (nargs = 0, scan = args; *scan; nargs++)
  1157. X        {
  1158. X            while (*scan && (!isascii(*scan) || !isspace(*scan)))
  1159. X            {
  1160. X                scan++;
  1161. X            }
  1162. X            while (isascii(*scan) && isspace(*scan))
  1163. X            {
  1164. X                scan++;
  1165. X            }
  1166. X        }
  1167. X        msg("%d files to edit", nargs);
  1168. X    }
  1169. X}
  1170. X
  1171. X
  1172. X/*ARGSUSED*/
  1173. Xvoid cmd_cd(frommark, tomark, cmd, bang, extra)
  1174. X    MARK    frommark, tomark;
  1175. X    CMD    cmd;
  1176. X    int    bang;
  1177. X    char    *extra;
  1178. X{
  1179. X    char    *getenv();
  1180. X
  1181. X    /* default directory name is $HOME */
  1182. X    if (!*extra)
  1183. X    {
  1184. X        extra = getenv("HOME");
  1185. X        if (!extra)
  1186. X        {
  1187. X            msg("environment variable $HOME not set");
  1188. X            return;
  1189. X        }
  1190. X    }
  1191. X
  1192. X    /* go to the directory */
  1193. X    if (chdir(extra) < 0)
  1194. X    {
  1195. X        perror(extra);
  1196. X    }
  1197. X}
  1198. X
  1199. X
  1200. X/*ARGSUSED*/
  1201. Xvoid cmd_map(frommark, tomark, cmd, bang, extra)
  1202. X    MARK    frommark, tomark;
  1203. X    CMD    cmd;
  1204. X    int    bang;
  1205. X    char    *extra;
  1206. X{
  1207. X    char    *mapto;
  1208. X
  1209. X    /* "map" with no extra will dump the map table contents */
  1210. X    if (!*extra)
  1211. X    {
  1212. X        dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
  1213. X    }
  1214. X    else
  1215. X    {
  1216. X        /* "extra" is key to map, followed my what it maps to */
  1217. X        for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
  1218. X        {
  1219. X        }
  1220. X        while (*mapto == ' ' || *mapto == '\t')
  1221. X        {
  1222. X            *mapto++ = '\0';
  1223. X        }
  1224. X
  1225. X        mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
  1226. X    }
  1227. X}
  1228. X
  1229. X
  1230. X/*ARGSUSED*/
  1231. Xvoid cmd_set(frommark, tomark, cmd, bang, extra)
  1232. X    MARK    frommark, tomark;
  1233. X    CMD    cmd;
  1234. X    int    bang;
  1235. X    char    *extra;
  1236. X{
  1237. X    if (!*extra)
  1238. X    {
  1239. X        dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
  1240. X    }
  1241. X    else if (!strcmp(extra, "all"))
  1242. X    {
  1243. X        dumpopts(TRUE);    /* "TRUE" means "dump all" - even unset vars */
  1244. X    }
  1245. X    else
  1246. X    {
  1247. X        setopts(extra);
  1248. X
  1249. X        /* That option may have affected the appearence of text */
  1250. X        changes++;
  1251. X    }
  1252. X}
  1253. X
  1254. X/*ARGSUSED*/
  1255. Xvoid cmd_tag(frommark, tomark, cmd, bang, extra)
  1256. X    MARK    frommark, tomark;
  1257. X    CMD    cmd;
  1258. X    int    bang;
  1259. X    char    *extra;
  1260. X{
  1261. X    char    *scan;    /* used to scan through the tmpblk.c */
  1262. X    char    *cmp;    /* char of tag name we're comparing, or NULL */
  1263. X    char    *end;    /* marks the end of chars in tmpblk.c */
  1264. X    int    fd;    /* file descriptor used to read the file */
  1265. X#ifndef NO_MAGIC
  1266. X    char    wasmagic; /* preserves the original state of o_magic */
  1267. X#endif
  1268. X    static char prevtag[30];
  1269. X
  1270. X    /* if no tag is given, use the previous tag */
  1271. X    if (!extra || !*extra)
  1272. X    {
  1273. X        if (!*prevtag)
  1274. X        {
  1275. X            msg("No previous tag");
  1276. X            return;
  1277. X        }
  1278. X        extra = prevtag;
  1279. X    }
  1280. X    else
  1281. X    {
  1282. X        strncpy(prevtag, extra, sizeof prevtag);
  1283. X    }
  1284. X
  1285. X    /* open the tags file */
  1286. X    fd = open(TAGS, O_RDONLY);
  1287. X    if (fd < 0)
  1288. X    {
  1289. X        msg("No tags file");
  1290. X        return;
  1291. X    }
  1292. X
  1293. X    /* Hmmm... this would have been a lot easier with <stdio.h> */
  1294. X
  1295. X    /* find the line with our tag in it */
  1296. X    for(scan = end = tmpblk.c, cmp = extra; ; scan++)
  1297. X    {
  1298. X        /* read a block, if necessary */
  1299. X        if (scan >= end)
  1300. X        {
  1301. X            end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
  1302. X            scan = tmpblk.c;
  1303. X            if (scan >= end)
  1304. X            {
  1305. X                msg("tag \"%s\" not found", extra);
  1306. X                close(fd);
  1307. X                return;
  1308. X            }
  1309. X        }
  1310. X
  1311. X        /* if we're comparing, compare... */
  1312. X        if (cmp)
  1313. X        {
  1314. X            /* matched??? wow! */
  1315. X            if (!*cmp && *scan == '\t')
  1316. X            {
  1317. X                break;
  1318. X            }
  1319. X            if (*cmp++ != *scan)
  1320. X            {
  1321. X                /* failed! skip to newline */
  1322. X                cmp = (char *)0;
  1323. X            }
  1324. X        }
  1325. X
  1326. X        /* if we're skipping to newline, do it fast! */
  1327. X        if (!cmp)
  1328. X        {
  1329. X            while (scan < end && *scan != '\n')
  1330. X            {
  1331. X                scan++;
  1332. X            }
  1333. X            if (scan < end)
  1334. X            {
  1335. X                cmp = extra;
  1336. X            }
  1337. X        }
  1338. X    }
  1339. X
  1340. X    /* found it! get the rest of the line into memory */
  1341. X    for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
  1342. X    {
  1343. X        *cmp++ = *scan++;
  1344. X    }
  1345. X    if (scan == end)
  1346. X    {
  1347. X        tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
  1348. X    }
  1349. X
  1350. X    /* we can close the tags file now */
  1351. X    close(fd);
  1352. X
  1353. X    /* extract the filename from the line, and edit the file */
  1354. X    for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
  1355. X    {
  1356. X    }
  1357. X    *cmp++ = '\0';
  1358. X    if (strcmp(origname, tmpblk.c) != 0)
  1359. X    {
  1360. X        if (!tmpabort(bang))
  1361. X        {
  1362. X            msg("Use :tag! to abort changes, or :w to save changes");
  1363. X            return;
  1364. X        }
  1365. X        tmpstart(tmpblk.c);
  1366. X    }
  1367. X
  1368. X    /* move to the desired line (or to line 1 if that fails) */
  1369. X#ifndef NO_MAGIC
  1370. X    wasmagic = *o_magic;
  1371. X    *o_magic = FALSE;
  1372. X#endif
  1373. X    cursor = MARK_FIRST;
  1374. X    linespec(cmp, &cursor);
  1375. X    if (cursor == MARK_UNSET)
  1376. X    {
  1377. X        cursor = MARK_FIRST;
  1378. X    }
  1379. X#ifndef NO_MAGIC
  1380. X    *o_magic = wasmagic;
  1381. X#endif
  1382. X}
  1383. X
  1384. X
  1385. X
  1386. X/*ARGSUSED*/
  1387. Xvoid cmd_visual(frommark, tomark, cmd, bang, extra)
  1388. X    MARK    frommark, tomark;
  1389. X    CMD    cmd;
  1390. X    int    bang;
  1391. X    char    *extra;
  1392. X{
  1393. X    mode = MODE_VI;
  1394. X    msg("");
  1395. X}
  1396. X
  1397. X
  1398. X
  1399. X
  1400. X
  1401. X/* describe this version of the program */
  1402. X/*ARGSUSED*/
  1403. Xvoid cmd_version(frommark, tomark, cmd, bang, extra)
  1404. X    MARK    frommark;
  1405. X    MARK    tomark;
  1406. X    CMD    cmd;
  1407. X    int    bang;
  1408. X    char    *extra;
  1409. X{
  1410. X#ifndef DATE
  1411. X    msg("%s", VERSION);
  1412. X#else
  1413. X    msg("%s  (%s)", VERSION, DATE);
  1414. X#endif
  1415. X#ifdef COMPILED_BY
  1416. X    msg("Compiled by %s", COMPILED_BY);
  1417. X#endif
  1418. X#ifdef CREDIT
  1419. X    msg("%s", CREDIT);
  1420. X#endif
  1421. X#ifdef COPYING
  1422. X    msg("%s", COPYING);
  1423. X#endif
  1424. X}
  1425. X
  1426. X
  1427. X#ifndef NO_MKEXRC
  1428. X/* make a .exrc file which describes the current configuration */
  1429. X/*ARGSUSED*/
  1430. Xvoid cmd_mkexrc(frommark, tomark, cmd, bang, extra)
  1431. X    MARK    frommark;
  1432. X    MARK    tomark;
  1433. X    CMD    cmd;
  1434. X    int    bang;
  1435. X    char    *extra;
  1436. X{
  1437. X    int    fd;
  1438. X
  1439. X    /* the default name for the .exrc file EXRC */
  1440. X    if (!*extra)
  1441. X    {
  1442. X        extra = EXRC;
  1443. X    }
  1444. X
  1445. X    /* create the .exrc file */
  1446. X    fd = creat(extra, FILEPERMS);
  1447. X    if (fd < 0)
  1448. X    {
  1449. X        msg("Couldn't create a new \"%s\" file", extra);
  1450. X        return;
  1451. X    }
  1452. X
  1453. X    /* save stuff */
  1454. X    savekeys(fd);
  1455. X    saveopts(fd);
  1456. X#ifndef NO_DIGRAPH
  1457. X    savedigs(fd);
  1458. X#endif
  1459. X#ifndef    NO_ABBR
  1460. X    saveabbr(fd);
  1461. X#endif
  1462. X
  1463. X    /* close the file */
  1464. X    close(fd);
  1465. X    msg("Created a new \"%s\" file", extra);
  1466. X}
  1467. X#endif
  1468. X
  1469. X#ifndef NO_DIGRAPH
  1470. X/*ARGSUSED*/
  1471. Xvoid cmd_digraph(frommark, tomark, cmd, bang, extra)
  1472. X    MARK    frommark;
  1473. X    MARK    tomark;
  1474. X    CMD    cmd;
  1475. X    int    bang;
  1476. X    char    *extra;
  1477. X{
  1478. X    do_digraph(bang, extra);
  1479. X}
  1480. X#endif
  1481. X
  1482. X
  1483. X#ifndef NO_ERRLIST 
  1484. Xstatic char    errfile[256];    /* the name of a file containing an error */
  1485. Xstatic long    errline;    /* the line number for an error */
  1486. X
  1487. X/* This static function tries to parse an error message.
  1488. X *
  1489. X * For most compilers, the first word is taken to be the name of the erroneous
  1490. X * file, and the first number after that is taken to be the line number where
  1491. X * the error was detected.  The description of the error follows, possibly
  1492. X * preceded by an "error ... :" or "warning ... :" label which is skipped.
  1493. X *
  1494. X * For Coherent, error messages look like "line#: filename: message".
  1495. X *
  1496. X * For non-error lines, or unparsable error lines, this function returns NULL.
  1497. X * Normally, though, it alters errfile and errline, and returns a pointer to
  1498. X * the description.
  1499. X */
  1500. Xstatic char *parse_errmsg(text)
  1501. X    REG char    *text;
  1502. X{
  1503. X    REG char    *cpy;
  1504. X    long        atol();
  1505. X# if COHERENT || TOS /* any Mark Williams compiler */
  1506. X    /* Get the line number.  If no line number, then ignore this line. */
  1507. X    errline = atol(text);
  1508. X    if (errline == 0L)
  1509. X        return (char *)0;
  1510. X
  1511. X    /* Skip to the start of the filename */
  1512. X    while (*text && *text++ != ':')
  1513. X    {
  1514. X    }
  1515. X    if (!*text++)
  1516. X        return (char *)0;
  1517. X
  1518. X    /* copy the filename to errfile */
  1519. X    for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
  1520. X    {
  1521. X    }
  1522. X    if (!*text++)
  1523. X        return (char *)0;
  1524. X    cpy[-1] = '\0';
  1525. X
  1526. X    return text;
  1527. X# else /* not a Mark Williams compiler */
  1528. X    char        *errmsg;
  1529. X
  1530. X    /* the error message is the whole line, by default */
  1531. X    errmsg = text;
  1532. X
  1533. X    /* skip leading garbage */
  1534. X    while (*text && !(isascii(*text) && isalnum(*text)))
  1535. X    {
  1536. X        text++;
  1537. X    }
  1538. X
  1539. X    /* copy over the filename */
  1540. X    cpy = errfile;
  1541. X    while(isascii(*text) && isalnum(*text) || *text == '.')
  1542. X    {
  1543. X        *cpy++ = *text++;
  1544. X    }
  1545. X    *cpy = '\0';
  1546. X
  1547. X    /* ignore the name "Error" and filenames that contain a '/' */
  1548. X    if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
  1549. X    {
  1550. X        return (char *)0;
  1551. X    }
  1552. X
  1553. X    /* skip garbage between filename and line number */
  1554. X    while (*text && !(isascii(*text) && isdigit(*text)))
  1555. X    {
  1556. X        text++;
  1557. X    }
  1558. X
  1559. X    /* if the number is part of a larger word, then ignore this line */
  1560. X    if (*text && isascii(text[-1]) && isalpha(text[-1]))
  1561. X    {
  1562. X        return (char *)0;
  1563. X    }
  1564. X
  1565. X    /* get the error line */
  1566. X    errline = 0L;
  1567. X    while (isascii(*text) && isdigit(*text))
  1568. X    {
  1569. X        errline *= 10;
  1570. X        errline += (*text - '0');
  1571. X        text++;
  1572. X    }
  1573. X
  1574. X    /* any line which lacks a filename or line number should be ignored */
  1575. X    if (!errfile[0] || !errline)
  1576. X    {
  1577. X        return (char *)0;
  1578. X    }
  1579. X
  1580. X    /* locate the beginning of the error description */
  1581. X    while (*text && isascii(*text) && !isspace(*text))
  1582. X    {
  1583. X        text++;
  1584. X    }
  1585. X    while (*text)
  1586. X    {
  1587. X#  ifndef CRUNCH
  1588. X        /* skip "error #:" and "warning #:" clauses */
  1589. X        if (!strncmp(text + 1, "rror ", 5)
  1590. X         || !strncmp(text + 1, "arning ", 7)
  1591. X         || !strncmp(text + 1, "atal error", 10))
  1592. X        {
  1593. X            do
  1594. X            {
  1595. X                text++;
  1596. X            } while (*text && *text != ':');
  1597. X            continue;
  1598. X        }
  1599. X#  endif
  1600. X
  1601. X        /* anything other than whitespace or a colon is important */
  1602. X        if (!isascii(*text) || (!isspace(*text) && *text != ':'))
  1603. X        {
  1604. X            errmsg = text;
  1605. X            break;
  1606. X        }
  1607. X
  1608. X        /* else keep looking... */
  1609. X        text++;
  1610. X    }
  1611. X
  1612. X    return errmsg;
  1613. X# endif /* not COHERENT */
  1614. X}
  1615. X
  1616. X/*ARGSUSED*/
  1617. Xvoid cmd_errlist(frommark, tomark, cmd, bang, extra)
  1618. X    MARK    frommark, tomark;
  1619. X    CMD    cmd;
  1620. X    int    bang;
  1621. X    char    *extra;
  1622. X{
  1623. X    static long    endline;/* original number of lines in this file */
  1624. X    static long    offset;    /* offset of the next line in the errlist file */
  1625. X    static int    fd = -2;/* fd of the errlist file */
  1626. X    int        i;
  1627. X    char        *errmsg;
  1628. X
  1629. X    /* if a new errlist file is named, open it */
  1630. X    if (extra && extra[0])
  1631. X    {
  1632. X        /* close the old one */
  1633. X        if (fd >= 0)
  1634. X        {
  1635. X            close(fd);
  1636. X        }
  1637. X
  1638. X        fd = open(extra, O_RDONLY);
  1639. X        offset = 0L;
  1640. X    }
  1641. X    else if (fd < 0)
  1642. X    {
  1643. X        fd = open(ERRLIST, O_RDONLY);
  1644. X        offset = 0L;
  1645. X    }
  1646. X
  1647. X    /* do we have an errlist file now? */
  1648. X    if (fd < 0)
  1649. X    {
  1650. X        msg("There is no errlist file");
  1651. X        beep();
  1652. X        return;
  1653. X    }
  1654. X
  1655. X    /* find the next error message in the file */
  1656. X    do
  1657. X    {
  1658. X        /* read the next line from the errlist */
  1659. X        lseek(fd, offset, 0);
  1660. X        if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
  1661. X        {
  1662. X            msg("No more errors");
  1663. X            beep();
  1664. X            close(fd);
  1665. X            return;
  1666. X        }
  1667. X        for (i = 0; tmpblk.c[i] != '\n'; i++)
  1668. X        {
  1669. X        }
  1670. X        tmpblk.c[i++] = 0;
  1671. X
  1672. X        /* look for an error message in the line */
  1673. X        errmsg = parse_errmsg(tmpblk.c);
  1674. X        if (!errmsg)
  1675. X        {
  1676. X            offset += i;
  1677. X        }
  1678. X
  1679. X    } while (!errmsg);
  1680. X
  1681. X    /* switch to the file containing the error, if this isn't it */
  1682. X    if (strcmp(origname, errfile))
  1683. X    {
  1684. X        if (!tmpabort(bang))
  1685. X        {
  1686. X            msg("Use :er! to abort changes, or :w to save changes");
  1687. X            beep();
  1688. X            return;
  1689. X        }
  1690. X        tmpstart(errfile);
  1691. X        endline = nlines;
  1692. X    }
  1693. X    else if (endline == 0L)
  1694. X    {
  1695. X        endline = nlines;
  1696. X    }
  1697. X
  1698. X    /* go to the line where the error was detected */
  1699. X    cursor = MARK_AT_LINE(errline + (nlines - endline));
  1700. X    if (cursor > MARK_LAST)
  1701. X    {
  1702. X        cursor = MARK_LAST;
  1703. X    }
  1704. X    if (mode == MODE_VI)
  1705. X    {
  1706. X        redraw(cursor, FALSE);
  1707. X    }
  1708. X
  1709. X    /* display the error message */
  1710. X    if (nlines > endline)
  1711. X    {
  1712. X        msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
  1713. X    }
  1714. X    else if (nlines < endline)
  1715. X    {
  1716. X        msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
  1717. X    }
  1718. X    else
  1719. X    {
  1720. X        msg("line %ld: %.65s", errline, errmsg);
  1721. X    }
  1722. X
  1723. X    /* remember where the NEXT error line will start */
  1724. X    offset += i;
  1725. X}
  1726. X
  1727. X
  1728. X/*ARGSUSED*/
  1729. Xvoid cmd_make(frommark, tomark, cmd, bang, extra)
  1730. X    MARK    frommark, tomark;
  1731. X    CMD    cmd;
  1732. X    int    bang;
  1733. X    char    *extra;
  1734. X{
  1735. X    BLK    buf;
  1736. X
  1737. X    /* if the file hasn't been saved, then complain unless ! */
  1738. X    if (tstflag(file, MODIFIED) && !bang)
  1739. X    {
  1740. X        msg("\"%s\" not saved yet", origname);
  1741. X        return;
  1742. X    }
  1743. X
  1744. X    /* build the command */
  1745. X    sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
  1746. X    qaddstr(buf.c);
  1747. X    addch('\n');
  1748. X
  1749. X    /* run the command, with curses temporarily disabled */
  1750. X    suspend_curses();
  1751. X    system(buf.c);
  1752. X    resume_curses(mode == MODE_EX);
  1753. X    if (mode == MODE_COLON)
  1754. X        mode = MODE_VI;
  1755. X
  1756. X    /* run the "errlist" command */
  1757. X    cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
  1758. X}
  1759. X#endif
  1760. X
  1761. X
  1762. X#ifndef NO_ABBR
  1763. X/*ARGSUSED*/
  1764. Xvoid cmd_abbr(frommark, tomark, cmd, bang, extra)
  1765. X    MARK    frommark, tomark;
  1766. X    CMD    cmd;
  1767. X    int    bang;
  1768. X    char    *extra;
  1769. X{
  1770. X    do_abbr(extra);
  1771. X}
  1772. X#endif
  1773. eof
  1774. if test `wc -c <cmd1.c` -ne 23975
  1775. then
  1776. echo cmd1.c damaged!
  1777. fi
  1778. fi
  1779.  
  1780. if test -f cmd2.c -a "$1" != -f
  1781. then
  1782. echo Will not overwrite cmd2.c
  1783. else
  1784. echo Extracting cmd2.c
  1785. sed 's/^X//' >cmd2.c <<\eof
  1786. X/* cmd2.c */
  1787. X
  1788. X/* Author:
  1789. X *    Steve Kirkendall
  1790. X *    14407 SW Teal Blvd. #C
  1791. X *    Beaverton, OR 97005
  1792. X *    kirkenda@cs.pdx.edu
  1793. X */
  1794. X
  1795. X
  1796. X/* This file contains some of the commands - mostly ones that change text */
  1797. X
  1798. X#include <ctype.h>
  1799. X#include "config.h"
  1800. X#include "vi.h"
  1801. X#include "regexp.h"
  1802. X#if TOS
  1803. X# include <stat.h>
  1804. X#else
  1805. X# if OSK
  1806. X#  include "osk.h"
  1807. X# else
  1808. X#  include <sys/stat.h>
  1809. X# endif
  1810. X#endif
  1811. X
  1812. X
  1813. X/*ARGSUSED*/
  1814. Xvoid cmd_substitute(frommark, tomark, cmd, bang, extra)
  1815. X    MARK    frommark;
  1816. X    MARK    tomark;
  1817. X    CMD    cmd;
  1818. X    int    bang;
  1819. X    char    *extra;    /* rest of the command line */
  1820. X{
  1821. X    char    *line;    /* a line from the file */
  1822. X    regexp    *re;    /* the compiled search expression */
  1823. X    char    *subst;    /* the substitution string */
  1824. X    char    *opt;    /* substitution options */
  1825. X    long    l;    /* a line number */
  1826. X    char    *s, *d;    /* used during subtitutions */
  1827. X    char    *conf;    /* used during confirmation */
  1828. X    long    chline;    /* # of lines changed */
  1829. X    long    chsub;    /* # of substitutions made */
  1830. X    static    optp;    /* boolean option: print when done? */
  1831. X    static    optg;    /* boolean option: substitute globally in line? */
  1832. X    static    optc;    /* boolean option: confirm before subst? */
  1833. X
  1834. X
  1835. X    /* for now, assume this will fail */
  1836. X    rptlines = -1L;
  1837. X
  1838. X    if (cmd == CMD_SUBAGAIN)
  1839. X    {
  1840. X#ifndef NO_MAGIC
  1841. X        if (*o_magic)
  1842. X            subst = "~";
  1843. X        else
  1844. X#endif
  1845. X        subst = "\\~";
  1846. X        re = regcomp("");
  1847. X
  1848. X        /* if visual "&", then turn off the "p" and "c" options */
  1849. X        if (bang)
  1850. X        {
  1851. X            optp = optc = FALSE;
  1852. X        }
  1853. X    }
  1854. X    else
  1855. X    {
  1856. X        /* make sure we got a search pattern */
  1857. X        if (*extra != '/' && *extra != '?')
  1858. X        {
  1859. X            msg("Usage: s/regular expression/new text/");
  1860. X            return;
  1861. X        }
  1862. X
  1863. X        /* parse & compile the search pattern */
  1864. X        subst = parseptrn(extra);
  1865. X        re = regcomp(extra + 1);
  1866. X    }
  1867. X
  1868. X    /* abort if RE error -- error message already given by regcomp() */
  1869. X    if (!re)
  1870. X    {
  1871. X        return;
  1872. X    }
  1873. X
  1874. X    if (cmd == CMD_SUBSTITUTE)
  1875. X    {
  1876. X        /* parse the substitution string & find the option string */
  1877. X        for (opt = subst; *opt && *opt != *extra; opt++)
  1878. X        {
  1879. X            if (*opt == '\\' && opt[1])
  1880. X            {
  1881. X                opt++;
  1882. X            }
  1883. X        }
  1884. X        if (*opt)
  1885. X        {
  1886. X            *opt++ = '\0';
  1887. X        }
  1888. X
  1889. X        /* analyse the option string */
  1890. X        if (!*o_edcompatible)
  1891. X        {
  1892. X            optp = optg = optc = FALSE;
  1893. X        }
  1894. X        while (*opt)
  1895. X        {
  1896. X            switch (*opt++)
  1897. X            {
  1898. X              case 'p':    optp = !optp;    break;
  1899. X              case 'g':    optg = !optg;    break;
  1900. X              case 'c':    optc = !optc;    break;
  1901. X              case ' ':
  1902. X              case '\t':            break;
  1903. X              default:
  1904. X                msg("Subst options are p, c, and g -- not %c", opt[-1]);
  1905. X                return;
  1906. X            }
  1907. X        }
  1908. X    }
  1909. X
  1910. X    /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
  1911. X    if ((optc || optp) && mode == MODE_VI)
  1912. X    {
  1913. X        addch('\n');
  1914. X        exrefresh();
  1915. X    }
  1916. X
  1917. X    ChangeText
  1918. X    {
  1919. X        /* reset the change counters */
  1920. X        chline = chsub = 0L;
  1921. X
  1922. X        /* for each selected line */
  1923. X        for (l = markline(frommark); l <= markline(tomark); l++)
  1924. X        {
  1925. X            /* fetch the line */
  1926. X            line = fetchline(l);
  1927. X
  1928. X            /* if it contains the search pattern... */
  1929. X            if (regexec(re, line, TRUE))
  1930. X            {
  1931. X                /* increment the line change counter */
  1932. X                chline++;
  1933. X
  1934. X                /* initialize the pointers */
  1935. X                s = line;
  1936. X                d = tmpblk.c;
  1937. X
  1938. X                /* do once or globally ... */
  1939. X                do
  1940. X                {
  1941. X#ifndef CRUNCH
  1942. X                    /* confirm, if necessary */
  1943. X                    if (optc)
  1944. X                    {
  1945. X                        for (conf = line; conf < re->startp[0]; conf++)
  1946. X                            addch(*conf);
  1947. X                        standout();
  1948. X                        for ( ; conf < re->endp[0]; conf++)
  1949. X                            addch(*conf);
  1950. X                        standend();
  1951. X                        for (; *conf; conf++)
  1952. X                            addch(*conf);
  1953. X                        addch('\n');
  1954. X                        exrefresh();
  1955. X                        if (getkey(0) != 'y')
  1956. X                        {
  1957. X                            /* copy accross the original chars */
  1958. X                            while (s < re->endp[0])
  1959. X                                *d++ = *s++;
  1960. X
  1961. X                            /* skip to next match on this line, if any */
  1962. X                            continue;
  1963. X                        }
  1964. X                    }
  1965. X#endif /* not CRUNCH */
  1966. X
  1967. X                    /* increment the substitution change counter */
  1968. X                    chsub++;
  1969. X
  1970. X                    /* this may be the first line to redraw */
  1971. X                    redrawrange(l, l + 1L, l + 1L);
  1972. X
  1973. X                    /* copy stuff from before the match */
  1974. X                    while (s < re->startp[0])
  1975. X                    {
  1976. X                        *d++ = *s++;
  1977. X                    }
  1978. X    
  1979. X                    /* substitute for the matched part */
  1980. X                    regsub(re, subst, d);
  1981. X                    s = re->endp[0];
  1982. X                    d += strlen(d);
  1983. X
  1984. X                } while (optg && regexec(re, s, FALSE));
  1985. X
  1986. X                /* copy stuff from after the match */
  1987. X                while (*d++ = *s++)    /* yes, ASSIGNMENT! */
  1988. X                {
  1989. X                }
  1990. X
  1991. X                /* replace the old version of the line with the new */
  1992. X                d[-1] = '\n';
  1993. X                d[0] = '\0';
  1994. X                change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
  1995. X
  1996. X                /* if supposed to print it, do so */
  1997. X                if (optp)
  1998. X                {
  1999. X                    addstr(tmpblk.c);
  2000. X                    exrefresh();
  2001. X                }
  2002. X
  2003. X                /* move the cursor to that line */
  2004. X                cursor = MARK_AT_LINE(l);
  2005. X            }
  2006. X        }
  2007. X    }
  2008. X
  2009. X    /* tweak for redrawing */
  2010. X    mustredraw = TRUE;
  2011. X
  2012. X    /* free the regexp */
  2013. X    free(re);
  2014. X
  2015. X    /* if done from within a ":g" command, then finish silently */
  2016. X    if (doingglobal)
  2017. X    {
  2018. X        rptlines = chline;
  2019. X        rptlabel = "changed";
  2020. X        return;
  2021. X    }
  2022. X
  2023. X    /* Reporting */
  2024. X    if (chsub == 0)
  2025. X    {
  2026. X        msg("Substitution failed");
  2027. X    }
  2028. X    else if (chline >= *o_report)
  2029. X    {
  2030. X        msg("%ld substitutions on %ld lines", chsub, chline);
  2031. X    }
  2032. X}
  2033. X
  2034. X
  2035. X
  2036. X
  2037. X/*ARGSUSED*/
  2038. Xvoid cmd_delete(frommark, tomark, cmd, bang, extra)
  2039. X    MARK    frommark;
  2040. X    MARK    tomark;
  2041. X    CMD    cmd;
  2042. X    int    bang;
  2043. X    char    *extra;
  2044. X{
  2045. X    MARK    curs2;    /* an altered form of the cursor */
  2046. X
  2047. X    /* choose your cut buffer */
  2048. X    if (*extra == '"')
  2049. X    {
  2050. X        extra++;
  2051. X    }
  2052. X    if (*extra)
  2053. X    {
  2054. X        cutname(*extra);
  2055. X    }
  2056. X
  2057. X    /* make sure we're talking about whole lines here */
  2058. X    frommark = frommark & ~(BLKSIZE - 1);
  2059. X    tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  2060. X
  2061. X    /* yank the lines */
  2062. X    cut(frommark, tomark);
  2063. X
  2064. X    /* if CMD_DELETE then delete the lines */
  2065. X    if (cmd != CMD_YANK)
  2066. X    {
  2067. X        curs2 = cursor;
  2068. X        ChangeText
  2069. X        {
  2070. X            /* delete the lines */
  2071. X            delete(frommark, tomark);
  2072. X        }
  2073. X        if (curs2 > tomark)
  2074. X        {
  2075. X            cursor = curs2 - tomark + frommark;
  2076. X        }
  2077. X        else if (curs2 > frommark)
  2078. X        {
  2079. X            cursor = frommark;
  2080. X        }
  2081. X    }
  2082. X}
  2083. X
  2084. X
  2085. X/*ARGSUSED*/
  2086. Xvoid cmd_append(frommark, tomark, cmd, bang, extra)
  2087. X    MARK    frommark;
  2088. X    MARK    tomark;
  2089. X    CMD    cmd;
  2090. X    int    bang;
  2091. X    char    *extra;
  2092. X{
  2093. X    long    l;    /* line counter */
  2094. X
  2095. X    ChangeText
  2096. X    {
  2097. X        /* if we're doing a change, delete the old version */
  2098. X        if (cmd == CMD_CHANGE)
  2099. X        {
  2100. X            /* delete 'em */
  2101. X            cmd_delete(frommark, tomark, cmd, bang, extra);
  2102. X        }
  2103. X
  2104. X        /* new lines start at the frommark line, or after it */
  2105. X        l = markline(frommark);
  2106. X        if (cmd == CMD_APPEND)
  2107. X        {
  2108. X             l++;
  2109. X        }
  2110. X
  2111. X        /* get lines until no more lines, or "." line, and insert them */
  2112. X        while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
  2113. X        {
  2114. X            addch('\n');
  2115. X            if (!strcmp(tmpblk.c, "."))
  2116. X            {
  2117. X                break;
  2118. X            }
  2119. X
  2120. X            strcat(tmpblk.c, "\n");
  2121. X            add(MARK_AT_LINE(l), tmpblk.c);
  2122. X            l++;
  2123. X        }
  2124. X    }
  2125. X
  2126. X    /* on the odd chance that we're calling this from vi mode ... */
  2127. X    redraw(MARK_UNSET, FALSE);
  2128. X}
  2129. X
  2130. X
  2131. X/*ARGSUSED*/
  2132. Xvoid cmd_put(frommark, tomark, cmd, bang, extra)
  2133. X    MARK    frommark;
  2134. X    MARK    tomark;
  2135. X    CMD    cmd;
  2136. X    int    bang;
  2137. X    char    *extra;
  2138. X{
  2139. X    /* choose your cut buffer */
  2140. X    if (*extra == '"')
  2141. X    {
  2142. X        extra++;
  2143. X    }
  2144. X    if (*extra)
  2145. X    {
  2146. X        cutname(*extra);
  2147. X    }
  2148. X
  2149. X    /* paste it */
  2150. X    ChangeText
  2151. X    {
  2152. X        cursor = paste(frommark, TRUE, FALSE);
  2153. X    }
  2154. X}
  2155. X
  2156. X
  2157. X/*ARGSUSED*/
  2158. Xvoid cmd_join(frommark, tomark, cmd, bang, extra)
  2159. X    MARK    frommark;
  2160. X    MARK    tomark;
  2161. X    CMD    cmd;
  2162. X    int    bang;
  2163. X    char    *extra;
  2164. X{
  2165. X    long    l;
  2166. X    char    *scan;
  2167. X    int    len;    /* length of the new line */
  2168. X
  2169. X    /* if only one line is specified, assume the following one joins too */
  2170. X    if (markline(frommark) == nlines)
  2171. X    {
  2172. X        msg("Nothing to join with this line");
  2173. X        return;
  2174. X    }
  2175. X    if (markline(frommark) == markline(tomark))
  2176. X    {
  2177. X        tomark += BLKSIZE;
  2178. X    }
  2179. X
  2180. X    /* get the first line */
  2181. X    l = markline(frommark);
  2182. X    strcpy(tmpblk.c, fetchline(l));
  2183. X    len = strlen(tmpblk.c);
  2184. X
  2185. X    /* build the longer line */
  2186. X    while (++l <= markline(tomark))
  2187. X    {
  2188. X        /* get the next line */
  2189. X        scan = fetchline(l);
  2190. X
  2191. X        /* remove any leading whitespace */
  2192. X        while (*scan == '\t' || *scan == ' ')
  2193. X        {
  2194. X            scan++;
  2195. X        }
  2196. X
  2197. X        /* see if the line will fit */
  2198. X        if (strlen(scan) + len + 3 > BLKSIZE)
  2199. X        {
  2200. X            msg("Can't join -- the resulting line would be too long");
  2201. X            return;
  2202. X        }
  2203. X
  2204. X        /* catenate it, with a space (or two) in between */
  2205. X        if (len >= 1 &&
  2206. X            (tmpblk.c[len - 1] == '.'
  2207. X             || tmpblk.c[len - 1] == '?'
  2208. X             || tmpblk.c[len - 1] == '!'))
  2209. X        {
  2210. X             tmpblk.c[len++] = ' ';
  2211. X        }
  2212. X        tmpblk.c[len++] = ' ';
  2213. X        strcpy(tmpblk.c + len, scan);
  2214. X        len += strlen(scan);
  2215. X    }
  2216. X    tmpblk.c[len++] = '\n';
  2217. X    tmpblk.c[len] = '\0';
  2218. X
  2219. X    /* make the change */
  2220. X    ChangeText
  2221. X    {
  2222. X        frommark &= ~(BLKSIZE - 1);
  2223. X        tomark &= ~(BLKSIZE - 1);
  2224. X        tomark += BLKSIZE;
  2225. X        change(frommark, tomark, tmpblk.c);
  2226. X    }
  2227. X
  2228. X    /* Reporting... */
  2229. X    rptlines = markline(tomark) - markline(frommark) - 1L;
  2230. X    rptlabel = "joined";
  2231. X}
  2232. X
  2233. X
  2234. X
  2235. X/*ARGSUSED*/
  2236. Xvoid cmd_shift(frommark, tomark, cmd, bang, extra)
  2237. X    MARK    frommark;
  2238. X    MARK    tomark;
  2239. X    CMD    cmd;
  2240. X    int    bang;
  2241. X    char    *extra;
  2242. X{
  2243. X    long    l;    /* line number counter */
  2244. X    int    oldidx;    /* number of chars previously used for indent */
  2245. X    int    newidx;    /* number of chars in the new indent string */
  2246. X    int    oldcol;    /* previous indent amount */
  2247. X    int    newcol;    /* new indent amount */
  2248. X    char    *text;    /* pointer to the old line's text */
  2249. X
  2250. X    /* figure out how much of the screen we must redraw (for vi mode) */
  2251. X    if (markline(frommark) != markline(tomark))
  2252. X    {
  2253. X        mustredraw = TRUE;
  2254. X        redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
  2255. X    }
  2256. X
  2257. X    ChangeText
  2258. X    {
  2259. X        /* for each line to shift... */
  2260. X        for (l = markline(frommark); l <= markline(tomark); l++)
  2261. X        {
  2262. X            /* get the line - ignore empty lines unless ! mode */
  2263. X            text = fetchline(l);
  2264. X            if (!*text && !bang)
  2265. X                continue;
  2266. X
  2267. X            /* calc oldidx and oldcol */
  2268. X            for (oldidx = 0, oldcol = 0;
  2269. X                 text[oldidx] == ' ' || text[oldidx] == '\t';
  2270. X                 oldidx++)
  2271. X            {
  2272. X                if (text[oldidx] == ' ')
  2273. X                {
  2274. X                    oldcol += 1;
  2275. X                }
  2276. X                else
  2277. X                {
  2278. X                    oldcol += *o_tabstop - (oldcol % *o_tabstop);
  2279. X                }
  2280. X            }
  2281. X    
  2282. X            /* calc newcol */
  2283. X            if (cmd == CMD_SHIFTR)
  2284. X            {
  2285. X                newcol = oldcol + (*o_shiftwidth & 0xff);
  2286. X            }
  2287. X            else
  2288. X            {
  2289. X                newcol = oldcol - (*o_shiftwidth & 0xff);
  2290. X                if (newcol < 0)
  2291. X                    newcol = 0;
  2292. X            }
  2293. X
  2294. X            /* if no change, then skip to next line */
  2295. X            if (oldcol == newcol)
  2296. X                continue;
  2297. X
  2298. X            /* build a new indent string */
  2299. X            newidx = 0;
  2300. X            while (newcol >= *o_tabstop)
  2301. X            {
  2302. X                tmpblk.c[newidx++] = '\t';
  2303. X                newcol -= *o_tabstop;
  2304. X            }
  2305. X            while (newcol > 0)
  2306. X            {
  2307. X                tmpblk.c[newidx++] = ' ';
  2308. X                newcol--;
  2309. X            }
  2310. X            tmpblk.c[newidx] = '\0';
  2311. X            
  2312. X            /* change the old indent string into the new */
  2313. X            change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
  2314. X        }
  2315. X    }
  2316. X
  2317. X    /* Reporting... */
  2318. X    rptlines = markline(tomark) - markline(frommark) + 1L;
  2319. X    if (cmd == CMD_SHIFTR)
  2320. X    {
  2321. X        rptlabel = ">ed";
  2322. X    }
  2323. X    else
  2324. X    {
  2325. X        rptlabel = "<ed";
  2326. X    }
  2327. X}
  2328. X
  2329. X
  2330. X/*ARGSUSED*/
  2331. Xvoid cmd_read(frommark, tomark, cmd, bang, extra)
  2332. X    MARK    frommark;
  2333. X    MARK    tomark;
  2334. X    CMD    cmd;
  2335. X    int    bang;
  2336. X    char    *extra;
  2337. X{
  2338. X    int    fd, rc;    /* used while reading from the file */
  2339. X    char    *scan;    /* used for finding NUL characters */
  2340. X    int    hadnul;    /* boolean: any NULs found? */
  2341. X    int    addnl;    /* boolean: forced to add newlines? */
  2342. X    int    len;    /* number of chars in current line */
  2343. X    long    lines;    /* number of lines in current block */
  2344. X    struct stat statb;
  2345. X
  2346. X    /* special case: if ":r !cmd" then let the filter() function do it */
  2347. X    if (extra[0] == '!')
  2348. X    {
  2349. X        filter(frommark, MARK_UNSET, extra + 1);
  2350. X        return;
  2351. X    }
  2352. X
  2353. X    /* open the file */
  2354. X    fd = open(extra, O_RDONLY);
  2355. X    if (fd < 0)
  2356. X    {
  2357. X        msg("Can't open \"%s\"", extra);
  2358. X        return;
  2359. X    }
  2360. X
  2361. X#ifndef CRUNCH
  2362. X    if (stat(extra, &statb) < 0)
  2363. X    {
  2364. X        msg("Can't stat \"%s\"", extra);
  2365. X    }
  2366. X# if TOS
  2367. X    if (statb.st_mode & S_IJDIR)
  2368. X# else
  2369. X#  if OSK
  2370. X    if (statb.st_mode & S_IFDIR)
  2371. X#  else
  2372. X    if ((statb.st_mode & S_IFMT) != S_IFREG)
  2373. X#  endif
  2374. X# endif
  2375. X    {
  2376. X        msg("\"%s\" is not a regular file", extra);
  2377. X        return;
  2378. X    }
  2379. X#endif /* not CRUNCH */
  2380. X
  2381. X    /* get blocks from the file, and add them */
  2382. X    ChangeText
  2383. X    {
  2384. X        /* insertion starts at the line following frommark */
  2385. X        tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
  2386. X        len = 0;
  2387. X        hadnul = addnl = FALSE;
  2388. X
  2389. X        /* add an extra newline, so partial lines at the end of
  2390. X         * the file don't trip us up
  2391. X         */
  2392. X        add(tomark, "\n");
  2393. X
  2394. X        /* for each chunk of text... */
  2395. X        while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  2396. X        {
  2397. X            /* count newlines, convert NULs, etc. ... */
  2398. X            for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
  2399. X            {
  2400. X                /* break up long lines */
  2401. X                if (*scan != '\n' && len + 2 > BLKSIZE)
  2402. X                {
  2403. X                    *scan = '\n';
  2404. X                    addnl = TRUE;
  2405. X                }
  2406. X
  2407. X                /* protect against NUL chars in file */
  2408. X                if (!*scan)
  2409. X                {
  2410. X                    *scan = 0x80;
  2411. X                    hadnul = TRUE;
  2412. X                }
  2413. X
  2414. X                /* starting a new line? */
  2415. X                if (*scan == '\n')
  2416. X                {
  2417. X                    /* reset length at newline */
  2418. X                    len = 0;
  2419. X                    lines++;
  2420. X                }
  2421. X                else
  2422. X                {
  2423. X                    len++;
  2424. X                }
  2425. X            }
  2426. X
  2427. X            /* add the text */
  2428. X            *scan = '\0';
  2429. X            add(tomark, tmpblk.c);
  2430. X            tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
  2431. X        }
  2432. X
  2433. X        /* if partial last line, then retain that first newline */
  2434. X        if (len > 0)
  2435. X        {
  2436. X            msg("Last line had no newline");
  2437. X            tomark += BLKSIZE; /* <- for the rptlines calc */
  2438. X        }
  2439. X        else /* delete that first newline */
  2440. X        {
  2441. X            delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
  2442. X        }
  2443. X    }
  2444. X
  2445. X    /* close the file */
  2446. X    close(fd);
  2447. X
  2448. X    /* Reporting... */
  2449. X    rptlines = markline(tomark) - markline(frommark);
  2450. X    rptlabel = "read";
  2451. X
  2452. X    if (addnl)
  2453. X        msg("Newlines were added to break up long lines");
  2454. X    if (hadnul)
  2455. X        msg("NULs were converted to 0x80");
  2456. X}
  2457. X
  2458. X
  2459. X
  2460. X/*ARGSUSED*/
  2461. Xvoid cmd_undo(frommark, tomark, cmd, bang, extra)
  2462. X    MARK    frommark;
  2463. X    MARK    tomark;
  2464. X    CMD    cmd;
  2465. X    int    bang;
  2466. X    char    *extra;
  2467. X{
  2468. X    undo();
  2469. X}
  2470. X
  2471. X
  2472. X/* print the selected lines */
  2473. X/*ARGSUSED*/
  2474. Xvoid cmd_print(frommark, tomark, cmd, bang, extra)
  2475. X    MARK    frommark;
  2476. X    MARK    tomark;
  2477. X    CMD    cmd;
  2478. X    int    bang;
  2479. X    char    *extra;
  2480. X{
  2481. X    REG char    *scan;
  2482. X    REG long    l;
  2483. X    REG int        col;
  2484. X
  2485. X    for (l = markline(frommark); l <= markline(tomark); l++)
  2486. X    {
  2487. X        /* display a line number, if CMD_NUMBER */
  2488. X        if (cmd == CMD_NUMBER)
  2489. X        {
  2490. X            sprintf(tmpblk.c, "%6ld  ", l);
  2491. X            qaddstr(tmpblk.c);
  2492. X            col = 8;
  2493. X        }
  2494. X        else
  2495. X        {
  2496. X            col = 0;
  2497. X        }
  2498. X
  2499. X        /* get the next line & display it */
  2500. X        for (scan = fetchline(l); *scan; scan++)
  2501. X        {
  2502. X            /* expand tabs to the proper width */
  2503. X            if (*scan == '\t' && cmd != CMD_LIST)
  2504. X            {
  2505. X                do
  2506. X                {
  2507. X                    qaddch(' ');
  2508. X                    col++;
  2509. X                } while (col % *o_tabstop != 0);
  2510. X            }
  2511. X            else if (*scan >= 0 && *scan < ' ' || *scan == '\177')
  2512. X            {
  2513. X                qaddch('^');
  2514. X                qaddch(*scan ^ 0x40);
  2515. X                col += 2;
  2516. X            }
  2517. X            else if ((*scan & 0x80) && cmd == CMD_LIST)
  2518. X            {
  2519. X                sprintf(tmpblk.c, "\\%03o", *scan);
  2520. X                qaddstr(tmpblk.c);
  2521. X                col += 4;
  2522. X            }
  2523. X            else
  2524. X            {
  2525. X                qaddch(*scan);
  2526. X                col++;
  2527. X            }
  2528. X
  2529. X            /* wrap at the edge of the screen */
  2530. X            if (!has_AM && col >= COLS)
  2531. X            {
  2532. X                addch('\n');
  2533. X                col -= COLS;
  2534. X            }
  2535. X        }
  2536. X        if (cmd == CMD_LIST)
  2537. X        {
  2538. X            qaddch('$');
  2539. X        }
  2540. X        addch('\n');
  2541. X        exrefresh();
  2542. X    }
  2543. X}
  2544. X
  2545. X
  2546. X/* move or copy selected lines */
  2547. X/*ARGSUSED*/
  2548. Xvoid cmd_move(frommark, tomark, cmd, bang, extra)
  2549. X    MARK    frommark;
  2550. X    MARK    tomark;
  2551. X    CMD    cmd;
  2552. X    int    bang;
  2553. X    char    *extra;
  2554. X{
  2555. X    MARK    destmark;
  2556. X
  2557. X    /* parse the destination linespec.  No defaults.  Line 0 is okay */
  2558. X    destmark = cursor;
  2559. X    if (!strcmp(extra, "0"))
  2560. X    {
  2561. X        destmark = 0L;
  2562. X    }
  2563. X    else if (linespec(extra, &destmark) == extra || !destmark)
  2564. X    {
  2565. X        msg("invalid destination address");
  2566. X        return;
  2567. X    }
  2568. X
  2569. X    /* flesh the marks out to encompass whole lines */
  2570. X    frommark &= ~(BLKSIZE - 1);
  2571. X    tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  2572. X    destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
  2573. X
  2574. X    /* make sure the destination is valid */
  2575. X    if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
  2576. X    {
  2577. X        msg("invalid destination address");
  2578. X    }
  2579. X
  2580. X    /* Do it */
  2581. X    ChangeText
  2582. X    {
  2583. X        /* save the text to a cut buffer */
  2584. X        cutname('\0');
  2585. X        cut(frommark, tomark);
  2586. X
  2587. X        /* if we're not copying, delete the old text & adjust destmark */
  2588. X        if (cmd != CMD_COPY)
  2589. X        {
  2590. X            delete(frommark, tomark);
  2591. X            if (destmark >= frommark)
  2592. X            {
  2593. X                destmark -= (tomark - frommark);
  2594. X            }
  2595. X        }
  2596. X
  2597. X        /* add the new text */
  2598. X        paste(destmark, FALSE, FALSE);
  2599. X    }
  2600. X
  2601. X    /* move the cursor to the last line of the moved text */
  2602. X    cursor = destmark + (tomark - frommark) - BLKSIZE;
  2603. X    if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
  2604. X    {
  2605. X        cursor = MARK_LAST;
  2606. X    }
  2607. X
  2608. X    /* Reporting... */
  2609. X    rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
  2610. X}
  2611. X
  2612. X
  2613. X
  2614. X/* execute EX commands from a file */
  2615. X/*ARGSUSED*/
  2616. Xvoid cmd_source(frommark, tomark, cmd, bang, extra)
  2617. X    MARK    frommark;
  2618. X    MARK    tomark;
  2619. X    CMD    cmd;
  2620. X    int    bang;
  2621. X    char    *extra;
  2622. X{
  2623. X    /* must have a filename */
  2624. X    if (!*extra)
  2625. X    {
  2626. X        msg("\"source\" requires a filename");
  2627. X        return;
  2628. X    }
  2629. X
  2630. X    doexrc(extra);
  2631. X}
  2632. X
  2633. X
  2634. X#ifndef NO_AT
  2635. X/*ARGSUSED*/
  2636. Xvoid cmd_at(frommark, tomark, cmd, bang, extra)
  2637. X    MARK    frommark;
  2638. X    MARK    tomark;
  2639. X    CMD    cmd;
  2640. X    int    bang;
  2641. X    char    *extra;
  2642. X{
  2643. X    static    nest = FALSE;
  2644. X    int    result;
  2645. X    char    buf[MAXRCLEN];
  2646. X
  2647. X    /* don't allow nested macros */
  2648. X    if (nest)
  2649. X    {
  2650. X        msg("@ macros can't be nested");
  2651. X        return;
  2652. X    }
  2653. X    nest = TRUE;
  2654. X
  2655. X    /* require a buffer name */
  2656. X    if (*extra == '"')
  2657. X        extra++;
  2658. X    if (!*extra || !isascii(*extra) ||!islower(*extra))
  2659. X    {
  2660. X        msg("@ requires a cut buffer name (a-z)");
  2661. X    }
  2662. X
  2663. X    /* get the contents of the buffer */
  2664. X    result = cb2str(*extra, buf, (unsigned)(sizeof buf));
  2665. X    if (result <= 0)
  2666. X    {
  2667. X        msg("buffer \"%c is empty", *extra);
  2668. X    }
  2669. X    else if (result >= sizeof buf)
  2670. X    {
  2671. X        msg("buffer \"%c is too large to execute", *extra);
  2672. X    }
  2673. X    else
  2674. X    {
  2675. X        /* execute the contents of the buffer as ex commands */
  2676. X        exstring(buf, result);
  2677. X    }
  2678. X
  2679. X    nest = FALSE;
  2680. X}
  2681. X#endif
  2682. eof
  2683. if test `wc -c <cmd2.c` -ne 16797
  2684. then
  2685. echo cmd2.c damaged!
  2686. fi
  2687. fi
  2688.  
  2689. if test -f config.h -a "$1" != -f
  2690. then
  2691. echo Will not overwrite config.h
  2692. else
  2693. echo Extracting config.h
  2694. sed 's/^X//' >config.h <<\eof
  2695. X/*
  2696. X * vi configuration file
  2697. X * We try to automatically configure to various compilers and operating
  2698. X * systems. Extend the autoconf section as needed.
  2699. X */
  2700. X
  2701. X/*************************** autoconf section ************************/
  2702. X
  2703. X/* standard unix V (?) */
  2704. X#ifdef    M_SYSV
  2705. X# define UNIXV        1
  2706. X#endif
  2707. X
  2708. X/* xelos system, University of Ulm */
  2709. X#ifdef    xelos
  2710. X# define UNIXV        1
  2711. X#endif
  2712. X
  2713. X/* BSD UNIX? */
  2714. X#ifdef bsd
  2715. X# define BSD        1
  2716. X#endif
  2717. X
  2718. X/* Microsoft C: sorry, Watcom does the same thing */
  2719. X#ifdef    M_I86
  2720. X# ifndef M_SYSV
  2721. X#  define MSDOS        1
  2722. X#  define MICROSOFT    1
  2723. X#  define COMPILED_BY    "Microsoft C 5.10"
  2724. X# endif
  2725. X#endif
  2726. X
  2727. X/* Borlands Turbo C */
  2728. X#ifdef    __TURBOC__
  2729. X# define MSDOS        1
  2730. X# define TURBOC        1
  2731. X# define COMPILED_BY    "Turbo C 2.00"
  2732. X#endif
  2733. X
  2734. X/* Tos Mark-Williams */
  2735. X#ifdef    M68000
  2736. X# define TOS 1
  2737. X# define COMPILED_BY    "Mark Williams C"
  2738. X#endif
  2739. X
  2740. X/* OS9/68000 */
  2741. X#ifdef    OSK
  2742. X# define COMPILED_BY    "Microware C V2.3 Edition 40"
  2743. X#endif
  2744. X
  2745. X/*************************** end of autoconf section ************************/
  2746. X
  2747. X/* All undefined symbols are defined to zero here, to allow for older    */
  2748. X/* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */
  2749. X
  2750. X/*************************** operating systems *****************************/
  2751. X#ifndef    BSD
  2752. X# define BSD    0        /* UNIX - Berkeley 4.x */
  2753. X#endif
  2754. X
  2755. X#ifndef    UNIXV
  2756. X# define UNIXV    0        /* UNIX - AT&T SYSV */
  2757. X#endif
  2758. X
  2759. X#ifndef    UNIX7
  2760. X# define UNIX7    0        /* UNIX - version 7 */
  2761. X#endif
  2762. X
  2763. X#ifndef    MSDOS
  2764. X# define MSDOS    0        /* PC        */
  2765. X#endif
  2766. X
  2767. X#ifndef    TOS
  2768. X# define TOS    0        /* Atari ST    */
  2769. X#endif
  2770. X
  2771. X#ifndef    AMIGA
  2772. X# define AMIGA    0        /* Commodore Amiga */
  2773. X#endif
  2774. X
  2775. X#ifndef OSK
  2776. X# define OSK    0        /* OS-9 / 68k */
  2777. X#endif
  2778. X
  2779. X#ifndef COHERENT
  2780. X# define COHERENT 0        /* Coherent */
  2781. X#endif
  2782. X
  2783. X                /* Minix has no predefines */
  2784. X#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT
  2785. X# define MINIX    1
  2786. X#else
  2787. X# define MINIX    0
  2788. X#endif
  2789. X
  2790. X                /* generic combination of Unices */
  2791. X#if UNIXV || UNIX7 || BSD || MINIX || COHERENT
  2792. X# define ANY_UNIX 1
  2793. X#else
  2794. X# define ANY_UNIX 0
  2795. X#endif
  2796. X
  2797. X/*************************** compilers **************************************/
  2798. X#ifndef    MICROSOFT
  2799. X# define MICROSOFT    0
  2800. X#endif
  2801. X
  2802. X#ifndef    TURBOC
  2803. X# define TURBOC        0
  2804. X#endif
  2805. X
  2806. X/******************************* Credit ************************************/
  2807. X
  2808. X#if MSDOS
  2809. X# define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel"
  2810. X#endif
  2811. X
  2812. X#if TOS
  2813. X# define CREDIT "Ported to Atari/TOS by Guntram Blohm & Martin Patzel"
  2814. X#endif
  2815. X
  2816. X#if OSK
  2817. X# define CREDIT    "Ported to Microware OS9/68k by Peter Reinig"
  2818. X#endif
  2819. X
  2820. X#if COHERENT
  2821. X# define CREDIT    "Ported to Coherent by Esa Ahola"
  2822. X#endif
  2823. X
  2824. X/*************************** functions depending on OS *********************/
  2825. X
  2826. X/* Only MSDOS, TOS, and OS9 need a special function for reading from the
  2827. X * keyboard.  All others just read from file descriptor 0.
  2828. X */
  2829. X#if !MSDOS && !TOS && !OSK
  2830. X# define ttyread(buf, len)    read(0, buf, (unsigned)len)    /* raw read */
  2831. X#endif
  2832. X#if !TOS
  2833. X# define ttywrite(buf, len)    write(1, buf, (unsigned)(len))    /* raw write */
  2834. X#endif
  2835. X
  2836. X/* The strchr() function is an official standard now, so everybody has it
  2837. X * except Unix version 7 (which is old) and BSD Unix (which is academic).
  2838. X * Those guys use something called index() to do the same thing.
  2839. X */
  2840. X#if BSD || UNIX7 || OSK
  2841. X# define strchr    index
  2842. X#endif
  2843. Xextern char *strchr();
  2844. X
  2845. X/* BSD uses bcopy() instead of memcpy() */
  2846. X#if BSD
  2847. X#define memcpy(dest, src, siz)    bcopy(src, dest, siz)
  2848. X#endif
  2849. X
  2850. X/* text versa binary mode for read/write */
  2851. X#if !TOS
  2852. X#define    tread(fd,buf,n)        read(fd,buf,(unsigned)(n))
  2853. X#define twrite(fd,buf,n)    write(fd,buf,(unsigned)(n))
  2854. X#endif
  2855. X
  2856. X/**************************** Compiler quirks *********************************/
  2857. X
  2858. X/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */
  2859. X#if UNIX7 || TOS
  2860. X# define void int
  2861. X#endif
  2862. X
  2863. X/* as far as I know, all compilers except version 7 support unsigned char */
  2864. X/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */
  2865. X#if UNIX7 || MINIX
  2866. X# define UCHAR(c)    ((c) & 0xff)
  2867. X# define uchar        char
  2868. X#else
  2869. X# define UCHAR(c)    ((unsigned char)(c))
  2870. X# define uchar        unsigned char
  2871. X#endif
  2872. X
  2873. X/* Some compilers prefer to have malloc declared as returning a (void *) */
  2874. X#if BSD
  2875. Xextern void *malloc();
  2876. X#else
  2877. Xextern char *malloc();
  2878. X#endif
  2879. X
  2880. X/* Most compilers could benefit from using the "register" storage class */
  2881. X#if 1
  2882. X# define REG    register
  2883. X#endif
  2884. X
  2885. X/******************* Names of files and environment vars **********************/
  2886. X
  2887. X#if ANY_UNIX
  2888. X# ifndef TMPDIR
  2889. X#  if MINIX
  2890. X#   define TMPDIR    "/usr/tmp"    /* Keep elvis' temp files off RAM disk! */
  2891. X#  else
  2892. X#   define TMPDIR    "/tmp"        /* directory where temp files live */
  2893. X#  endif
  2894. X# endif
  2895. X# define TMPNAME    "%s/elv%x%04x%03x" /* temp file */
  2896. X# define CUTNAME    "%s/elv_%04x%03x" /* cut buffer's temp file */
  2897. X# ifndef EXRC
  2898. X#  define EXRC        ".exrc"        /* init file in current directory */
  2899. X# endif
  2900. X# define SCRATCHOUT    "%s/soXXXXXX"    /* temp file used as input to filter */
  2901. X# ifndef EXINIT
  2902. X#  define EXINIT    "EXINIT"
  2903. X# endif
  2904. X# ifndef SHELL
  2905. X#  define SHELL        "/bin/sh"    /* default shell */
  2906. X# endif
  2907. X# if COHERENT
  2908. X#  ifndef REDIRECT
  2909. X#   define REDIRECT    ">"        /* Coherent CC writes errors to stdout */
  2910. X#  endif
  2911. X# endif
  2912. X#endif
  2913. X
  2914. X#if MSDOS || TOS
  2915. X/* do not change TMPNAME, CUTNAME and SCRATCH*: they MUST begin with '%s\\'! */
  2916. X# ifndef TMPDIR
  2917. X#  define TMPDIR    "C:\\tmp"    /* directory where temp files live */
  2918. X# endif
  2919. X# define TMPNAME    "%s\\elv%x%04x.%03x" /* temp file */
  2920. X# define CUTNAME    "%s\\elv_%04x.%03x" /* cut buffer's temp file */
  2921. X# if MSDOS
  2922. X#  if MICROSOFT
  2923. X#   define CC_COMMAND    "cl -c"        /* C compiler */
  2924. X#  else /* TURBO_C */
  2925. X#   define CC_COMMAND    "tc"        /* C compiler */
  2926. X#  endif
  2927. X# endif
  2928. X# define SCRATCHIN    "%s\\siXXXXXX"    /* DOS ONLY - output of filter program */
  2929. X# define SCRATCHOUT    "%s\\soXXXXXX"    /* temp file used as input to filter */
  2930. X# define SLASH        '\\'
  2931. X# ifndef SHELL
  2932. X#  if TOS
  2933. X#   define SHELL    "shell.ttp"    /* default shell */
  2934. X#  else
  2935. X#   define SHELL    "command.com"    /* default shell */
  2936. X#  endif
  2937. X# endif
  2938. X# define NEEDSYNC    TRUE        /* assume ":se sync" by default */
  2939. X# define REDIRECT    ">"        /* shell's redirection of stderr */
  2940. X# ifndef MAXMAPS
  2941. X#  define MAXMAPS    40
  2942. X# endif
  2943. X# ifndef EXINIT
  2944. X#  define EXINIT    "EXINIT"
  2945. X# endif
  2946. X#endif
  2947. X
  2948. X#if OSK
  2949. X# ifndef TMPDIR
  2950. X#  define TMPDIR    "/dd/tmp"       /* directory where temp files live */
  2951. X# endif
  2952. X# define TMPNAME    "%s/elv%x%04x%03x"  /* temp file */
  2953. X# define CUTNAME    "%s/elv_%04x%03x"  /* cut buffer's temp file */
  2954. X# ifndef CC_COMMAND
  2955. X#  define CC_COMMAND    "cc -r"           /* name of the compiler */
  2956. X# endif
  2957. X# ifndef EXRC
  2958. X#  define EXRC        ".exrc"           /* init file in current directory */
  2959. X# endif
  2960. X# define SCRATCHOUT    "%s/soXXXXXX"       /* temp file used as input to filter */
  2961. X# ifndef SHELL
  2962. X#  define SHELL        "shell"           /* default shell */
  2963. X# endif
  2964. X# define FILEPERMS    (S_IREAD|S_IWRITE) /* file permissions used for creat() */
  2965. X# define REDIRECT    ">>-"           /* shell's redirection of stderr */
  2966. X#endif
  2967. X
  2968. X#ifndef    TAGS
  2969. X# define TAGS        "tags"        /* tags file */
  2970. X#endif
  2971. X
  2972. X#ifndef TMPNAME
  2973. X# define TMPNAME    "%s/elv%x%04x.%03x"    /* temp file */
  2974. X#endif
  2975. X
  2976. X#ifndef CUTNAME
  2977. X# define CUTNAME    "%s/elv_%04x.%03x"    /* cut buffer's temp file */
  2978. X#endif
  2979. X
  2980. X#ifndef    EXRC
  2981. X# define EXRC        "elvis.rc"
  2982. X#endif
  2983. X
  2984. X#ifndef HMEXRC
  2985. X# if !MSDOS && !TOS
  2986. X#  define HMEXRC    EXRC
  2987. X# endif
  2988. X#endif
  2989. X
  2990. X#ifndef    KEYWORDPRG
  2991. X# define KEYWORDPRG    "ref"
  2992. X#endif
  2993. X
  2994. X#ifndef    SCRATCHOUT
  2995. X# define SCRATCHIN    "%s/SIXXXXXX"
  2996. X# define SCRATCHOUT    "%s/SOXXXXXX"
  2997. X#endif
  2998. X
  2999. X#ifndef ERRLIST
  3000. X# define ERRLIST    "errlist"
  3001. X#endif
  3002. X
  3003. X#ifndef    SLASH
  3004. X# define SLASH        '/'
  3005. X#endif
  3006. X
  3007. X#ifndef SHELL
  3008. X# define SHELL        "shell"
  3009. X#endif
  3010. X
  3011. X#ifndef REG
  3012. X# define REG
  3013. X#endif
  3014. X
  3015. X#ifndef NEEDSYNC
  3016. X# define NEEDSYNC    FALSE
  3017. X#endif
  3018. X
  3019. X#ifndef FILEPERMS
  3020. X# define FILEPERMS    0666
  3021. X#endif
  3022. X
  3023. X#ifndef CC_COMMAND
  3024. X# define CC_COMMAND    "cc -c"
  3025. X#endif
  3026. X
  3027. X#ifndef MAKE_COMMAND
  3028. X# define MAKE_COMMAND    "make"
  3029. X#endif
  3030. X
  3031. X#ifndef REDIRECT
  3032. X# define REDIRECT    "2>"
  3033. X#endif
  3034. X
  3035. X#ifndef MAXMAPS
  3036. X# define MAXMAPS    20        /* number of :map keys */
  3037. X#endif
  3038. X#ifndef MAXDIGS
  3039. X# define MAXDIGS    30        /* number of :digraph combos */
  3040. X#endif
  3041. X#ifndef MAXABBR
  3042. X# define MAXABBR    20        /* number of :abbr entries */
  3043. X#endif
  3044. eof
  3045. if test `wc -c <config.h` -ne 7951
  3046. then
  3047. echo config.h damaged!
  3048. fi
  3049. fi
  3050.  
  3051. if test -f curses.c -a "$1" != -f
  3052. then
  3053. echo Will not overwrite curses.c
  3054. else
  3055. echo Extracting curses.c
  3056. sed 's/^X//' >curses.c <<\eof
  3057. X/* curses.c */
  3058. X
  3059. X/* Author:
  3060. X *    Steve Kirkendall
  3061. X *    14407 SW Teal Blvd. #C
  3062. X *    Beaverton, OR 97005
  3063. X *    kirkenda@cs.pdx.edu
  3064. X */
  3065. X
  3066. X
  3067. X/* This file contains the functions & variables needed for a tiny subset of
  3068. X * curses.  The principle advantage of this version of curses is its
  3069. X * extreme speed.  Disadvantages are potentially larger code, few supported
  3070. X * functions, limited compatibility with full curses, and only stdscr.
  3071. X */
  3072. X
  3073. X#include "config.h"
  3074. X#include "vi.h"
  3075. X
  3076. X#if ANY_UNIX
  3077. X# if UNIXV
  3078. X#  include    <termio.h>
  3079. X#  undef    TIOCWINSZ    /* we can't handle it correctly yet */
  3080. X# else
  3081. X#  include    <sgtty.h>
  3082. X# endif
  3083. X#endif
  3084. X
  3085. X#if TOS
  3086. X# include    <osbind.h>
  3087. X#endif
  3088. X
  3089. X#if OSK
  3090. X# include    <sgstat.h>
  3091. X#endif
  3092. X
  3093. X#include <signal.h>
  3094. X
  3095. Xextern char    *getenv();
  3096. Xstatic void     starttcap();
  3097. X
  3098. X/* variables, publicly available & used in the macros */
  3099. Xshort    ospeed;        /* speed of the tty, eg B2400 */
  3100. X#if OSK
  3101. Xchar    PC_;    /* Pad char */
  3102. Xchar    *BC;    /* backspace character string */
  3103. X#else
  3104. Xchar    PC;        /* Pad char */
  3105. X#endif
  3106. XWINDOW    *stdscr;    /* pointer into kbuf[] */
  3107. XWINDOW    kbuf[KBSIZ];    /* a very large output buffer */
  3108. Xint    LINES;        /* :li#: number of rows */
  3109. Xint    COLS;        /* :co#: number of columns */
  3110. Xint    AM;        /* :am:  boolean: auto margins? */
  3111. Xint    PT;        /* :pt:  boolean: physical tabs? */
  3112. Xchar    *VB;        /* :vb=: visible bell */
  3113. Xchar    *UP;        /* :up=: move cursor up */
  3114. Xchar    *SO;        /* :so=: standout start */
  3115. Xchar    *SE;        /* :se=: standout end */
  3116. Xchar    *US = "";    /* :us=: underline start */
  3117. Xchar    *UE = "";    /* :ue=: underline end */
  3118. Xchar    *MD = "";    /* :md=: bold start */
  3119. Xchar    *ME = "";    /* :me=: bold end */
  3120. Xchar    *AS;        /* :as=: alternate (italic) start */
  3121. Xchar    *AE;        /* :ae=: alternate (italic) end */
  3122. Xchar    *CM;        /* :cm=: cursor movement */
  3123. Xchar    *CE;        /* :ce=: clear to end of line */
  3124. Xchar    *CD;        /* :cd=: clear to end of screen */
  3125. Xchar    *AL;        /* :al=: add a line */
  3126. Xchar    *DL;        /* :dl=: delete a line */
  3127. X#if OSK
  3128. Xchar    *SR_;        /* :sr=: scroll reverse */
  3129. X#else
  3130. Xchar    *SR;        /* :sr=: scroll reverse */
  3131. X#endif
  3132. Xchar    *KS;        /* :ks=: init string for cursor */
  3133. Xchar    *KE;        /* :ke=: restore string for cursor */
  3134. Xchar    *KU;        /* :ku=: key sequence sent by up arrow */
  3135. Xchar    *KD;        /* :kd=: key sequence sent by down arrow */
  3136. Xchar    *KL;        /* :kl=: key sequence sent by left arrow */
  3137. Xchar    *KR;        /* :kr=: key sequence sent by right arrow */
  3138. Xchar    *HM;        /* :HM=: key sequence sent by the <Home> key */
  3139. Xchar    *EN;        /* :EN=: key sequence sent by the <End> key */
  3140. Xchar    *PU;        /* :PU=: key sequence sent by the <PgUp> key */
  3141. Xchar    *PD;        /* :PD=: key sequence sent by the <PgDn> key */
  3142. Xchar    *IM;        /* :im=: insert mode start */
  3143. Xchar    *IC = "";    /* :ic=: insert the following character */
  3144. Xchar    *EI;        /* :ei=: insert mode end */
  3145. Xchar    *DC;        /* :dc=: delete a character */
  3146. Xchar    *TI;        /* :ti=: terminal init */    /* GB */
  3147. Xchar    *TE;        /* :te=: terminal exit */    /* GB */
  3148. X#ifndef NO_CURSORSHAPE
  3149. Xchar    *CQ = (char *)0;/* :cQ=: normal cursor */
  3150. Xchar    *CX = (char *)1;/* :cX=: cursor used for EX command/entry */
  3151. Xchar    *CV = (char *)2;/* :cV=: cursor used for VI command mode */
  3152. Xchar    *CI = (char *)3;/* :cI=: cursor used for VI input mode */
  3153. Xchar    *CR = (char *)4;/* :cR=: cursor used for VI replace mode */
  3154. X#endif
  3155. Xchar    *aend = "";    /* end an attribute -- either UE or ME */
  3156. Xchar    ERASEKEY;    /* backspace key taken from ioctl structure */
  3157. X
  3158. X#if ANY_UNIX
  3159. X# if UNIXV
  3160. Xstatic struct termio    oldtermio;    /* original tty mode */
  3161. Xstatic struct termio    newtermio;    /* cbreak/noecho tty mode */
  3162. X# else
  3163. Xstatic struct sgttyb    oldsgttyb;    /* original tty mode */
  3164. Xstatic struct sgttyb    newsgttyb;    /* cbreak/nl/noecho tty mode */
  3165. Xstatic int        oldint;        /* ^C or DEL, the "intr" character */
  3166. X#  ifdef TIOCSLTC
  3167. Xstatic int        oldswitch;    /* ^Z, the "suspend" character */
  3168. Xstatic int        oldquote;    /* ^V, the "quote next char" char */
  3169. X#  endif
  3170. X# endif
  3171. X#endif
  3172. X
  3173. X#if OSK
  3174. Xstatic struct sgbuf    oldsgttyb;    /* orginal tty mode */
  3175. Xstatic struct sgbuf    newsgttyb;    /* noecho tty mode */
  3176. X#endif
  3177. X
  3178. Xstatic char    *capbuf;    /* capability string buffer */
  3179. X
  3180. X
  3181. Xvoid initscr()
  3182. X{
  3183. X    /* make sure TERM variable is set */
  3184. X#if MSDOS
  3185. X    char *val;
  3186. X    if (! (val = getenv("TERM"))
  3187. X    || !strcmp(val, "pcbios"))
  3188. X#else
  3189. X    if (!getenv("TERM"))
  3190. X#endif
  3191. X    {
  3192. X#if ANY_UNIX
  3193. X        write(2, "Environment variable TERM must be set\n", (unsigned)38);
  3194. X        exit(1);
  3195. X#endif
  3196. X#if OSK
  3197. X        writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
  3198. X        exit(1);
  3199. X#endif
  3200. X#if MSDOS || TOS
  3201. X        getsize(0);
  3202. X#endif
  3203. X    }
  3204. X    else
  3205. X    {
  3206. X#if MSDOS
  3207. X        *o_pcbios=0;
  3208. X#endif
  3209. X        /* start termcap stuff */
  3210. X        starttcap();
  3211. X    }
  3212. X
  3213. X    /* create stdscr and curscr */
  3214. X    stdscr = kbuf;
  3215. X
  3216. X    /* change the terminal mode to cbreak/noecho */
  3217. X#if ANY_UNIX
  3218. X# if UNIXV
  3219. X    ioctl(2, TCGETA, &oldtermio);
  3220. X# else
  3221. X    ioctl(2, TIOCGETP, &oldsgttyb);
  3222. X# endif
  3223. X#endif
  3224. X
  3225. X#if OSK
  3226. X    _gs_opt(0, &oldsgttyb);
  3227. X#endif
  3228. X    resume_curses(TRUE);
  3229. X}
  3230. X
  3231. X
  3232. Xvoid endwin()
  3233. X{
  3234. X    /* change the terminal mode back the way it was */
  3235. X    suspend_curses();
  3236. X}
  3237. X
  3238. X
  3239. Xstatic int curses_active = FALSE;
  3240. X
  3241. Xvoid suspend_curses()
  3242. X{
  3243. X#if ANY_UNIX && !UNIXV
  3244. X    struct tchars    tbuf;
  3245. X# ifdef TIOCSLTC
  3246. X    struct ltchars    ltbuf;
  3247. X# endif
  3248. X#endif
  3249. X#ifndef NO_CURSORSHAPE
  3250. X    if (has_CQ)
  3251. X    {
  3252. X        do_CQ();
  3253. X    }
  3254. X#endif
  3255. X    if (has_TE)                    /* GB */
  3256. X    {
  3257. X        do_TE();
  3258. X    }
  3259. X    if (has_KE)
  3260. X    {
  3261. X        do_KE();
  3262. X    }
  3263. X    refresh();
  3264. X
  3265. X    /* change the terminal mode back the way it was */
  3266. X#if ANY_UNIX
  3267. X# if UNIXV
  3268. X    ioctl(2, TCSETAW, &oldtermio);
  3269. X# else
  3270. X    ioctl(2, TIOCSETP, &oldsgttyb);
  3271. X
  3272. X    ioctl(2, TIOCGETC, &tbuf);
  3273. X    tbuf.t_intrc = oldint;
  3274. X    ioctl(2, TIOCSETC, &tbuf);
  3275. X
  3276. X#  ifdef TIOCSLTC
  3277. X    ioctl(2, TIOCGLTC, <buf);
  3278. X    ltbuf.t_suspc = oldswitch;
  3279. X    ltbuf.t_lnextc = oldquote;
  3280. X    ioctl(2, TIOCSLTC, <buf);
  3281. X#  endif
  3282. X# endif
  3283. X#endif
  3284. X#if OSK
  3285. X    _ss_opt(0, &oldsgttyb);
  3286. X#endif
  3287. X    curses_active = FALSE;
  3288. X}
  3289. X
  3290. Xvoid resume_curses(quietly)
  3291. X    int    quietly;
  3292. X{
  3293. X    if (!curses_active)
  3294. X    {
  3295. X        /* change the terminal mode to cbreak/noecho */
  3296. X#if ANY_UNIX
  3297. X# if UNIXV
  3298. X        ospeed = (oldtermio.c_cflag & CBAUD);
  3299. X        ERASEKEY = oldtermio.c_cc[VERASE];
  3300. X        newtermio = oldtermio;
  3301. X        newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
  3302. X        newtermio.c_oflag &= ~OPOST;
  3303. X        newtermio.c_lflag &= ISIG;
  3304. X        newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
  3305. X        newtermio.c_cc[VMIN] = 1;
  3306. X        newtermio.c_cc[VTIME] = 0;
  3307. X#  ifdef VSWTCH
  3308. X        newtermio.c_cc[VSWTCH] = 0;
  3309. X#  endif
  3310. X        ioctl(2, TCSETAW, &newtermio);
  3311. X# else /* BSD or V7 or Coherent or Minix */
  3312. X        struct tchars    tbuf;
  3313. X#  ifdef TIOCSLTC
  3314. X        struct ltchars    ltbuf;
  3315. X#  endif
  3316. X
  3317. X        ospeed = oldsgttyb.sg_ospeed;
  3318. X        ERASEKEY = oldsgttyb.sg_erase;
  3319. X        newsgttyb = oldsgttyb;
  3320. X        newsgttyb.sg_flags |= CBREAK;
  3321. X        newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
  3322. X        ioctl(2, TIOCSETP, &newsgttyb);
  3323. X
  3324. X        ioctl(2, TIOCGETC, &tbuf);
  3325. X        oldint = tbuf.t_intrc;
  3326. X        tbuf.t_intrc = ctrl('C');    /* always use ^C for interrupts */
  3327. X        ioctl(2, TIOCSETC, &tbuf);
  3328. X
  3329. X#  ifdef TIOCSLTC
  3330. X        ioctl(2, TIOCGLTC, <buf);
  3331. X        oldswitch = ltbuf.t_suspc;
  3332. X        ltbuf.t_suspc = 0;        /* disable ^Z for elvis */
  3333. X        oldquote = ltbuf.t_lnextc;
  3334. X        ltbuf.t_lnextc = 0;        /* disable ^V for elvis */
  3335. X        ioctl(2, TIOCSLTC, <buf);
  3336. X#  endif
  3337. X
  3338. X# endif
  3339. X#endif
  3340. X#if OSK
  3341. X        newsgttyb = oldsgttyb;
  3342. X        newsgttyb.sg_echo = 0;
  3343. X        newsgttyb.sg_eofch = 0;
  3344. X        newsgttyb.sg_kbach = 0;
  3345. X        newsgttyb.sg_kbich = ctrl('C');
  3346. X        _ss_opt(0, &newsgttyb);
  3347. X        ospeed = oldsgttyb.sg_baud;
  3348. X        ERASEKEY = oldsgttyb.sg_bspch;
  3349. X#endif
  3350. X
  3351. X        if (has_TI)                    /* GB */
  3352. X        {
  3353. X            do_TI();
  3354. X        }
  3355. X        if (has_KS)
  3356. X        {
  3357. X            do_KS();
  3358. X        }
  3359. X
  3360. X        curses_active = TRUE;
  3361. X    }
  3362. X
  3363. X    /* If we're supposed to quit quietly, then we're done */
  3364. X    if (quietly)
  3365. X    {
  3366. X        return;
  3367. X    }
  3368. X
  3369. X    signal(SIGINT, SIG_IGN);
  3370. X
  3371. X    move(LINES - 1, 0);
  3372. X    do_SO();
  3373. X    qaddstr("[Press <RETURN> to continue]");
  3374. X    do_SE();
  3375. X    refresh();
  3376. X    ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */
  3377. X    if (kbuf[0] == ':')
  3378. X    {
  3379. X        mode = MODE_COLON;
  3380. X        addch('\n');
  3381. X        refresh();
  3382. X    }
  3383. X    else
  3384. X    {
  3385. X        mode = MODE_VI;
  3386. X        redraw(MARK_UNSET, FALSE);
  3387. X    }    
  3388. X    exwrote = FALSE;
  3389. X
  3390. X#if TURBOC
  3391. X    signal(SIGINT, (void(*)()) trapint);
  3392. X#else
  3393. X    signal(SIGINT, trapint);
  3394. X#endif
  3395. X}
  3396. X
  3397. Xstatic void lacking(s)
  3398. X    char    *s;
  3399. X{
  3400. X    write(2, "This termcap entry lacks the :", (unsigned)30);
  3401. X    write(2, s, (unsigned)2);
  3402. X    write(2, "=: capability\n", (unsigned)14);
  3403. X#if OSK
  3404. X    write(2, "\l", 1);
  3405. X#endif
  3406. X    exit(1);
  3407. X}
  3408. X
  3409. Xstatic void starttcap()
  3410. X{
  3411. X    char    *str;
  3412. X    static char    cbmem[800];
  3413. X#define MUSTHAVE(T,s)    if (!(T = tgetstr(s, &capbuf))) lacking(s)
  3414. X#define MAYHAVE(T,s)    if (str = tgetstr(s, &capbuf)) T = str
  3415. X#define PAIR(T,U,sT,sU)    T=tgetstr(sT,&capbuf);U=tgetstr(sU,&capbuf);if (!T||!U)T=U=""
  3416. X
  3417. X    /* allocate memory for capbuf */
  3418. X    capbuf = cbmem;
  3419. X
  3420. X    /* get the termcap entry */
  3421. X    switch (tgetent(kbuf, getenv("TERM")))
  3422. X    {
  3423. X      case -1:
  3424. X        write(2, "Can't read /etc/termcap\n", (unsigned)24);
  3425. X#if OSK
  3426. X        write(2, "\l", 1);
  3427. X#endif
  3428. X        exit(2);
  3429. X
  3430. X      case 0:
  3431. X        write(2, "Unrecognized TERM type\n", (unsigned)23);
  3432. X#if OSK
  3433. X        write(2, "\l", 1);
  3434. X#endif
  3435. X        exit(3);
  3436. X    }
  3437. X
  3438. X    /* get strings */
  3439. X    MUSTHAVE(UP, "up");
  3440. X    MAYHAVE(VB, "vb");
  3441. X    MUSTHAVE(CM, "cm");
  3442. X    PAIR(SO, SE, "so", "se");
  3443. X    PAIR(TI, TE, "ti", "te");
  3444. X    if (tgetnum("ug") <= 0)
  3445. X    {
  3446. X        PAIR(US, UE, "us", "ue");
  3447. X        PAIR(MD, ME, "md", "me");
  3448. X
  3449. X        /* get italics, or have it default to underline */
  3450. X        PAIR(AS, AE, "as", "ae");
  3451. X        if (!*AS)
  3452. X        {
  3453. X            AS = US;
  3454. X            AE = UE;
  3455. X        }
  3456. X    }
  3457. X    MAYHAVE(AL, "al");
  3458. X    MAYHAVE(DL, "dl");
  3459. X    MUSTHAVE(CE, "ce");
  3460. X    MAYHAVE(CD, "cd");
  3461. X#if OSK
  3462. X    MAYHAVE(SR_, "sr");
  3463. X#else    
  3464. X    MAYHAVE(SR, "sr");
  3465. X#endif
  3466. X    PAIR(IM, EI, "im", "ei");
  3467. X    MAYHAVE(IC, "ic");
  3468. X    MAYHAVE(DC, "dc");
  3469. X
  3470. X    /* other termcap stuff */
  3471. X    AM = tgetflag("am");
  3472. X    PT = tgetflag("pt");
  3473. X    getsize(0);
  3474. X
  3475. X    /* Key sequences */
  3476. X    PAIR(KS, KE, "ks", "ke");
  3477. X    MAYHAVE(KU, "ku");        /* up */
  3478. X    MAYHAVE(KD, "kd");        /* down */
  3479. X    MAYHAVE(KL, "kl");        /* left */
  3480. X    MAYHAVE(KR, "kr");        /* right */
  3481. X    MAYHAVE(PU, "kP");        /* PgUp */
  3482. X    MAYHAVE(PD, "kN");        /* PgDn */
  3483. X    MAYHAVE(HM, "kh");        /* Home */
  3484. X    MAYHAVE(EN, "kH");        /* End */
  3485. X#ifndef CRUNCH
  3486. X    if (!PU) MAYHAVE(PU, "K2");    /* "3x3 pad" names for PgUp, etc. */
  3487. X    if (!PD) MAYHAVE(PD, "K5");
  3488. X    if (!HM) MAYHAVE(HM, "K1");
  3489. X    if (!EN) MAYHAVE(EN, "K4");
  3490. X
  3491. X    MAYHAVE(PU, "PU");        /* old XENIX names for PgUp, etc. */
  3492. X    MAYHAVE(PD, "PD");        /* (overrides others, if used.) */
  3493. X    MAYHAVE(HM, "HM");
  3494. X    MAYHAVE(EN, "EN");
  3495. X#endif
  3496. X
  3497. X#ifndef NO_CURSORSHAPE
  3498. X    /* cursor shapes */
  3499. X    CQ = tgetstr("cQ", &capbuf);
  3500. X    if (has_CQ)
  3501. X    {
  3502. X        CX = tgetstr("cX", &capbuf);
  3503. X        if (!CX) CX = CQ;
  3504. X        CV = tgetstr("cV", &capbuf);
  3505. X        if (!CV) CV = CQ;
  3506. X        CI = tgetstr("cI", &capbuf);
  3507. X        if (!CI) CI = CQ;
  3508. X        CR = tgetstr("cR", &capbuf);
  3509. X        if (!CR) CR = CQ;
  3510. X    }
  3511. X# ifndef CRUNCH
  3512. X    else
  3513. X    {
  3514. X        PAIR(CQ, CV, "ve", "vs");
  3515. X        CX = CI = CR = CQ;
  3516. X    }
  3517. X# endif /* !CRUNCH */
  3518. X#endif /* !NO_CURSORSHAPE */
  3519. X
  3520. X#undef MUSTHAVE
  3521. X#undef MAYHAVE
  3522. X#undef PAIR
  3523. X}
  3524. X
  3525. X
  3526. X/* This function gets the window size.  It uses the TIOCGWINSZ ioctl call if
  3527. X * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
  3528. X * This function is called once during initialization, and thereafter it is
  3529. X * called whenever the SIGWINCH signal is sent to this process.
  3530. X */
  3531. Xint getsize(signo)
  3532. X    int    signo;
  3533. X{
  3534. X    int    lines;
  3535. X    int    cols;
  3536. X#ifdef TIOCGWINSZ
  3537. X    struct winsize size;
  3538. X#endif
  3539. X
  3540. X#ifdef SIGWINCH
  3541. X    /* reset the signal vector */
  3542. X    signal(SIGWINCH, getsize);
  3543. X#endif
  3544. X
  3545. X    /* get the window size, one way or another. */
  3546. X    lines = cols = 0;
  3547. X#ifdef TIOCGWINSZ
  3548. X    if (ioctl(2, TIOCGWINSZ, &size) >= 0)
  3549. X    {
  3550. X        lines = size.ws_row;
  3551. X        cols = size.ws_col;
  3552. X    }
  3553. X#endif
  3554. X    if ((lines == 0 || cols == 0) && signo == 0)
  3555. X    {
  3556. X        LINES = CHECKBIOS(v_rows(), tgetnum("li"));
  3557. X        COLS = CHECKBIOS(v_cols(), tgetnum("co"));
  3558. X    }
  3559. X    if (lines >= 2 && cols >= 30)
  3560. X    {
  3561. X        LINES = lines;
  3562. X        COLS = cols;
  3563. X    }
  3564. X
  3565. X    /* Make sure we got values that we can live with */
  3566. X    if (LINES < 2 || COLS < 30)
  3567. X    {
  3568. X        write(2, "Screen too small\n", (unsigned)17);
  3569. X#if OSK
  3570. X        write(2, "\l", 1);
  3571. X#endif
  3572. X        endwin();
  3573. X        exit(2);
  3574. X    }
  3575. X
  3576. X    /* !!! copy the new values into Elvis' options */
  3577. X    {
  3578. X        extern char    o_columns[], o_lines[];
  3579. X
  3580. X        *o_columns = COLS;
  3581. X        *o_lines = LINES;
  3582. X    }
  3583. X
  3584. X    return 0;
  3585. X}
  3586. X
  3587. X
  3588. X/* This is a function version of addch() -- it is used by tputs() */
  3589. Xint faddch(ch)
  3590. X    int    ch;
  3591. X{
  3592. X    addch(ch);
  3593. X
  3594. X    return 0;
  3595. X}
  3596. X
  3597. X/* These functions are equivelent to the macros of the same names... */
  3598. X
  3599. Xvoid qaddstr(str)
  3600. X    char    *str;
  3601. X{
  3602. X    REG char *s_, *d_;
  3603. X
  3604. X#if MSDOS
  3605. X    if (o_pcbios[0])
  3606. X    {
  3607. X        while (*str)
  3608. X            qaddch(*str++);
  3609. X        return;
  3610. X    }
  3611. X#endif
  3612. X    for (s_=(str), d_=stdscr; *d_++ = *s_++; )
  3613. X    {
  3614. X    }
  3615. X    stdscr = d_ - 1;
  3616. X}
  3617. X
  3618. Xvoid attrset(a)
  3619. X    int    a;
  3620. X{
  3621. X    do_aend();
  3622. X    if (a == A_BOLD)
  3623. X    {
  3624. X        do_MD();
  3625. X        aend = ME;
  3626. X    }
  3627. X    else if (a == A_UNDERLINE)
  3628. X    {
  3629. X        do_US();
  3630. X        aend = UE;
  3631. X    }
  3632. X    else if (a == A_ALTCHARSET)
  3633. X    {
  3634. X        do_AS();
  3635. X        aend = AE;
  3636. X    }
  3637. X    else
  3638. X    {
  3639. X        aend = "";
  3640. X    }
  3641. X}
  3642. X
  3643. X
  3644. Xvoid insch(ch)
  3645. X    int    ch;
  3646. X{
  3647. X    if (has_IM)
  3648. X        do_IM();
  3649. X    do_IC();
  3650. X    qaddch(ch);
  3651. X    if (has_EI)
  3652. X        do_EI();
  3653. X}
  3654. X
  3655. X#if MSDOS
  3656. X
  3657. Xstatic int alarmtime;
  3658. X
  3659. X/* raw read - #defined to read (0, ...) on non-MSDOS.
  3660. X * With MSDOS, am maximum of 1 byte is read.
  3661. X * If more bytes should be read, just change the loop.
  3662. X * The following code uses the IBM-PC-System-Timer, so probably wont't work
  3663. X * on non-compatibles.
  3664. X */
  3665. X/*ARGSUSED*/
  3666. Xttyread(buf, len)
  3667. X    char *buf;
  3668. X    int len;
  3669. X{
  3670. X    volatile char far *biostimer;
  3671. X    char oldtime;
  3672. X    int nticks = 0;
  3673. X    int pos = 0;
  3674. X
  3675. X    biostimer = (char far *)0x0040006cl;
  3676. X    oldtime = *biostimer;
  3677. X
  3678. X    while (!pos && (!alarmtime || nticks<alarmtime))
  3679. X    {    if (kbhit())
  3680. X            if ((buf[pos++] = getch()) == 0) /* function key */
  3681. X                buf[pos-1] = '#';
  3682. X        if (oldtime != *biostimer)
  3683. X        {    nticks++;
  3684. X            oldtime = *biostimer;
  3685. X        }
  3686. X    }
  3687. X    return pos;
  3688. X}
  3689. X
  3690. Xalarm(time)
  3691. X    int time;
  3692. X{
  3693. X    alarmtime = 2 * time;        /* ticks are 1/18 sec. */
  3694. X}
  3695. X
  3696. Xsleep(seconds)
  3697. X    unsigned seconds;
  3698. X{
  3699. X    volatile char    far *biostimer = (char far *)0x0040006cl;
  3700. X    char        stop;
  3701. X
  3702. X    stop = *biostimer + 18 * seconds;
  3703. X    while (*biostimer != stop)
  3704. X    {
  3705. X    }
  3706. X}
  3707. X#endif
  3708. X
  3709. X#if TOS
  3710. X
  3711. Xstatic int alarmtime;
  3712. Xstatic long timer;
  3713. X
  3714. Xstatic gettime()
  3715. X{
  3716. X    timer = *(long *)(0x4ba);
  3717. X}
  3718. X
  3719. X/*ARGSUSED*/
  3720. Xttyread(buf, len)
  3721. X    char *buf;
  3722. X    int len;
  3723. X{
  3724. X    int    pos=0;
  3725. X    long    l;
  3726. X    long    endtime;
  3727. X
  3728. X    Supexec(gettime);
  3729. X    endtime = timer+alarmtime;
  3730. X
  3731. X    while (!pos && (!alarmtime || timer<endtime))
  3732. X    {
  3733. X        if (Bconstat(2))
  3734. X        {
  3735. X            l = Bconin(2);
  3736. X            if ((buf[pos++]=l) == '\0')
  3737. X            {
  3738. X                buf[pos-1]='#';
  3739. X                buf[pos++]=l>>16;
  3740. X            }
  3741. X        }
  3742. X        Supexec(gettime);
  3743. X    }
  3744. X    return pos;
  3745. X}
  3746. X
  3747. Xalarm(time)
  3748. X    int time;
  3749. X{
  3750. X    alarmtime = 50 * time;        /* ticks are 1/200 sec. */
  3751. X}
  3752. X
  3753. Xttywrite(buf, len)
  3754. X    char *buf;
  3755. X    int len;
  3756. X{
  3757. X    while (len--)
  3758. X        Bconout(2, *buf++);
  3759. X}
  3760. X#endif
  3761. X
  3762. X#if OSK
  3763. Xttyread(buf, len)
  3764. X    char *buf;
  3765. X    int len;
  3766. X{
  3767. X    REG int i;
  3768. X    if ((i = _gs_rdy(0)) > 0)
  3769. X        return read(0, buf, i < len ? i : len);
  3770. X    else
  3771. X        return read(0, buf, 1);
  3772. X}
  3773. X
  3774. Xalarm(time)
  3775. X    int time;
  3776. X{
  3777. X#define TIME(secs) ((secs << 8) | 0x80000000)
  3778. X    static int alrmid;
  3779. X
  3780. X    if (time)    
  3781. X        alrmid = alm_set(SIGQUIT, TIME(time));
  3782. X    else    
  3783. X        alm_delete(alrmid);
  3784. X}
  3785. X#endif /* OSK */
  3786. eof
  3787. if test `wc -c <curses.c` -ne 14196
  3788. then
  3789. echo curses.c damaged!
  3790. fi
  3791. fi
  3792.  
  3793. exit 0
  3794. -------------------------------------------------------------------------------
  3795. Steve Kirkendall     kirkenda@cs.pdx.edu      Grad student at Portland State U.
  3796.