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

  1. From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall)
  2. Newsgroups: alt.sources
  3. Subject: Elvis 1.4, part 7 of 8
  4. Message-ID: <831@pdxgate.UUCP>
  5. Date: 3 Dec 90 21:35:35 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    16871 Dec  2 17:57 regexp.c
  12. # -rw-r--r--  1 kirkenda      579 Dec  2 17:57 regexp.h
  13. # -rw-r--r--  1 kirkenda     3614 Dec  2 17:57 regsub.c
  14. # -rw-r--r--  1 kirkenda     8849 Dec  2 17:57 system.c
  15. # -rw-r--r--  1 kirkenda     3767 Dec  2 17:57 tinytcap.c
  16. # -rw-r--r--  1 kirkenda    16181 Dec  2 17:57 tio.c
  17. # -rw-r--r--  1 kirkenda    12770 Dec  2 17:57 tmp.c
  18. #
  19.  
  20. if test -f regexp.c -a "$1" != -f
  21. then
  22. echo Will not overwrite regexp.c
  23. else
  24. echo Extracting regexp.c
  25. sed 's/^X//' >regexp.c <<\eof
  26. X/* regexp.c */
  27. X
  28. X/* This file contains the code that compiles regular expressions and executes
  29. X * them.  It supports the same syntax and features as vi's regular expression
  30. X * code.  Specifically, the meta characters are:
  31. X *    ^    matches the beginning of a line
  32. X *    $    matches the end of a line
  33. X *    \<    matches the beginning of a word
  34. X *    \>    matches the end of a word
  35. X *    .    matches any single character
  36. X *    []    matches any character in a character class
  37. X *    \(    delimits the start of a subexpression
  38. X *    \)    delimits the end of a subexpression
  39. X *    *    repeats the preceding 0 or more times
  40. X * NOTE: You cannot follow a \) with a *.
  41. X *
  42. X * The physical structure of a compiled RE is as follows:
  43. X *    - First, there is a one-byte value that says how many character classes
  44. X *      are used in this regular expression
  45. X *    - Next, each character class is stored as a bitmap that is 256 bits
  46. X *      (32 bytes) long.
  47. X *    - A mixture of literal characters and compiled meta characters follows.
  48. X *      This begins with M_BEGIN(0) and ends with M_END(0).  All meta chars
  49. X *      are stored as a \n followed by a one-byte code, so they take up two
  50. X *      bytes apiece.  Literal characters take up one byte apiece.  \n can't
  51. X *      be used as a literal character.
  52. X *
  53. X * If NO_MAGIC is defined, then a different set of functions is used instead.
  54. X * That right, this file contains TWO versions of the code.
  55. X */
  56. X
  57. X#include <setjmp.h>
  58. X#include <ctype.h>
  59. X#include "config.h"
  60. X#include "vi.h"
  61. X#include "regexp.h"
  62. X
  63. X
  64. X
  65. Xstatic char    *previous;    /* the previous regexp, used when null regexp is given */
  66. X
  67. X
  68. X#ifndef NO_MAGIC
  69. X/* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */
  70. X
  71. X/* These are used to classify or recognize meta-characters */
  72. X#define META        '\0'
  73. X#define BASE_META(m)    ((m) - 256)
  74. X#define INT_META(c)    ((c) + 256)
  75. X#define IS_META(m)    ((m) >= 256)
  76. X#define IS_CLASS(m)    ((m) >= M_CLASS(0) && (m) <= M_CLASS(9))
  77. X#define IS_START(m)    ((m) >= M_START(0) && (m) <= M_START(9))
  78. X#define IS_END(m)    ((m) >= M_END(0) && (m) <= M_END(9))
  79. X#define IS_CLOSURE(m)    ((m) >= M_SPLAT && (m) <= M_QMARK)
  80. X#define ADD_META(s,m)    (*(s)++ = META, *(s)++ = BASE_META(m))
  81. X#define GET_META(s)    (*(s) == META ? INT_META(*++(s)) : *s)
  82. X
  83. X/* These are the internal codes used for each type of meta-character */
  84. X#define M_BEGLINE    256        /* internal code for ^ */
  85. X#define M_ENDLINE    257        /* internal code for $ */
  86. X#define M_BEGWORD    258        /* internal code for \< */
  87. X#define M_ENDWORD    259        /* internal code for \> */
  88. X#define M_ANY        260        /* internal code for . */
  89. X#define M_SPLAT        261        /* internal code for * */
  90. X#define M_PLUS        262        /* internal code for \+ */
  91. X#define M_QMARK        263        /* internal code for \? */
  92. X#define M_CLASS(n)    (264+(n))    /* internal code for [] */
  93. X#define M_START(n)    (274+(n))    /* internal code for \( */
  94. X#define M_END(n)    (284+(n))    /* internal code for \) */
  95. X
  96. X/* These are used during compilation */
  97. Xstatic int    class_cnt;    /* used to assign class IDs */
  98. Xstatic int    start_cnt;    /* used to assign start IDs */
  99. Xstatic int    end_stk[NSUBEXP];/* used to assign end IDs */
  100. Xstatic int    end_sp;
  101. Xstatic char    *retext;    /* points to the text being compiled */
  102. X
  103. X/* error-handling stuff */
  104. Xjmp_buf    errorhandler;
  105. X#define FAIL(why)    regerror(why); longjmp(errorhandler, 1)
  106. X
  107. X
  108. X
  109. X
  110. X
  111. X/* This function builds a bitmap for a particular class */
  112. Xstatic char *makeclass(text, bmap)
  113. X    REG char    *text;    /* start of the class */
  114. X    REG char    *bmap;    /* the bitmap */
  115. X{
  116. X    REG int        i;
  117. X    int        complement = 0;
  118. X
  119. X# if TRACE
  120. X    printf("makeclass(\"%s\", 0x%lx)\n", text, (long)bmap);
  121. X# endif
  122. X
  123. X    /* zero the bitmap */
  124. X    for (i = 0; bmap && i < 32; i++)
  125. X    {
  126. X        bmap[i] = 0;
  127. X    }
  128. X
  129. X    /* see if we're going to complement this class */
  130. X    if (*text == '^')
  131. X    {
  132. X        text++;
  133. X        complement = 1;
  134. X    }
  135. X
  136. X    /* add in the characters */
  137. X    while (*text && *text != ']')
  138. X    {
  139. X        /* is this a span of characters? */
  140. X        if (text[1] == '-' && text[2])
  141. X        {
  142. X            /* spans can't be backwards */
  143. X            if (text[0] > text[2])
  144. X            {
  145. X                FAIL("Backwards span in []");
  146. X            }
  147. X
  148. X            /* add each character in the span to the bitmap */
  149. X            for (i = text[0]; bmap && i <= text[2]; i++)
  150. X            {
  151. X                bmap[i >> 3] |= (1 << (i & 7));
  152. X            }
  153. X
  154. X            /* move past this span */
  155. X            text += 3;
  156. X        }
  157. X        else
  158. X        {
  159. X            /* add this single character to the span */
  160. X            i = *text++;
  161. X            if (bmap)
  162. X            {
  163. X                bmap[i >> 3] |= (1 << (i & 7));
  164. X            }
  165. X        }
  166. X    }
  167. X
  168. X    /* make sure the closing ] is missing */
  169. X    if (*text++ != ']')
  170. X    {
  171. X        FAIL("] missing");
  172. X    }
  173. X
  174. X    /* if we're supposed to complement this class, then do so */
  175. X    if (complement && bmap)
  176. X    {
  177. X        for (i = 0; i < 32; i++)
  178. X        {
  179. X            bmap[i] = ~bmap[i];
  180. X        }
  181. X    }
  182. X
  183. X    return text;
  184. X}
  185. X
  186. X
  187. X
  188. X
  189. X/* This function gets the next character or meta character from a string.
  190. X * The pointer is incremented by 1, or by 2 for \-quoted characters.  For [],
  191. X * a bitmap is generated via makeclass() (if re is given), and the
  192. X * character-class text is skipped.
  193. X */
  194. Xstatic int gettoken(sptr, re)
  195. X    char    **sptr;
  196. X    regexp    *re;
  197. X{
  198. X    int    c;
  199. X
  200. X    c = **sptr;
  201. X    ++*sptr;
  202. X    if (c == '\\')
  203. X    {
  204. X        c = **sptr;
  205. X        ++*sptr;
  206. X        switch (c)
  207. X        {
  208. X          case '<':
  209. X            return M_BEGWORD;
  210. X
  211. X          case '>':
  212. X            return M_ENDWORD;
  213. X
  214. X          case '(':
  215. X            if (start_cnt >= NSUBEXP)
  216. X            {
  217. X                FAIL("Too many \\(s");
  218. X            }
  219. X            end_stk[end_sp++] = start_cnt;
  220. X            return M_START(start_cnt++);
  221. X
  222. X          case ')':
  223. X            if (end_sp <= 0)
  224. X            {
  225. X                FAIL("Mismatched \\)");
  226. X            }
  227. X            return M_END(end_stk[--end_sp]);
  228. X
  229. X          case '*':
  230. X            return (*o_magic ? c : M_SPLAT);
  231. X
  232. X          case '.':
  233. X            return (*o_magic ? c : M_ANY);
  234. X
  235. X          case '+':
  236. X            return M_PLUS;
  237. X
  238. X          case '?':
  239. X            return M_QMARK;
  240. X
  241. X          default:
  242. X            return c;
  243. X        }
  244. X    }
  245. X    else if (*o_magic)
  246. X    {
  247. X        switch (c)
  248. X        {
  249. X          case '^':
  250. X            if (*sptr == retext + 1)
  251. X            {
  252. X                return M_BEGLINE;
  253. X            }
  254. X            return c;
  255. X
  256. X          case '$':
  257. X            if (!**sptr)
  258. X            {
  259. X                return M_ENDLINE;
  260. X            }
  261. X            return c;
  262. X
  263. X          case '.':
  264. X            return M_ANY;
  265. X
  266. X          case '*':
  267. X            return M_SPLAT;
  268. X
  269. X          case '[':
  270. X            /* make sure we don't have too many classes */
  271. X            if (class_cnt >= 10)
  272. X            {
  273. X                FAIL("Too many []s");
  274. X            }
  275. X
  276. X            /* process the character list for this class */
  277. X            if (re)
  278. X            {
  279. X                /* generate the bitmap for this class */
  280. X                *sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt);
  281. X            }
  282. X            else
  283. X            {
  284. X                /* skip to end of the class */
  285. X                *sptr = makeclass(*sptr, (char *)0);
  286. X            }
  287. X            return M_CLASS(class_cnt++);
  288. X
  289. X          default:
  290. X            return c;
  291. X        }
  292. X    }
  293. X    else    /* unquoted nomagic */
  294. X    {
  295. X        switch (c)
  296. X        {
  297. X          case '^':
  298. X            if (*sptr == retext + 1)
  299. X            {
  300. X                return M_BEGLINE;
  301. X            }
  302. X            return c;
  303. X
  304. X          case '$':
  305. X            if (!**sptr)
  306. X            {
  307. X                return M_ENDLINE;
  308. X            }
  309. X            return c;
  310. X
  311. X          default:
  312. X            return c;
  313. X        }
  314. X    }
  315. X    /*NOTREACHED*/
  316. X}
  317. X
  318. X
  319. X
  320. X
  321. X/* This function calculates the number of bytes that will be needed for a
  322. X * compiled RE.  Its argument is the uncompiled version.  It is not clever
  323. X * about catching syntax errors; that is done in a later pass.
  324. X */
  325. Xstatic unsigned calcsize(text)
  326. X    char        *text;
  327. X{
  328. X    unsigned    size;
  329. X    int        token;
  330. X
  331. X    retext = text;
  332. X    class_cnt = 0;
  333. X    start_cnt = 1;
  334. X    end_sp = 0;
  335. X    size = 5;
  336. X    while ((token = gettoken(&text, (regexp *)0)) != 0)
  337. X    {
  338. X        if (IS_CLASS(token))
  339. X        {
  340. X            size += 34;
  341. X        }
  342. X        else if (IS_META(token))
  343. X        {
  344. X            size += 2;
  345. X        }
  346. X        else
  347. X        {
  348. X            size++;
  349. X        }
  350. X    }
  351. X
  352. X    return size;
  353. X}
  354. X
  355. X
  356. X
  357. X/* This function compiles a regexp. */
  358. Xregexp *regcomp(text)
  359. X    char        *text;
  360. X{
  361. X    int        needfirst;
  362. X    unsigned    size;
  363. X    int        token;
  364. X    int        peek;
  365. X    char        *build;
  366. X    regexp        *re;
  367. X
  368. X
  369. X    /* prepare for error handling */
  370. X    re = (regexp *)0;
  371. X    if (setjmp(errorhandler))
  372. X    {
  373. X        if (re)
  374. X        {
  375. X            free(re);
  376. X        }
  377. X        return (regexp *)0;
  378. X    }
  379. X
  380. X    /* if an empty regexp string was given, use the previous one */
  381. X    if (*text == 0)
  382. X    {
  383. X        if (!previous)
  384. X        {
  385. X            FAIL("No previous RE");
  386. X        }
  387. X        text = previous;
  388. X    }
  389. X    else /* non-empty regexp given, so remember it */
  390. X    {
  391. X        if (previous)
  392. X            free(previous);
  393. X        previous = (char *)malloc((unsigned)(strlen(text) + 1));
  394. X        if (previous)
  395. X            strcpy(previous, text);
  396. X    }
  397. X
  398. X    /* allocate memory */
  399. X    class_cnt = 0;
  400. X    start_cnt = 1;
  401. X    end_sp = 0;
  402. X    retext = text;
  403. X    size = calcsize(text) + sizeof(regexp);
  404. X#ifdef lint
  405. X    re = ((regexp *)0) + size;
  406. X#else
  407. X    re = (regexp *)malloc((unsigned)size);
  408. X#endif
  409. X    if (!re)
  410. X    {
  411. X        FAIL("Not enough memory for this RE");
  412. X    }
  413. X
  414. X    /* compile it */
  415. X    build = &re->program[1 + 32 * class_cnt];
  416. X    re->program[0] = class_cnt;
  417. X    for (token = 0; token < NSUBEXP; token++)
  418. X    {
  419. X        re->startp[token] = re->endp[token] = (char *)0;
  420. X    }
  421. X    re->first = 0;
  422. X    re->bol = 0;
  423. X    re->minlen = 0;
  424. X    needfirst = 1;
  425. X    class_cnt = 0;
  426. X    start_cnt = 1;
  427. X    end_sp = 0;
  428. X    retext = text;
  429. X    for (token = M_START(0), peek = gettoken(&text, re);
  430. X         token;
  431. X         token = peek, peek = gettoken(&text, re))
  432. X    {
  433. X        /* special processing for the closure operator */
  434. X        if (IS_CLOSURE(peek))
  435. X        {
  436. X            /* detect misuse of closure operator */
  437. X            if (IS_START(token))
  438. X            {
  439. X                FAIL("* or \\+ or \\? follows nothing");
  440. X            }
  441. X            else if (IS_META(token) && token != M_ANY && !IS_CLASS(token))
  442. X            {
  443. X                FAIL("* or \\+ or \\? can only follow a normal character or . or []");
  444. X            }
  445. X
  446. X            /* it is okay -- make it prefix instead of postfix */
  447. X            ADD_META(build, peek);
  448. X
  449. X            /* take care of "needfirst" - is this the first char? */
  450. X            if (needfirst && peek == M_PLUS && !IS_META(token))
  451. X            {
  452. X                re->first = token;
  453. X            }
  454. X            needfirst = 0;
  455. X
  456. X            /* we used "peek" -- need to refill it */
  457. X            peek = gettoken(&text, re);
  458. X            if (IS_CLOSURE(peek))
  459. X            {
  460. X                FAIL("* or \\+ or \\? doubled up");
  461. X            }
  462. X        }
  463. X        else if (!IS_META(token))
  464. X        {
  465. X            /* normal char is NOT argument of closure */
  466. X            if (needfirst)
  467. X            {
  468. X                re->first = token;
  469. X                needfirst = 0;
  470. X            }
  471. X            re->minlen++;
  472. X        }
  473. X        else if (token == M_ANY || IS_CLASS(token))
  474. X        {
  475. X            /* . or [] is NOT argument of closure */
  476. X            needfirst = 0;
  477. X            re->minlen++;
  478. X        }
  479. X
  480. X        /* the "token" character is not closure -- process it normally */
  481. X        if (token == M_BEGLINE)
  482. X        {
  483. X            /* set the BOL flag instead of storing M_BEGLINE */
  484. X            re->bol = 1;
  485. X        }
  486. X        else if (IS_META(token))
  487. X        {
  488. X            ADD_META(build, token);
  489. X        }
  490. X        else
  491. X        {
  492. X            *build++ = token;
  493. X        }
  494. X    }
  495. X
  496. X    /* end it with a \) which MUST MATCH the opening \( */
  497. X    ADD_META(build, M_END(0));
  498. X    if (end_sp > 0)
  499. X    {
  500. X        FAIL("Not enough \\)s");
  501. X    }
  502. X
  503. X    return re;
  504. X}
  505. X
  506. X
  507. X
  508. X/*---------------------------------------------------------------------------*/
  509. X
  510. X
  511. X/* This function checks for a match between a character and a token which is
  512. X * known to represent a single character.  It returns 0 if they match, or
  513. X * 1 if they don't.
  514. X */
  515. Xint match1(re, ch, token)
  516. X    regexp        *re;
  517. X    REG char    ch;
  518. X    REG int        token;
  519. X{
  520. X    if (!ch)
  521. X    {
  522. X        /* the end of a line can't match any RE of width 1 */
  523. X        return 1;
  524. X    }
  525. X    if (token == M_ANY)
  526. X    {
  527. X        return 0;
  528. X    }
  529. X    else if (IS_CLASS(token))
  530. X    {
  531. X        if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7)))
  532. X            return 0;
  533. X    }
  534. X    else if (ch == token
  535. X        || (*o_ignorecase && isupper(ch) && tolower(ch) == token))
  536. X    {
  537. X        return 0;
  538. X    }
  539. X    return 1;
  540. X}
  541. X
  542. X
  543. X
  544. X/* This function checks characters up to and including the next closure, at
  545. X * which point it does a recursive call to check the rest of it.  This function
  546. X * returns 0 if everything matches, or 1 if something doesn't match.
  547. X */
  548. Xint match(re, str, prog, here)
  549. X    regexp        *re;    /* the regular expression */
  550. X    char        *str;    /* the string */
  551. X    REG char    *prog;    /* a portion of re->program, an compiled RE */
  552. X    REG char    *here;    /* a portion of str, the string to compare it to */
  553. X{
  554. X    REG int        token;
  555. X    REG int        nmatched;
  556. X    REG int        closure;
  557. X
  558. X    for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog))
  559. X    {
  560. X        switch (token)
  561. X        {
  562. X        /*case M_BEGLINE: can't happen; re->bol is used instead */
  563. X          case M_ENDLINE:
  564. X            if (*here)
  565. X                return 1;
  566. X            break;
  567. X
  568. X          case M_BEGWORD:
  569. X            if (here != str &&
  570. X               (here[-1] == '_' ||
  571. X                 isascii(here[-1]) && isalnum(here[-1])))
  572. X                return 1;
  573. X            break;
  574. X
  575. X          case M_ENDWORD:
  576. X            if (here[0] == '_' || isascii(here[0]) && isalnum(here[0]))
  577. X                return 1;
  578. X            break;
  579. X
  580. X          case M_START(0):
  581. X          case M_START(1):
  582. X          case M_START(2):
  583. X          case M_START(3):
  584. X          case M_START(4):
  585. X          case M_START(5):
  586. X          case M_START(6):
  587. X          case M_START(7):
  588. X          case M_START(8):
  589. X          case M_START(9):
  590. X            re->startp[token - M_START(0)] = (char *)here;
  591. X            break;
  592. X
  593. X          case M_END(0):
  594. X          case M_END(1):
  595. X          case M_END(2):
  596. X          case M_END(3):
  597. X          case M_END(4):
  598. X          case M_END(5):
  599. X          case M_END(6):
  600. X          case M_END(7):
  601. X          case M_END(8):
  602. X          case M_END(9):
  603. X            re->endp[token - M_END(0)] = (char *)here;
  604. X            if (token == M_END(0))
  605. X            {
  606. X                return 0;
  607. X            }
  608. X            break;
  609. X
  610. X          default: /* literal, M_CLASS(n), or M_ANY */
  611. X            if (match1(re, *here, token) != 0)
  612. X                return 1;
  613. X            here++;
  614. X        }
  615. X    }
  616. X
  617. X    /* C L O S U R E */
  618. X
  619. X    /* step 1: see what we have to match against, and move "prog" to point
  620. X     * the the remainder of the compiled RE.
  621. X     */
  622. X    closure = token;
  623. X    prog++, token = GET_META(prog);
  624. X    prog++;
  625. X
  626. X    /* step 2: see how many times we can match that token against the string */
  627. X    for (nmatched = 0;
  628. X         (closure != M_QMARK || nmatched < 1) && *here && match1(re, *here, token) == 0;
  629. X         nmatched++, here++)
  630. X    {
  631. X    }
  632. X
  633. X    /* step 3: try to match the remainder, and back off if it doesn't */
  634. X    while (nmatched >= 0 && match(re, str, prog, here) != 0)
  635. X    {
  636. X        nmatched--;
  637. X        here--;
  638. X    }
  639. X
  640. X    /* so how did it work out? */
  641. X    if (nmatched >= ((closure == M_PLUS) ? 1 : 0))
  642. X        return 0;
  643. X    return 1;
  644. X}
  645. X
  646. X
  647. X
  648. X/* This function searches through a string for text that matches an RE. */
  649. Xint regexec(re, str, bol)
  650. X    regexp    *re;    /* the compiled regexp to search for */
  651. X    char    *str;    /* the string to search through */
  652. X    int    bol;    /* boolean: does str start at the beginning of a line? */
  653. X{
  654. X    char    *prog;    /* the entry point of re->program */
  655. X    int    len;    /* length of the string */
  656. X    REG char    *here;
  657. X
  658. X    /* if must start at the beginning of a line, and this isn't, then fail */
  659. X    if (re->bol && !bol)
  660. X    {
  661. X        return 0;
  662. X    }
  663. X
  664. X    len = strlen(str);
  665. X    prog = re->program + 1 + 32 * re->program[0];
  666. X
  667. X    /* search for the RE in the string */
  668. X    if (re->bol)
  669. X    {
  670. X        /* must occur at BOL */
  671. X        if ((re->first
  672. X            && match1(re, *(char *)str, re->first))/* wrong first letter? */
  673. X         || len < re->minlen            /* not long enough? */
  674. X         || match(re, (char *)str, prog, str))    /* doesn't match? */
  675. X            return 0;            /* THEN FAIL! */
  676. X    }
  677. X#ifndef CRUNCH
  678. X    else if (!*o_ignorecase)
  679. X    {
  680. X        /* can occur anywhere in the line, noignorecase */
  681. X        for (here = (char *)str;
  682. X             (re->first && re->first != *here)
  683. X            || match(re, (char *)str, prog, here);
  684. X             here++, len--)
  685. X        {
  686. X            if (len < re->minlen)
  687. X                return 0;
  688. X        }
  689. X    }
  690. X#endif
  691. X    else
  692. X    {
  693. X        /* can occur anywhere in the line, ignorecase */
  694. X        for (here = (char *)str;
  695. X             (re->first && match1(re, *here, (int)re->first))
  696. X            || match(re, (char *)str, prog, here);
  697. X             here++, len--)
  698. X        {
  699. X            if (len < re->minlen)
  700. X                return 0;
  701. X        }
  702. X    }
  703. X
  704. X    /* if we didn't fail, then we must have succeeded */
  705. X    return 1;
  706. X}
  707. X
  708. X#else /* NO_MAGIC */
  709. X
  710. Xregexp *regcomp(exp)
  711. X    char    *exp;
  712. X{
  713. X    char    *src;
  714. X    char    *dest;
  715. X    regexp    *re;
  716. X    int    i;
  717. X
  718. X    /* allocate a big enough regexp structure */
  719. X#ifdef lint
  720. X    re = (regexp *)0;
  721. X#else
  722. X    re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp)));
  723. X#endif
  724. X    if (!re)
  725. X    {
  726. X        regerror("Could not malloc a regexp structure");
  727. X        return (regexp *)0;
  728. X    }
  729. X
  730. X    /* initialize all fields of the structure */
  731. X    for (i = 0; i < NSUBEXP; i++)
  732. X    {
  733. X        re->startp[i] = re->endp[i] = (char *)0;
  734. X    }
  735. X    re->minlen = 0;
  736. X    re->first = 0;
  737. X    re->bol = 0;
  738. X
  739. X    /* copy the string into it, translating ^ and $ as needed */
  740. X    for (src = exp, dest = re->program + 1; *src; src++)
  741. X    {
  742. X        switch (*src)
  743. X        {
  744. X          case '^':
  745. X            if (src == exp)
  746. X            {
  747. X                re->bol += 1;
  748. X            }
  749. X            else
  750. X            {
  751. X                *dest++ = '^';
  752. X                re->minlen++;
  753. X            }
  754. X            break;
  755. X
  756. X          case '$':
  757. X            if (!src[1])
  758. X            {
  759. X                re->bol += 2;
  760. X            }
  761. X            else
  762. X            {
  763. X                *dest++ = '$';
  764. X                re->minlen++;
  765. X            }
  766. X            break;
  767. X
  768. X          case '\\':
  769. X            if (src[1])
  770. X            {
  771. X                *dest++ = *++src;
  772. X                re->minlen++;
  773. X            }
  774. X            else
  775. X            {
  776. X                regerror("extra \\ at end of regular expression");
  777. X            }
  778. X            break;
  779. X
  780. X          default:
  781. X            *dest++ = *src;
  782. X            re->minlen++;
  783. X        }
  784. X    }
  785. X    *dest = '\0';
  786. X
  787. X    return re;
  788. X}
  789. X
  790. X
  791. X/* This "helper" function checks for a match at a given location.  It returns
  792. X * 1 if it matches, 0 if it doesn't match here but might match later on in the
  793. X * string, or -1 if it could not possibly match
  794. X */
  795. Xstatic int reghelp(prog, string, bolflag)
  796. X    struct regexp    *prog;
  797. X    char        *string;
  798. X    int        bolflag;
  799. X{
  800. X    char        *scan;
  801. X    char        *str;
  802. X
  803. X    /* if ^, then require bolflag */
  804. X    if ((prog->bol & 1) && !bolflag)
  805. X    {
  806. X        return -1;
  807. X    }
  808. X
  809. X    /* if it matches, then it will start here */
  810. X    prog->startp[0] = string;
  811. X
  812. X    /* compare, possibly ignoring case */
  813. X    if (*o_ignorecase)
  814. X    {
  815. X        for (scan = &prog->program[1]; *scan; scan++, string++)
  816. X            if (tolower(*scan) != tolower(*string))
  817. X                return *string ? 0 : -1;
  818. X    }
  819. X    else
  820. X    {
  821. X        for (scan = &prog->program[1]; *scan; scan++, string++)
  822. X            if (*scan != *string)
  823. X                return *string ? 0 : -1;
  824. X    }
  825. X
  826. X    /* if $, then require string to end here, too */
  827. X    if ((prog->bol & 2) && *string)
  828. X    {
  829. X        return 0;
  830. X    }
  831. X
  832. X    /* if we get to here, it matches */
  833. X    prog->endp[0] = string;
  834. X    return 1;
  835. X}
  836. X
  837. X
  838. X
  839. Xint regexec(prog, string, bolflag)
  840. X    struct regexp    *prog;
  841. X    char        *string;
  842. X    int        bolflag;
  843. X{
  844. X    int        rc;
  845. X
  846. X    /* keep trying to match it */
  847. X    for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0))
  848. X    {
  849. X        string++;
  850. X    }
  851. X
  852. X    /* did we match? */
  853. X    return rc == 1;
  854. X}
  855. X#endif
  856. eof
  857. if test `wc -c <regexp.c` -ne 16871
  858. then
  859. echo regexp.c damaged!
  860. fi
  861. fi
  862.  
  863. if test -f regexp.h -a "$1" != -f
  864. then
  865. echo Will not overwrite regexp.h
  866. else
  867. echo Extracting regexp.h
  868. sed 's/^X//' >regexp.h <<\eof
  869. X/*
  870. X * Definitions etc. for regexp(3) routines.
  871. X *
  872. X * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
  873. X * not the System V one.
  874. X */
  875. X#define NSUBEXP  10
  876. X
  877. Xtypedef struct regexp {
  878. X    char    *startp[NSUBEXP];
  879. X    char    *endp[NSUBEXP];
  880. X    int    minlen;        /* length of shortest possible match */
  881. X    char    first;        /* first character, if known; else \0 */
  882. X    char    bol;        /* boolean: must start at beginning of line? */
  883. X    char    program[1];    /* Unwarranted chumminess with compiler. */
  884. X} regexp;
  885. X
  886. Xextern regexp *regcomp();
  887. Xextern int regexec();
  888. Xextern void regsub();
  889. Xextern void regerror();
  890. eof
  891. if test `wc -c <regexp.h` -ne 579
  892. then
  893. echo regexp.h damaged!
  894. fi
  895. fi
  896.  
  897. if test -f regsub.c -a "$1" != -f
  898. then
  899. echo Will not overwrite regsub.c
  900. else
  901. echo Extracting regsub.c
  902. sed 's/^X//' >regsub.c <<\eof
  903. X/* regsub.c */
  904. X
  905. X/* This file contains the regsub() function, which performs substitutions
  906. X * after a regexp match has been found.
  907. X */
  908. X
  909. X#include <ctype.h>
  910. X#include "config.h"
  911. X#include "vi.h"
  912. X#include "regexp.h"
  913. X
  914. Xstatic char *previous;    /* a copy of the text from the previous substitution */
  915. X
  916. X/* perform substitutions after a regexp match */
  917. Xvoid regsub(re, src, dst)
  918. X    regexp        *re;
  919. X    REG char    *src;
  920. X    REG char    *dst;
  921. X{
  922. X    REG char    *cpy;
  923. X    REG char    *end;
  924. X    REG char    c;
  925. X    char        *start;
  926. X#ifndef CRUNCH
  927. X    int        mod;
  928. X
  929. X    mod = 0;
  930. X#endif
  931. X
  932. X    start = src;
  933. X    while ((c = *src++) != '\0')
  934. X    {
  935. X#ifndef NO_MAGIC
  936. X        /* recognize any meta characters */
  937. X        if (c == '&' && *o_magic)
  938. X        {
  939. X            cpy = re->startp[0];
  940. X            end = re->endp[0];
  941. X        }
  942. X        else if (c == '~' && *o_magic)
  943. X        {
  944. X            cpy = previous;
  945. X            if (cpy)
  946. X                end = cpy + strlen(cpy);
  947. X        }
  948. X        else
  949. X#endif /* not NO_MAGIC */
  950. X        if (c == '\\')
  951. X        {
  952. X            c = *src++;
  953. X            switch (c)
  954. X            {
  955. X#ifndef NO_MAGIC
  956. X              case '0':
  957. X              case '1':
  958. X              case '2':
  959. X              case '3':
  960. X              case '4':
  961. X              case '5':
  962. X              case '6':
  963. X              case '7':
  964. X              case '8':
  965. X              case '9':
  966. X                /* \0 thru \9 mean "copy subexpression" */
  967. X                c -= '0';
  968. X                cpy = re->startp[c];
  969. X                end = re->endp[c];
  970. X                break;
  971. X# ifndef CRUNCH
  972. X              case 'U':
  973. X              case 'u':
  974. X              case 'L':
  975. X              case 'l':
  976. X                /* \U and \L mean "convert to upper/lowercase" */
  977. X                mod = c;
  978. X                continue;
  979. X
  980. X              case 'E':
  981. X              case 'e':
  982. X                /* \E ends the \U or \L */
  983. X                mod = 0;
  984. X                continue;
  985. X# endif /* not CRUNCH */
  986. X              case '&':
  987. X                /* "\&" means "original text" */
  988. X                if (*o_magic)
  989. X                {
  990. X                    *dst++ = c;
  991. X                    continue;
  992. X                }
  993. X                cpy = re->startp[0];
  994. X                end = re->endp[0];
  995. X                break;
  996. X
  997. X              case '~':
  998. X                /* "\~" means "previous text, if any" */
  999. X                if (*o_magic)
  1000. X                {
  1001. X                    *dst++ = c;
  1002. X                    continue;
  1003. X                }
  1004. X                cpy = previous;
  1005. X                if (cpy)
  1006. X                    end = cpy + strlen(cpy);
  1007. X                break;
  1008. X#else /* NO_MAGIC */
  1009. X              case '&':
  1010. X                /* "\&" means "original text" */
  1011. X                cpy = re->startp[0];
  1012. X                end = re->endp[0];
  1013. X                break;
  1014. X
  1015. X              case '~':
  1016. X                /* "\~" means "previous text, if any" */
  1017. X                cpy = previous;
  1018. X                if (cpy)
  1019. X                    end = cpy + strlen(cpy);
  1020. X                break;
  1021. X#endif /* NO_MAGIC */
  1022. X              default:
  1023. X                /* ordinary char preceded by backslash */
  1024. X                *dst++ = c;
  1025. X                continue;
  1026. X            }
  1027. X        }
  1028. X        else
  1029. X        {
  1030. X            /* ordinary character, so just copy it */
  1031. X            *dst++ = c;
  1032. X            continue;
  1033. X        }
  1034. X
  1035. X        /* Note: to reach this point in the code, we must have evaded
  1036. X         * all "continue" statements.  To do that, we must have hit
  1037. X         * a metacharacter that involves copying.
  1038. X         */
  1039. X
  1040. X        /* if there is nothing to copy, loop */
  1041. X        if (!cpy)
  1042. X            continue;
  1043. X
  1044. X        /* copy over a portion of the original */
  1045. X        while (cpy < end)
  1046. X        {
  1047. X#ifndef NO_MAGIC
  1048. X# ifndef CRUNCH
  1049. X            switch (mod)
  1050. X            {
  1051. X              case 'U':
  1052. X              case 'u':
  1053. X                /* convert to uppercase */
  1054. X                if (isascii(*cpy) && islower(*cpy))
  1055. X                {
  1056. X                    *dst++ = toupper(*cpy);
  1057. X                    cpy++;
  1058. X                }
  1059. X                else
  1060. X                {
  1061. X                    *dst++ = *cpy++;
  1062. X                }
  1063. X                break;
  1064. X
  1065. X              case 'L':
  1066. X              case 'l':
  1067. X                /* convert to lowercase */
  1068. X                if (isascii(*cpy) && isupper(*cpy))
  1069. X                {
  1070. X                    *dst++ = tolower(*cpy);
  1071. X                    cpy++;
  1072. X                }
  1073. X                else
  1074. X                {
  1075. X                    *dst++ = *cpy++;
  1076. X                }
  1077. X                break;
  1078. X
  1079. X              default:
  1080. X                /* copy without any conversion */
  1081. X                *dst++ = *cpy++;
  1082. X            }
  1083. X
  1084. X            /* \u and \l end automatically after the first char */
  1085. X            if (mod && (mod == 'u' || mod == 'l'))
  1086. X            {
  1087. X                mod = 0;
  1088. X            }
  1089. X# else /* CRUNCH */
  1090. X            *dst++ = *cpy++;
  1091. X# endif /* CRUNCH */
  1092. X#else /* NO_MAGIC */
  1093. X            *dst++ = *cpy++;
  1094. X#endif /* NO_MAGIC */
  1095. X        }
  1096. X    }
  1097. X    *dst = '\0';
  1098. X
  1099. X    /* remember what text we inserted this time */
  1100. X    if (previous)
  1101. X        free(previous);
  1102. X    previous = (char *)malloc((unsigned)(strlen(start) + 1));
  1103. X    if (previous)
  1104. X        strcpy(previous, start);
  1105. X}
  1106. eof
  1107. if test `wc -c <regsub.c` -ne 3614
  1108. then
  1109. echo regsub.c damaged!
  1110. fi
  1111. fi
  1112.  
  1113. if test -f system.c -a "$1" != -f
  1114. then
  1115. echo Will not overwrite system.c
  1116. else
  1117. echo Extracting system.c
  1118. sed 's/^X//' >system.c <<\eof
  1119. X/* system.c  -- UNIX version */
  1120. X
  1121. X/* Author:
  1122. X *    Steve Kirkendall
  1123. X *    14407 SW Teal Blvd. #C
  1124. X *    Beaverton, OR 97005
  1125. X *    kirkenda@cs.pdx.edu
  1126. X */
  1127. X
  1128. X
  1129. X/* This file contains a new version of the system() function and related stuff.
  1130. X *
  1131. X * Entry points are:
  1132. X *    system(cmd)    - run a single shell command
  1133. X *    wildcard(names)    - expand wildcard characters in filanames
  1134. X *    filter(m,n,cmd)    - run text lines through a filter program
  1135. X *
  1136. X * This is probably the single least portable file in the program.  The code
  1137. X * shown here should work correctly if it links at all; it will work on UNIX
  1138. X * and any O.S./Compiler combination which adheres to UNIX forking conventions.
  1139. X */
  1140. X
  1141. X#include "config.h"
  1142. X#include "vi.h"
  1143. X#include <signal.h>
  1144. Xextern char    **environ;
  1145. X
  1146. X#if ANY_UNIX
  1147. X
  1148. X/* This is a new version of the system() function.  The only difference
  1149. X * between this one and the library one is: this one uses the o_shell option.
  1150. X */
  1151. Xint system(cmd)
  1152. X    char    *cmd;    /* a command to run */
  1153. X{
  1154. X    int    status;    /* exit status of the command */
  1155. X
  1156. X    /* warn the user if the file hasn't been saved yet */
  1157. X    if (*o_warn && tstflag(file, MODIFIED))
  1158. X    {
  1159. X        if (mode == MODE_VI)
  1160. X        {
  1161. X            mode = MODE_COLON;
  1162. X        }
  1163. X        msg("Warning: \"%s\" has been modified but not yet saved", origname);
  1164. X    }
  1165. X
  1166. X    signal(SIGINT, SIG_IGN);
  1167. X    switch (fork())
  1168. X    {
  1169. X      case -1:                        /* error */
  1170. X        msg("fork() failed");
  1171. X        status = -1;
  1172. X        break;
  1173. X
  1174. X      case 0:                        /* child */
  1175. X        /* for the child, close all files except stdin/out/err */
  1176. X        for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
  1177. X        {
  1178. X        }
  1179. X
  1180. X        signal(SIGINT, SIG_DFL);
  1181. X        if (cmd == o_shell)
  1182. X        {
  1183. X            execle(o_shell, o_shell, (char *)0, environ);
  1184. X        }
  1185. X        else
  1186. X        {
  1187. X            execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
  1188. X        }
  1189. X        msg("execle(\"%s\", ...) failed", o_shell);
  1190. X        exit(1); /* if we get here, the exec failed */
  1191. X
  1192. X      default:                        /* parent */
  1193. X        wait(&status);
  1194. X        signal(SIGINT, trapint);
  1195. X    }
  1196. X
  1197. X    return status;
  1198. X}
  1199. X
  1200. X/* This private function opens a pipe from a filter.  It is similar to the
  1201. X * system() function above, and to popen(cmd, "r").
  1202. X */
  1203. Xstatic int rpipe(cmd, in)
  1204. X    char    *cmd;    /* the filter command to use */
  1205. X    int    in;    /* the fd to use for stdin */
  1206. X{
  1207. X    int    r0w1[2];/* the pipe fd's */
  1208. X
  1209. X    /* make the pipe */
  1210. X    if (pipe(r0w1) < 0)
  1211. X    {
  1212. X        return -1;    /* pipe failed */
  1213. X    }
  1214. X
  1215. X    /* The parent process (elvis) ignores signals while the filter runs.
  1216. X     * The child (the filter program) will reset this, so that it can
  1217. X     * catch the signal.
  1218. X     */
  1219. X    signal(SIGINT, SIG_IGN);
  1220. X
  1221. X    switch (fork())
  1222. X    {
  1223. X      case -1:                        /* error */
  1224. X        return -1;
  1225. X
  1226. X      case 0:                        /* child */
  1227. X        /* close the "read" end of the pipe */
  1228. X        close(r0w1[0]);
  1229. X
  1230. X        /* redirect stdout to go to the "write" end of the pipe */
  1231. X        close(1);
  1232. X        dup(r0w1[1]);
  1233. X        close(2);
  1234. X        dup(r0w1[1]);
  1235. X        close(r0w1[1]);
  1236. X
  1237. X        /* redirect stdin */
  1238. X        if (in != 0)
  1239. X        {
  1240. X            close(0);
  1241. X            dup(in);
  1242. X            close(in);
  1243. X        }
  1244. X
  1245. X        /* the filter should accept SIGINT signals */
  1246. X        signal(SIGINT, SIG_DFL);
  1247. X
  1248. X        /* exec the shell to run the command */
  1249. X        execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
  1250. X        exit(1); /* if we get here, exec failed */
  1251. X
  1252. X      default:                        /* parent */
  1253. X        /* close the "write" end of the pipe */    
  1254. X        close(r0w1[1]);
  1255. X
  1256. X        return r0w1[0];
  1257. X    }
  1258. X}
  1259. X
  1260. X#endif /* non-DOS */
  1261. X
  1262. X#if OSK
  1263. X
  1264. X/* This private function opens a pipe from a filter.  It is similar to the
  1265. X * system() function above, and to popen(cmd, "r").
  1266. X */
  1267. Xstatic int rpipe(cmd, in)
  1268. X    char    *cmd;    /* the filter command to use */
  1269. X    int    in;    /* the fd to use for stdin */
  1270. X{
  1271. X
  1272. X    char **argblk;
  1273. X    char *p, *buffer, *command;
  1274. X    int stdinp, stdoutp;
  1275. X    unsigned addstack = 0;
  1276. X    int words, len, loop = 1;
  1277. X    int fp, pipe_pid;
  1278. X    extern int os9forkc();
  1279. X    extern char *index();
  1280. X
  1281. X    command = cmd;
  1282. X    words = 0;
  1283. X    if ((buffer = (char*) malloc(strlen(cmd))) == (char*) 0)
  1284. X        return 0;
  1285. X
  1286. X    do {
  1287. X        if (!(p = index(command, ' '))) {
  1288. X            loop--;
  1289. X            len = strlen(command);
  1290. X        }
  1291. X        else
  1292. X            len = p - command;
  1293. X        words++;    
  1294. X        while (command[len] && command[len] == ' ')
  1295. X            len++;
  1296. X        if (!command[len])
  1297. X            break;
  1298. X        command = command + len;
  1299. X    }    
  1300. X    while (loop);
  1301. X    if ((argblk = (char **)malloc((words+1) * sizeof(char*))) == (char **)0)
  1302. X        return 0;
  1303. X    command = cmd;
  1304. X    words = 0;
  1305. X    do {
  1306. X        if (!(p = index(command, ' '))) {
  1307. X            loop--;
  1308. X            len = strlen(command);
  1309. X        }
  1310. X        else
  1311. X            len = p - command;
  1312. X        strncpy(buffer, command, len);
  1313. X        argblk[words++] = buffer;
  1314. X        buffer += len;
  1315. X        *buffer++ = '\0';
  1316. X        while (command[len] && command[len] == ' ')
  1317. X            len++;
  1318. X        if (!command[len])
  1319. X            break;
  1320. X        command = command + len;
  1321. X    } while (loop);
  1322. X    if (argblk[words - 1][0] == '#') 
  1323. X        addstack = 1024 * atoi(&argblk[--words][1]);
  1324. X    argblk[words] = 0;
  1325. X
  1326. X    stdoutp = dup(1);
  1327. X    close(1);               /* close stdout */
  1328. X    if ((fp = open("/pipe",S_IREAD)) < 0) {
  1329. X        dup(stdoutp);
  1330. X        close(stdoutp);
  1331. X        return 0;
  1332. X    }
  1333. X    if (in != 0) {
  1334. X        stdinp = dup(0);
  1335. X        close(0);
  1336. X        dup(in);
  1337. X        close(in);
  1338. X    }
  1339. X    pipe_pid = os9exec(os9forkc,argblk[0],argblk,environ,addstack,0,3);
  1340. X    if (pipe_pid == -1) {
  1341. X        fclose(fp);
  1342. X        dup(stdoutp);
  1343. X        close(stdoutp);
  1344. X        if (in != 0) {
  1345. X            close(0);
  1346. X            dup(stdinp);
  1347. X        }
  1348. X        return 0;
  1349. X    }
  1350. X    fp = (short)dup(1);     /* save pipe */
  1351. X    close(1);               /* get rid of the pipe */
  1352. X    dup(stdoutp);           /* restore old stdout */
  1353. X    close(stdoutp);         /* close path to stdout copy */
  1354. X    if (in != 0) {
  1355. X        close(0);
  1356. X        dup(stdinp);
  1357. X    }
  1358. X    return fp;
  1359. X}    
  1360. X#endif
  1361. X
  1362. X#if ANY_UNIX || OSK
  1363. X
  1364. X/* This function closes the pipe opened by rpipe(), and returns 0 for success */
  1365. Xstatic int rpclose(fd)
  1366. X    int    fd;
  1367. X{
  1368. X    int    status;
  1369. X
  1370. X    close(fd);
  1371. X    wait(&status);
  1372. X    signal(SIGINT, trapint);
  1373. X    return status;
  1374. X}
  1375. X
  1376. X#endif /* non-DOS */
  1377. X
  1378. X/* This function expands wildcards in a filename or filenames.  It does this
  1379. X * by running the "echo" command on the filenames via the shell; it is assumed
  1380. X * that the shell will expand the names for you.  If for any reason it can't
  1381. X * run echo, then it returns the names unmodified.
  1382. X */
  1383. X
  1384. X#if MSDOS || TOS
  1385. X#define    PROG    "wildcard "
  1386. X#define    PROGLEN    9
  1387. X#include <string.h>
  1388. X#else
  1389. X#define    PROG    "echo "
  1390. X#define    PROGLEN    5
  1391. X#endif
  1392. X
  1393. Xchar *wildcard(names)
  1394. X    char    *names;
  1395. X{
  1396. X    int    i, j, fd;
  1397. X    REG char *s, *d;
  1398. X
  1399. X
  1400. X    /* build the echo command */
  1401. X    if (names != tmpblk.c)
  1402. X    {
  1403. X        /* the names aren't in tmpblk.c, so we can do it the easy way */
  1404. X        strcpy(tmpblk.c, PROG);
  1405. X        strcat(tmpblk.c, names);
  1406. X    }
  1407. X    else
  1408. X    {
  1409. X        /* the names are already in tmpblk.c, so shift them to make
  1410. X         * room for the word "echo "
  1411. X         */
  1412. X        for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
  1413. X        {
  1414. X            *--d = *--s;
  1415. X        }
  1416. X        strncpy(names, PROG, PROGLEN);
  1417. X    }
  1418. X
  1419. X    /* run the command & read the resulting names */
  1420. X    fd = rpipe(tmpblk.c, 0);
  1421. X    if (fd < 0) return names;
  1422. X    i = 0;
  1423. X    do
  1424. X    {
  1425. X        j = tread(fd, tmpblk.c + i, BLKSIZE - i);
  1426. X        i += j;
  1427. X    } while (j > 0);
  1428. X
  1429. X    /* successful? */
  1430. X    if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
  1431. X    {
  1432. X        tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
  1433. X        return tmpblk.c;
  1434. X    }
  1435. X    else
  1436. X    {
  1437. X        return names;
  1438. X    }
  1439. X}
  1440. X
  1441. X/* This function runs a range of lines through a filter program, and replaces
  1442. X * the original text with the filtered version.  As a special case, if "to"
  1443. X * is MARK_UNSET, then it runs the filter program with stdin coming from
  1444. X * /dev/null, and inserts any output lines.
  1445. X */
  1446. Xint filter(from, to, cmd)
  1447. X    MARK    from, to;    /* the range of lines to filter */
  1448. X    char    *cmd;        /* the filter command */
  1449. X{
  1450. X    int    scratch;    /* fd of the scratch file */
  1451. X    int    fd;        /* fd of the pipe from the filter */
  1452. X    char    scrout[50];    /* name of the scratch out file */
  1453. X    MARK    new;        /* place where new text should go */
  1454. X    int    i;
  1455. X
  1456. X    /* write the lines (if specified) to a temp file */
  1457. X    if (to)
  1458. X    {
  1459. X        /* we have lines */
  1460. X#if MSDOS || TOS
  1461. X        strcpy(scrout, o_directory);
  1462. X        if ((i=strlen(scrout)) && strchr("\\/:", scrout[i-1]))
  1463. X            scrout[i++]=SLASH;
  1464. X        strcpy(scrout+i, SCRATCHOUT+3);
  1465. X#else
  1466. X        sprintf(scrout, SCRATCHOUT, o_directory);
  1467. X#endif
  1468. X        mktemp(scrout);
  1469. X        cmd_write(from, to, CMD_BANG, 0, scrout);
  1470. X
  1471. X        /* use those lines as stdin */
  1472. X        scratch = open(scrout, O_RDONLY);
  1473. X        if (scratch < 0)
  1474. X        {
  1475. X            unlink(scrout);
  1476. X            return -1;
  1477. X        }
  1478. X    }
  1479. X    else
  1480. X    {
  1481. X        scratch = 0;
  1482. X    }
  1483. X
  1484. X    /* start the filter program */
  1485. X    fd = rpipe(cmd, scratch);
  1486. X    if (fd < 0)
  1487. X    {
  1488. X        if (to)
  1489. X        {
  1490. X            close(scratch);
  1491. X            unlink(scrout);
  1492. X        }
  1493. X        return -1;
  1494. X    }
  1495. X
  1496. X    ChangeText
  1497. X    {
  1498. X        /* adjust MARKs for whole lines, and set "new" */
  1499. X        from &= ~(BLKSIZE - 1);
  1500. X        if (to)
  1501. X        {
  1502. X            to &= ~(BLKSIZE - 1);
  1503. X            to += BLKSIZE;
  1504. X            new = to;
  1505. X        }
  1506. X        else
  1507. X        {
  1508. X            new = from + BLKSIZE;
  1509. X        }
  1510. X
  1511. X        /* repeatedly read in new text and add it */
  1512. X        while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  1513. X        {
  1514. X            tmpblk.c[i] = '\0';
  1515. X            add(new, tmpblk.c);
  1516. X            for (i = 0; tmpblk.c[i]; i++)
  1517. X            {
  1518. X                if (tmpblk.c[i] == '\n')
  1519. X                {
  1520. X                    new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
  1521. X                }
  1522. X                else
  1523. X                {
  1524. X                    new++;
  1525. X                }
  1526. X            }
  1527. X        }
  1528. X    }
  1529. X
  1530. X    /* delete old text, if any */
  1531. X    if (to)
  1532. X    {
  1533. X        delete(from, to);
  1534. X    }
  1535. X
  1536. X    /* Reporting... */
  1537. X    rptlabel = "more";
  1538. X    if (rptlines < 0)
  1539. X    {
  1540. X        rptlines = -rptlines;
  1541. X        rptlabel = "less";
  1542. X    }
  1543. X
  1544. X    /* cleanup */
  1545. X    rpclose(fd);
  1546. X    if (to)
  1547. X    {
  1548. X        close(scratch);
  1549. X        unlink(scrout);
  1550. X    }
  1551. X    return 0;
  1552. X}
  1553. eof
  1554. if test `wc -c <system.c` -ne 8849
  1555. then
  1556. echo system.c damaged!
  1557. fi
  1558. fi
  1559.  
  1560. if test -f tinytcap.c -a "$1" != -f
  1561. then
  1562. echo Will not overwrite tinytcap.c
  1563. else
  1564. echo Extracting tinytcap.c
  1565. sed 's/^X//' >tinytcap.c <<\eof
  1566. X/* tinytcap.c */
  1567. X
  1568. X/* This file contains functions which simulate the termcap functions, but which
  1569. X * can only describe the capabilities of the ANSI.SYS and NANSI.SYS drivers on
  1570. X * an MS-DOS system or the VT-52 emulator of an Atari-ST.  These functions
  1571. X * do *NOT* access a "termcap" database file.
  1572. X */
  1573. X
  1574. X#include "config.h"
  1575. X#if MSDOS || TOS || MINIX || COHERENT
  1576. X
  1577. X#define CAP(str) CAP2((str)[0], (str)[1])
  1578. X#define CAP2(a,b) (((a) << 8) + ((b) & 0xff))
  1579. X
  1580. X#if MSDOS
  1581. X# define VAL2(v,a)    (a)
  1582. X# define VAL3(v,a,n)    (nansi ? (n) : (a))
  1583. Xstatic int    nansi;
  1584. X#endif
  1585. X
  1586. X#if TOS
  1587. X# define VAL2(v,a)    (v)
  1588. X# define VAL3(v,a,n)    (v)
  1589. X#endif
  1590. X
  1591. X#if MINIX || COHERENT
  1592. X# define VAL2(v,a)    (a)
  1593. X# define VAL3(v,a,n)    (n)
  1594. X#endif
  1595. X
  1596. X
  1597. X/*ARGSUSED*/
  1598. Xint tgetent(bp, name)
  1599. X    char    *bp;    /* buffer for storing the entry -- ignored */
  1600. X    char    *name;    /* name of the entry */
  1601. X{
  1602. X#if MSDOS
  1603. X    nansi = strcmp(name, "ansi");
  1604. X#endif
  1605. X    return 1;
  1606. X}
  1607. X
  1608. Xint tgetnum(id)
  1609. X    char    *id;
  1610. X{
  1611. X    switch (CAP(id))
  1612. X    {
  1613. X      case CAP2('l','i'):    return 25;
  1614. X      case CAP2('c','o'):    return 80;
  1615. X      case CAP2('s','g'):    return 0;
  1616. X      case CAP2('u','g'):    return 0;
  1617. X      default:        return -1;
  1618. X    }
  1619. X}
  1620. X
  1621. Xint tgetflag(id)
  1622. X    char    *id;
  1623. X{
  1624. X    switch (CAP(id))
  1625. X    {
  1626. X#if !MINIX || COHERENT
  1627. X      case CAP2('a','m'):    return 1;
  1628. X#endif
  1629. X      case CAP2('b','s'):    return 1;
  1630. X      case CAP2('m','i'):    return 1;
  1631. X      default:        return 0;
  1632. X    }
  1633. X}
  1634. X
  1635. X/*ARGSUSED*/
  1636. Xchar *tgetstr(id, bp)
  1637. X    char    *id;
  1638. X    char    **bp;    /* pointer to pointer to buffer - ignored */
  1639. X{
  1640. X    switch (CAP(id))
  1641. X    {
  1642. X      case CAP2('c','e'):    return VAL2("\033K", "\033[K");
  1643. X      case CAP2('c','l'):    return VAL2("\033E", "\033[2J");
  1644. X
  1645. X      case CAP2('a','l'):    return VAL3("\033L", (char *)0, "\033[L");
  1646. X      case CAP2('d','l'):    return VAL3("\033M", (char *)0, "\033[M");
  1647. X
  1648. X      case CAP2('c','m'):    return VAL2("\033Y%i%+ %+ ", "\033[%i%d;%dH");
  1649. X      case CAP2('d','o'):    return VAL2("\033B", "\033[B");
  1650. X      case CAP2('n','d'):    return VAL2("\033C", "\033[C");
  1651. X      case CAP2('u','p'):    return VAL2("\033A", "\033[A");
  1652. X      case CAP2('t','i'):    return VAL2("\033e", "");
  1653. X      case CAP2('t','e'):    return VAL2("", "");
  1654. X
  1655. X      case CAP2('s','o'):    return VAL2("\033p", "\033[7m");
  1656. X      case CAP2('s','e'):    return VAL2("\033q", "\033[m");
  1657. X      case CAP2('u','s'):    return VAL2((char *)0, "\033[4m");
  1658. X      case CAP2('u','e'):    return VAL2((char *)0, "\033[m");
  1659. X      case CAP2('m','d'):    return VAL2((char *)0, "\033[1m");
  1660. X      case CAP2('m','e'):    return VAL2((char *)0, "\033[m");
  1661. X
  1662. X#if MINIX || COHERENT
  1663. X      case CAP2('k','u'):    return "\033[A";
  1664. X      case CAP2('k','d'):    return "\033[B";
  1665. X      case CAP2('k','l'):    return "\033[D";
  1666. X      case CAP2('k','r'):    return "\033[C";
  1667. X      case CAP2('k','P'):    return "\033[V";
  1668. X      case CAP2('k','N'):    return "\033[U";
  1669. X      case CAP2('k','h'):    return "\033[H";
  1670. X# if MINIX
  1671. X      case CAP2('k','H'):    return "\033[Y";
  1672. X# else /* COHERENT */
  1673. X      case CAP2('k','H'):    return "\033[24H";
  1674. X# endif
  1675. X#else /* MS-DOS or TOS */
  1676. X      case CAP2('k','u'):    return "#H";
  1677. X      case CAP2('k','d'):    return "#P";
  1678. X      case CAP2('k','l'):    return "#K";
  1679. X      case CAP2('k','r'):    return "#M";
  1680. X      case CAP2('k','h'):    return "#G";
  1681. X      case CAP2('k','H'):    return "#O";
  1682. X      case CAP2('k','P'):    return "#I";
  1683. X      case CAP2('k','N'):    return "#Q";
  1684. X#endif
  1685. X
  1686. X      default:        return (char *)0;
  1687. X    }
  1688. X}
  1689. X
  1690. X/*ARGSUSED*/
  1691. Xchar *tgoto(cm, destcol, destrow)
  1692. X    char    *cm;    /* cursor movement string -- ignored */
  1693. X    int    destcol;/* destination column, 0 - 79 */
  1694. X    int    destrow;/* destination row, 0 - 24 */
  1695. X{
  1696. X    static char buf[30];
  1697. X
  1698. X#if MSDOS || MINIX || COHERENT
  1699. X    sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1);
  1700. X#endif
  1701. X#if TOS
  1702. X    sprintf(buf, "\033Y%c%c", ' ' + destrow, ' ' + destcol);
  1703. X#endif
  1704. X    return buf;
  1705. X}
  1706. X
  1707. X/*ARGSUSED*/
  1708. Xvoid tputs(cp, affcnt, outfn)
  1709. X    char    *cp;        /* the string to output */
  1710. X    int    affcnt;        /* number of affected lines -- ignored */
  1711. X    int    (*outfn)();    /* the output function */
  1712. X{
  1713. X    while (*cp)
  1714. X    {
  1715. X        (*outfn)(*cp);
  1716. X        cp++;
  1717. X    }
  1718. X}
  1719. X#endif
  1720. eof
  1721. if test `wc -c <tinytcap.c` -ne 3767
  1722. then
  1723. echo tinytcap.c damaged!
  1724. fi
  1725. fi
  1726.  
  1727. if test -f tio.c -a "$1" != -f
  1728. then
  1729. echo Will not overwrite tio.c
  1730. else
  1731. echo Extracting tio.c
  1732. sed 's/^X//' >tio.c <<\eof
  1733. X/* tio.c */
  1734. X
  1735. X/* Author:
  1736. X *    Steve Kirkendall
  1737. X *    14407 SW Teal Blvd. #C
  1738. X *    Beaverton, OR 97005
  1739. X *    kirkenda@cs.pdx.edu
  1740. X */
  1741. X
  1742. X
  1743. X/* This file contains terminal I/O functions */
  1744. X
  1745. X#include "config.h"
  1746. X#if BSD || COHERENT
  1747. X# include <setjmp.h>
  1748. X#endif
  1749. X#include <signal.h>
  1750. X#include "vi.h"
  1751. X
  1752. X
  1753. X/* This function reads in a line from the terminal. */
  1754. Xint vgets(prompt, buf, bsize)
  1755. X    char    prompt;    /* the prompt character, or '\0' for none */
  1756. X    char    *buf;    /* buffer into which the string is read */
  1757. X    int    bsize;    /* size of the buffer */
  1758. X{
  1759. X    int    len;    /* how much we've read so far */
  1760. X    int    ch;    /* a character from the user */
  1761. X    int    quoted;    /* is the next char quoted? */
  1762. X    int    tab;    /* column position of cursor */
  1763. X    char    widths[132];    /* widths of characters */
  1764. X#ifndef NO_DIGRAPH
  1765. X    int    erased;    /* 0, or first char of a digraph */
  1766. X#endif
  1767. X
  1768. X    /* show the prompt */
  1769. X    move(LINES - 1, 0);
  1770. X    tab = 0;
  1771. X    if (prompt)
  1772. X    {
  1773. X        addch(prompt);
  1774. X        tab = 1;
  1775. X    }
  1776. X    clrtoeol();
  1777. X    refresh();
  1778. X
  1779. X    /* read in the line */
  1780. X#ifndef NO_DIGRAPH
  1781. X    erased =
  1782. X#endif
  1783. X    quoted = len = 0;
  1784. X    for (;;)
  1785. X    {
  1786. X        ch = getkey(quoted ? 0 : WHEN_EX);
  1787. X
  1788. X        /* some special conversions */
  1789. X        if (ch == ctrl('D') && len == 0)
  1790. X            ch = ctrl('[');
  1791. X#ifndef NO_DIGRAPH
  1792. X        if (*o_digraph && erased != 0 && ch != '\b')
  1793. X        {
  1794. X            ch = digraph(erased, ch);
  1795. X            erased = 0;
  1796. X        }
  1797. X#endif
  1798. X
  1799. X        /* inhibit detection of special chars (except ^J) after a ^V */
  1800. X        if (quoted && ch != '\n')
  1801. X        {
  1802. X            ch |= 256;
  1803. X        }
  1804. X
  1805. X        /* process the character */
  1806. X        switch(ch)
  1807. X        {
  1808. X          case ctrl('V'):
  1809. X            qaddch('^');
  1810. X            qaddch('\b');
  1811. X            quoted = TRUE;
  1812. X            break;
  1813. X
  1814. X          case ctrl('['):
  1815. X            return -1;
  1816. X
  1817. X          case '\n':
  1818. X#if OSK
  1819. X          case '\l':
  1820. X#else
  1821. X          case '\r':
  1822. X#endif
  1823. X            clrtoeol();
  1824. X            goto BreakBreak;
  1825. X
  1826. X          case '\b':
  1827. X            if (len > 0)
  1828. X            {
  1829. X                len--;
  1830. X#ifndef NO_DIGRAPH
  1831. X                erased = buf[len];
  1832. X#endif
  1833. X                for (ch = widths[len]; ch > 0; ch--)
  1834. X                    addch('\b');
  1835. X                if (mode == MODE_EX)
  1836. X                {
  1837. X                    clrtoeol();
  1838. X                }
  1839. X                tab -= widths[len];
  1840. X            }
  1841. X            else
  1842. X            {
  1843. X                return -1;
  1844. X            }
  1845. X            break;
  1846. X
  1847. X          default:
  1848. X            /* strip off quotation bit */
  1849. X            if (ch & 256)
  1850. X            {
  1851. X                ch &= ~256;
  1852. X                quoted = FALSE;
  1853. X                qaddch(' ');
  1854. X                qaddch('\b');
  1855. X            }
  1856. X            /* add & echo the char */
  1857. X            if (len < bsize - 1)
  1858. X            {
  1859. X                if (ch == '\t')
  1860. X                {
  1861. X                    widths[len] = *o_tabstop - (tab % *o_tabstop);
  1862. X                    addstr("        " + 8 - widths[len]);
  1863. X                    tab += widths[len];
  1864. X                }
  1865. X                else if (ch > 0 && ch < ' ') /* > 0 by GB */
  1866. X                {
  1867. X                    addch('^');
  1868. X                    addch(ch + '@');
  1869. X                    widths[len] = 2;
  1870. X                    tab += 2;
  1871. X                }
  1872. X                else if (ch == '\177')
  1873. X                {
  1874. X                    addch('^');
  1875. X                    addch('?');
  1876. X                    widths[len] = 2;
  1877. X                    tab += 2;
  1878. X                }
  1879. X                else
  1880. X                {
  1881. X                    addch(ch);
  1882. X                    widths[len] = 1;
  1883. X                    tab++;
  1884. X                }
  1885. X                buf[len++] = ch;
  1886. X            }
  1887. X            else
  1888. X            {
  1889. X                beep();
  1890. X            }
  1891. X        }
  1892. X    }
  1893. XBreakBreak:
  1894. X    refresh();
  1895. X    buf[len] = '\0';
  1896. X    return len;
  1897. X}
  1898. X
  1899. X
  1900. X/* ring the terminal's bell */
  1901. Xvoid beep()
  1902. X{
  1903. X    if (*o_vbell)
  1904. X    {
  1905. X        do_VB();
  1906. X        refresh();
  1907. X    }
  1908. X    else if (*o_errorbells)
  1909. X    {
  1910. X        ttywrite("\007", 1);
  1911. X    }
  1912. X}
  1913. X
  1914. Xstatic int    manymsgs; /* This variable keeps msgs from overwriting each other */
  1915. Xstatic char    pmsg[80]; /* previous message (waiting to be displayed) */
  1916. X
  1917. X
  1918. Xstatic int showmsg()
  1919. X{
  1920. X    /* if there is no message to show, then don't */
  1921. X    if (!manymsgs)
  1922. X        return FALSE;
  1923. X
  1924. X    /* display the message */
  1925. X    move(LINES - 1, 0);
  1926. X    if (*pmsg)
  1927. X    {
  1928. X        standout();
  1929. X        qaddch(' ');
  1930. X        qaddstr(pmsg);
  1931. X        qaddch(' ');
  1932. X        standend();
  1933. X    }
  1934. X    clrtoeol();
  1935. X
  1936. X    manymsgs = FALSE;
  1937. X    return TRUE;
  1938. X}
  1939. X
  1940. X
  1941. Xvoid endmsgs()
  1942. X{
  1943. X    if (manymsgs)
  1944. X    {
  1945. X        showmsg();
  1946. X        addch('\n');
  1947. X    }
  1948. X}
  1949. X
  1950. X/* Write a message in an appropriate way.  This should really be a varargs
  1951. X * function, but there is no such thing as vwprintw.  Hack!!!
  1952. X *
  1953. X * In MODE_EX or MODE_COLON, the message is written immediately, with a
  1954. X * newline at the end.
  1955. X *
  1956. X * In MODE_VI, the message is stored in a character buffer.  It is not
  1957. X * displayed until getkey() is called.  msg() will call getkey() itself,
  1958. X * if necessary, to prevent messages from being lost.
  1959. X *
  1960. X * msg("")        - clears the message line
  1961. X * msg("%s %d", ...)    - does a printf onto the message line
  1962. X */
  1963. X/*VARARGS1*/
  1964. Xvoid msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
  1965. X    char    *fmt;
  1966. X    long    arg1, arg2, arg3, arg4, arg5, arg6, arg7;
  1967. X{
  1968. X    if (mode != MODE_VI)
  1969. X    {
  1970. X        sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
  1971. X        qaddstr(pmsg);
  1972. X        addch('\n');
  1973. X        exrefresh();
  1974. X    }
  1975. X    else
  1976. X    {
  1977. X        /* wait for keypress between consecutive msgs */
  1978. X        if (manymsgs)
  1979. X        {
  1980. X            getkey(WHEN_MSG);
  1981. X        }
  1982. X
  1983. X        /* real message */
  1984. X        sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
  1985. X        manymsgs = TRUE;
  1986. X    }
  1987. X}
  1988. X
  1989. X
  1990. X/* This function calls refresh() if the option exrefresh is set */
  1991. Xvoid exrefresh()
  1992. X{
  1993. X    char    *scan;
  1994. X
  1995. X    /* If this ex command wrote ANYTHING set exwrote so vi's  :  command
  1996. X     * can tell that it must wait for a user keystroke before redrawing.
  1997. X     */
  1998. X    for (scan=kbuf; scan<stdscr; scan++)
  1999. X        if (*scan == '\n')
  2000. X            exwrote = TRUE;
  2001. X
  2002. X#if MICROSOFT            /* avoid compiler bug */
  2003. X    scan = stdscr;
  2004. X#define    stdscr    scan
  2005. X#endif
  2006. X    /* now we do the refresh thing */
  2007. X    if (*o_exrefresh)
  2008. X    {
  2009. X        refresh();
  2010. X    }
  2011. X    else
  2012. X    {
  2013. X        wqrefresh(stdscr);
  2014. X    }
  2015. X#if MICROSOFT
  2016. X#undef    stdscr
  2017. X    stdscr = scan;
  2018. X#endif
  2019. X    if (mode != MODE_VI)
  2020. X    {
  2021. X        manymsgs = FALSE;
  2022. X    }
  2023. X}
  2024. X
  2025. X
  2026. X
  2027. X/* These are used for typeahead, and also for fudging the visual @ command */
  2028. Xstatic char    keybuf[100];    /* array of already-read keys */
  2029. Xstatic int    nkeys;        /* total number of keys in keybuf */
  2030. Xstatic int    next;        /* index of next key to return */
  2031. X#ifndef NO_AT
  2032. Xstatic int    atnext;        /* index of next key for "@", or 0 normally */
  2033. Xint fromcutbuf(cbname)
  2034. X    int    cbname;
  2035. X{
  2036. X    int    len;
  2037. X
  2038. X    /* fail if we're already doing an @ macro */
  2039. X    if (atnext > 0 && keybuf[atnext])
  2040. X    {
  2041. X        msg("Can't nest @ commands");
  2042. X        return FALSE;
  2043. X    }
  2044. X
  2045. X    /* use the empty portion of keybuf[] to get chars from the cut buffer */
  2046. X    len = cb2str(cbname, keybuf + nkeys, sizeof keybuf - nkeys);
  2047. X    if (len < 0)
  2048. X    {
  2049. X        msg("Invalid cut buffer name.  Must be a-z");
  2050. X        return FALSE;
  2051. X    }
  2052. X    if (len == 0)
  2053. X    {
  2054. X        msg("Cut buffer \"%c is empty", cbname);
  2055. X        return FALSE;
  2056. X    }
  2057. X    else if (len >= sizeof keybuf - nkeys)
  2058. X    {
  2059. X        msg("Cut buffer \"%c is too large to execute", cbname);
  2060. X        return FALSE;
  2061. X    }
  2062. X
  2063. X    /* prepare to "read" those keys on subsequent getkey() calls */
  2064. X    atnext = nkeys;
  2065. X    return TRUE;
  2066. X}
  2067. X#endif
  2068. X
  2069. X/* This array describes mapped key sequences */
  2070. Xstatic struct _keymap
  2071. X{
  2072. X    char    *name;        /* name of the key, or NULL */
  2073. X    char    rawin[LONGKEY];    /* the unmapped version of input */
  2074. X    char    cooked[80];    /* the mapped version of input */
  2075. X    int    len;        /* length of the unmapped version */
  2076. X    int    when;        /* when is this key mapped? */
  2077. X}
  2078. X    mapped[MAXMAPS];
  2079. X
  2080. X#if !MSDOS && !TOS
  2081. X# if BSD || COHERENT
  2082. Xstatic jmp_buf env_timeout;
  2083. Xstatic int dummy()
  2084. X{
  2085. X    longjmp(env_timeout, 1);
  2086. X    return 0;
  2087. X}
  2088. X# else 
  2089. Xstatic int dummy()
  2090. X{
  2091. X}
  2092. X# endif
  2093. X#endif
  2094. X
  2095. X/* This function reads in a keystroke for VI mode.  It automatically handles
  2096. X * key mapping.
  2097. X */
  2098. Xint getkey(when)
  2099. X    int        when;        /* which bits must be ON? */
  2100. X{
  2101. X    static char    *cooked;    /* rawin, or pointer to converted key */ 
  2102. X    static int    oldwhen;    /* "when" from last time */
  2103. X    static int    oldleft;
  2104. X    static long    oldtop;
  2105. X    static long    oldnlines;
  2106. X    static char    *cshape;    /* current cursor shape */
  2107. X    REG char    *kptr;        /* &keybuf[next] */
  2108. X    REG struct _keymap *km;    /* used to count through keymap */
  2109. X    REG int        i, j, k;
  2110. X
  2111. X#ifdef DEBUG
  2112. X    watch();
  2113. X#endif
  2114. X
  2115. X    /* if this key is needed for delay between multiple error messages,
  2116. X     * then reset the manymsgs flag and abort any mapped key sequence.
  2117. X     */
  2118. X    if (showmsg())
  2119. X    {
  2120. X        if (when == WHEN_MSG)
  2121. X        {
  2122. X            qaddstr("[More...]");
  2123. X            refresh();
  2124. X            cooked = (char *)0;
  2125. X        }
  2126. X        else if (when == WHEN_VIINP || when == WHEN_VIREP)
  2127. X        {
  2128. X            redraw(cursor, TRUE);
  2129. X        }
  2130. X    }
  2131. X
  2132. X#ifndef NO_AT
  2133. X    /* if we're in the middle of a visual @ macro, take atnext */
  2134. X    if (atnext > 0)
  2135. X    {
  2136. X        if (keybuf[atnext])
  2137. X        {
  2138. X            return keybuf[atnext++];
  2139. X        }
  2140. X        atnext = 0;
  2141. X    }
  2142. X#endif
  2143. X
  2144. X    /* if we're doing a mapped key, get the next char */
  2145. X    if (cooked && *cooked)
  2146. X    {
  2147. X        return *cooked++;
  2148. X    }
  2149. X
  2150. X    /* if keybuf is empty, fill it */
  2151. X    if (next == nkeys)
  2152. X    {
  2153. X#ifndef NO_CURSORSHAPE
  2154. X        /* make sure the cursor is the right shape */
  2155. X        if (has_CQ)
  2156. X        {
  2157. X            cooked = cshape;
  2158. X            switch (when)
  2159. X            {
  2160. X              case WHEN_EX:        cooked = CX;    break;
  2161. X              case WHEN_VICMD:    cooked = CV;    break;
  2162. X              case WHEN_VIINP:    cooked = CI;    break;
  2163. X              case WHEN_VIREP:    cooked = CR;    break;
  2164. X            }
  2165. X            if (cooked != cshape)
  2166. X            {
  2167. X                cshape = cooked;
  2168. X                switch (when)
  2169. X                {
  2170. X                  case WHEN_EX:        do_CX();    break;
  2171. X                  case WHEN_VICMD:    do_CV();    break;
  2172. X                  case WHEN_VIINP:    do_CI();    break;
  2173. X                  case WHEN_VIREP:    do_CR();    break;
  2174. X                }
  2175. X            }
  2176. X            cooked = (char *)0;
  2177. X        }
  2178. X#endif
  2179. X
  2180. X#ifndef NO_SHOWMODE
  2181. X        /* if "showmode" then say which mode we're in */
  2182. X        if (*o_smd
  2183. X         && mode == MODE_VI
  2184. X         && (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines))
  2185. X        {
  2186. X            oldwhen = when;
  2187. X            oldtop = topline;
  2188. X            oldleft = leftcol;
  2189. X            oldnlines = nlines;
  2190. X
  2191. X            if (when & WHEN_VICMD)
  2192. X            {
  2193. X                redraw(cursor, FALSE);
  2194. X                move(LINES - 1, COLS - 10);
  2195. X                standout();
  2196. X                addstr("Command");
  2197. X                standend();
  2198. X                redraw(cursor, FALSE);
  2199. X            }
  2200. X            else if (when & WHEN_VIINP)
  2201. X            {
  2202. X                redraw(cursor, TRUE);
  2203. X                move(LINES - 1, COLS - 10);
  2204. X                standout();
  2205. X                addstr(" Input ");
  2206. X                standend();
  2207. X                redraw(cursor, TRUE);
  2208. X            }
  2209. X            else if (when & WHEN_VIREP)
  2210. X            {
  2211. X                redraw(cursor, TRUE);
  2212. X                move(LINES - 1, COLS - 10);
  2213. X                standout();
  2214. X                addstr("Replace");
  2215. X                standend();
  2216. X                redraw(cursor, TRUE);
  2217. X            }
  2218. X        }
  2219. X        else
  2220. X#endif
  2221. X
  2222. X        /* redraw if getting a VI command */
  2223. X        if (when & WHEN_VICMD)
  2224. X        {
  2225. X            redraw(cursor, FALSE);
  2226. X        }
  2227. X
  2228. X        /* read the rawin keystrokes */
  2229. X        refresh();
  2230. X        while ((nkeys = ttyread(keybuf, sizeof keybuf)) < 0)
  2231. X        {
  2232. X            /* terminal was probably resized */
  2233. X            *o_lines = LINES;
  2234. X            *o_columns = COLS;
  2235. X            if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
  2236. X            {
  2237. X                redraw(MARK_UNSET, FALSE);
  2238. X                redraw(cursor, (when & WHEN_VICMD) == 0);
  2239. X                refresh();
  2240. X            }
  2241. X        }
  2242. X        next = 0;
  2243. X
  2244. X        /* if nkeys == 0 then we've reached EOF of an ex script. */
  2245. X        if (nkeys == 0)
  2246. X        {
  2247. X            tmpabort(TRUE);
  2248. X            move(LINES - 1, 0);
  2249. X            clrtoeol();
  2250. X            refresh();
  2251. X            endwin();
  2252. X            exit(1);
  2253. X        }
  2254. X    }
  2255. X
  2256. X    /* see how many mapped keys this might be */
  2257. X    kptr = &keybuf[next];
  2258. X    for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
  2259. X    {
  2260. X        if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
  2261. X        {
  2262. X            if (km->len > nkeys - next)
  2263. X            {
  2264. X                if (!strncmp(km->rawin, kptr, nkeys - next))
  2265. X                {
  2266. X                    j++;
  2267. X                }
  2268. X            }
  2269. X            else
  2270. X            {
  2271. X                if (!strncmp(km->rawin, kptr, km->len))
  2272. X                {
  2273. X                    j++;
  2274. X                    k = i;
  2275. X                }
  2276. X            }
  2277. X        }
  2278. X    }
  2279. X
  2280. X    /* if more than one, try to read some more */
  2281. X    while (j > 1)
  2282. X    {
  2283. X#if BSD || COHERENT
  2284. X        if (setjmp(env_timeout))
  2285. X        {
  2286. X            /* we timed out - assume no mapping */
  2287. X            j = 0;
  2288. X            break;
  2289. X        }
  2290. X#endif
  2291. X#if ANY_UNIX
  2292. X        signal(SIGALRM, dummy);
  2293. X#endif
  2294. X#if OSK
  2295. X        signal(SIGQUIT, dummy);
  2296. X#endif
  2297. X        alarm((unsigned)*o_keytime);
  2298. X        i = nkeys;
  2299. X        if ((k = ttyread(keybuf + nkeys, sizeof keybuf - nkeys)) >= 0)
  2300. X        {
  2301. X            nkeys += k;
  2302. X        }
  2303. X        alarm(0);
  2304. X#if OSK
  2305. X# ifndef DEBUG
  2306. X        signal(SIGQUIT, SIG_IGN);
  2307. X# endif
  2308. X#endif
  2309. X
  2310. X        /* if we couldn't read any more, pretend 0 mapped keys */
  2311. X        if (i == nkeys)
  2312. X        {
  2313. X            j = 0;
  2314. X        }
  2315. X        else /* else we got some more - try again */
  2316. X        {
  2317. X            for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
  2318. X            {
  2319. X                if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
  2320. X                {
  2321. X                    if (km->len > nkeys - next)
  2322. X                    {
  2323. X                        if (!strncmp(km->rawin, kptr, nkeys - next))
  2324. X                        {
  2325. X                            j++;
  2326. X                        }
  2327. X                    }
  2328. X                    else
  2329. X                    {
  2330. X                        if (!strncmp(km->rawin, kptr, km->len))
  2331. X                        {
  2332. X                            j++;
  2333. X                            k = i;
  2334. X                        }
  2335. X                    }
  2336. X                }
  2337. X            }
  2338. X        }
  2339. X    }
  2340. X
  2341. X    /* if unambiguously mapped key, use it! */
  2342. X    if (j == 1 && k >= 0)
  2343. X    {
  2344. X        next += mapped[k].len;
  2345. X        cooked = mapped[k].cooked;
  2346. X#ifndef NO_EXTENSIONS
  2347. X        if ((when & (WHEN_VIINP|WHEN_VIREP))
  2348. X         && (mapped[k].when & WHEN_INMV))
  2349. X        {
  2350. X            return 0; /* special case, means "a movement char follows" */
  2351. X        }
  2352. X        else
  2353. X#endif
  2354. X        {
  2355. X            return *cooked++;
  2356. X        }
  2357. X    }
  2358. X    else
  2359. X    /* assume key is unmapped, but still translate weird erase key to '\b' */
  2360. X    if (keybuf[next] == ERASEKEY && when != 0)
  2361. X    {
  2362. X        next++;
  2363. X        return '\b';
  2364. X    }
  2365. X    else if (keybuf[next] == '\0')
  2366. X    {
  2367. X        next++;
  2368. X        return ('A' & 0x1f);
  2369. X    }
  2370. X    else
  2371. X    {
  2372. X        return keybuf[next++];
  2373. X    }
  2374. X}
  2375. X
  2376. X
  2377. X/* This function maps or unmaps a key */
  2378. Xvoid mapkey(rawin, cooked, when, name)
  2379. X    char    *rawin;    /* the input key sequence, before mapping */
  2380. X    char    *cooked;/* after mapping */
  2381. X    short    when;    /* bitmap of when mapping should happen */
  2382. X    char    *name;    /* name of the key, if any */
  2383. X{
  2384. X    int    i, j;
  2385. X
  2386. X#ifndef NO_EXTENSIONS
  2387. X    /* if the mapped version starts with the word "visual" then set WHEN_INMV */
  2388. X    if (!strncmp(cooked, "visual ", 7))
  2389. X    {
  2390. X        when |= WHEN_INMV;
  2391. X        cooked += 7;
  2392. X    }
  2393. X    /* if WHEN_INMV is set, then WHEN_VIINP and WHEN_VIREP must be set */
  2394. X    if (when & WHEN_INMV)
  2395. X    {
  2396. X        when |= (WHEN_VIINP | WHEN_VIREP);
  2397. X    }
  2398. X#endif
  2399. X
  2400. X    /* see if the key sequence was mapped before */
  2401. X    j = strlen(rawin);
  2402. X    for (i = 0; i < MAXMAPS; i++)
  2403. X    {
  2404. X        if (mapped[i].len == j
  2405. X         && !strncmp(mapped[i].rawin, rawin, j)
  2406. X         && (mapped[i].when & when))
  2407. X        {
  2408. X            break;
  2409. X        }
  2410. X    }
  2411. X
  2412. X    /* if not already mapped, then try to find a new slot to use */
  2413. X    if (i == MAXMAPS)
  2414. X    {
  2415. X        for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
  2416. X        {
  2417. X        }
  2418. X    }
  2419. X
  2420. X    /* no room for the new key? */
  2421. X    if (i == MAXMAPS)
  2422. X    {
  2423. X        msg("No room left in the key map table");
  2424. X        return;
  2425. X    }
  2426. X
  2427. X    /* map the key */
  2428. X    if (cooked && *cooked)
  2429. X    {
  2430. X        /* Map the key */
  2431. X        mapped[i].len = j;
  2432. X        strncpy(mapped[i].rawin, rawin, j);
  2433. X        strcpy(mapped[i].cooked, cooked);
  2434. X        mapped[i].when = when;
  2435. X        mapped[i].name = name;
  2436. X    }
  2437. X    else /* unmap the key */
  2438. X    {
  2439. X        mapped[i].len = 0;
  2440. X    }
  2441. X}
  2442. X
  2443. X/* Dump keys of a given type - WHEN_VICMD dumps the ":map" keys, and
  2444. X * WHEN_VIINP|WHEN_VIREP dumps the ":map!" keys
  2445. X */
  2446. Xvoid dumpkey(when)
  2447. X    int    when;    /* WHEN_XXXX of mappings to be dumped */
  2448. X{
  2449. X    int    i, len, mlen;
  2450. X    char    *scan;
  2451. X    char    *mraw;
  2452. X
  2453. X    for (i = 0; i < MAXMAPS; i++)
  2454. X    {
  2455. X        /* skip unused entries, or entries that don't match "when" */
  2456. X        if (mapped[i].len <= 0 || !(mapped[i].when & when))
  2457. X        {
  2458. X            continue;
  2459. X        }
  2460. X
  2461. X        /* dump the key label, if any */
  2462. X        len = 8;
  2463. X        if (mapped[i].name)
  2464. X        {
  2465. X            qaddstr(mapped[i].name);
  2466. X            len -= strlen(mapped[i].name);
  2467. X        }
  2468. X        do
  2469. X        {
  2470. X            qaddch(' ');
  2471. X        } while (len-- > 0);
  2472. X
  2473. X        /* dump the raw version */
  2474. X        len = 0;
  2475. X        mlen = mapped[i].len;
  2476. X        mraw = mapped[i].rawin;
  2477. X        for (scan = mraw; scan < mraw + mlen; scan++)
  2478. X        {
  2479. X            if (UCHAR(*scan) < ' ' || *scan == '\177')
  2480. X            {
  2481. X                qaddch('^');
  2482. X                qaddch(*scan ^ '@');
  2483. X                len += 2;
  2484. X            }
  2485. X            else
  2486. X            {
  2487. X                qaddch(*scan);
  2488. X                len++;
  2489. X            }
  2490. X        }
  2491. X        do
  2492. X        {
  2493. X            qaddch(' ');
  2494. X        } while (++len < 8);
  2495. X
  2496. X        /* dump the mapped version */
  2497. X#ifndef NO_EXTENSIONS
  2498. X        if ((mapped[i].when & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
  2499. X        {
  2500. X            qaddstr("visual ");
  2501. X        }
  2502. X#endif
  2503. X        for (scan = mapped[i].cooked; *scan; scan++)
  2504. X        {
  2505. X            if (UCHAR(*scan) < ' ' || *scan == '\177')
  2506. X            {
  2507. X                qaddch('^');
  2508. X                qaddch(*scan ^ '@');
  2509. X            }
  2510. X            else
  2511. X            {
  2512. X                qaddch(*scan);
  2513. X            }
  2514. X        }
  2515. X
  2516. X        addch('\n');
  2517. X        exrefresh();
  2518. X    }
  2519. X}
  2520. X
  2521. X
  2522. X
  2523. X#ifndef MKEXRC
  2524. X/* This function saves the current configuration of mapped keys to a file */
  2525. Xvoid savekeys(fd)
  2526. X    int    fd;    /* file descriptor to save them to */
  2527. X{
  2528. X    int    i;
  2529. X    char    buf[80];
  2530. X
  2531. X    /* now write a map command for each key other than the arrows */
  2532. X    for (i = 0; i < MAXMAPS; i++)
  2533. X    {
  2534. X        /* ignore keys that came from termcap */
  2535. X        if (mapped[i].name)
  2536. X        {
  2537. X            continue;
  2538. X        }
  2539. X
  2540. X        /* If this isn't used, ignore it */
  2541. X        if (mapped[i].len <= 0)
  2542. X        {
  2543. X            continue;
  2544. X        }
  2545. X
  2546. X        /* write the map command */
  2547. X#ifndef NO_EXTENSIONS
  2548. X        if (mapped[i].when & WHEN_INMV)
  2549. X        {
  2550. X#if OSK
  2551. X            char    fmt[80];
  2552. X            sprintf(fmt, "map%%s %%.%ds %%s\n", mapped[i].len);
  2553. X            sprintf(buf, fmt,
  2554. X                (mapped[i].when & WHEN_VICMD) ? "" : "!",
  2555. X#else
  2556. X            sprintf(buf, "map%s %.*s visual %s\n",
  2557. X                (mapped[i].when & WHEN_VICMD) ? "" : "!",
  2558. X                mapped[i].len,
  2559. X#endif
  2560. X                mapped[i].rawin,
  2561. X                mapped[i].cooked);
  2562. X            twrite(fd, buf, strlen(buf));
  2563. X        }
  2564. X        else
  2565. X#endif
  2566. X        {
  2567. X            if (mapped[i].when & WHEN_VICMD)
  2568. X            {
  2569. X#if OSK
  2570. X                char    fmt[80];
  2571. X                sprintf(fmt, "map %%.%ds %%s\n", mapped[i].len);
  2572. X                sprintf(buf, fmt,
  2573. X#else
  2574. X                sprintf(buf, "map %.*s %s\n", mapped[i].len,
  2575. X#endif
  2576. X                    mapped[i].rawin,
  2577. X                    mapped[i].cooked);
  2578. X                twrite(fd, buf, strlen(buf));
  2579. X            }
  2580. X            if (mapped[i].when & (WHEN_VIINP | WHEN_VIREP))
  2581. X            {
  2582. X#if OSK
  2583. X                char    fmt[80];
  2584. X                sprintf(fmt, "map! %%.%ds %%s\n", mapped[i].len);
  2585. X                sprintf(buf, fmt,
  2586. X#else
  2587. X                sprintf(buf, "map! %.*s %s\n", mapped[i].len,
  2588. X#endif
  2589. X                    mapped[i].rawin,
  2590. X                    mapped[i].cooked);
  2591. X                twrite(fd, buf, strlen(buf));
  2592. X            }
  2593. X        }
  2594. X    }
  2595. X}
  2596. X#endif
  2597. eof
  2598. if test `wc -c <tio.c` -ne 16181
  2599. then
  2600. echo tio.c damaged!
  2601. fi
  2602. fi
  2603.  
  2604. if test -f tmp.c -a "$1" != -f
  2605. then
  2606. echo Will not overwrite tmp.c
  2607. else
  2608. echo Extracting tmp.c
  2609. sed 's/^X//' >tmp.c <<\eof
  2610. X/* tmpfile.c */
  2611. X
  2612. X/* Author:
  2613. X *    Steve Kirkendall
  2614. X *    14407 SW Teal Blvd. #C
  2615. X *    Beaverton, OR 97005
  2616. X *    kirkenda@cs.pdx.edu
  2617. X */
  2618. X
  2619. X
  2620. X/* This file contains functions which create & readback a TMPFILE */
  2621. X
  2622. X
  2623. X#include "config.h"
  2624. X#include <ctype.h>
  2625. X#include "vi.h"
  2626. X#if TOS
  2627. X# include <stat.h>
  2628. X#else
  2629. X# if OSK
  2630. X#  include "osk.h"
  2631. X# else
  2632. X#  include <sys/stat.h>
  2633. X# endif
  2634. X#endif
  2635. X
  2636. X
  2637. X#ifndef NO_MODELINE
  2638. Xstatic void do_modeline(l, stop)
  2639. X    long    l;    /* line number to start at */
  2640. X    long    stop;    /* line number to stop at */
  2641. X{
  2642. X    char    *str;    /* used to scan through the line */
  2643. X    char    *start;    /* points to the start of the line */
  2644. X    char    buf[80];
  2645. X
  2646. X    /* if modelines are disabled, then do nothing */
  2647. X    if (!*o_modeline)
  2648. X    {
  2649. X        return;
  2650. X    }
  2651. X
  2652. X    /* for each line... */
  2653. X    for (l = 1; l <= stop; l++)
  2654. X    {
  2655. X        /* for each position in the line.. */
  2656. X        for (str = fetchline(l); *str; str++)
  2657. X        {
  2658. X            /* if it is the start of a modeline command... */
  2659. X            if ((str[0] == 'e' && str[1] == 'x'
  2660. X              || str[0] == 'v' && str[1] == 'i')
  2661. X              && str[2] == ':')
  2662. X            {
  2663. X                start = str += 3;
  2664. X
  2665. X                /* find the end */
  2666. X                while (*str && *str != ':')
  2667. X                {
  2668. X                    str++;
  2669. X                }
  2670. X
  2671. X                /* if it is a well-formed modeline, execute it */
  2672. X                if (*str && str - start < sizeof buf)
  2673. X                {
  2674. X                    strncpy(buf, start, (int)(str - start));
  2675. X                    buf[str - start] = '\0';
  2676. X                    doexcmd(buf);
  2677. X                    break;
  2678. X                }
  2679. X            }
  2680. X        }
  2681. X    }
  2682. X}
  2683. X#endif
  2684. X
  2685. X
  2686. X/* The FAIL() macro prints an error message and then exits. */
  2687. X#define FAIL(why,arg)    mode = MODE_EX; msg(why, arg); endwin(); exit(9)
  2688. X
  2689. X/* This is the name of the temp file */
  2690. Xstatic char    tmpname[80];
  2691. X
  2692. X/* This function creates the temp file and copies the original file into it.
  2693. X * Returns if successful, or stops execution if it fails.
  2694. X */
  2695. Xint tmpstart(filename)
  2696. X    char        *filename; /* name of the original file */
  2697. X{
  2698. X    int        origfd;    /* fd used for reading the original file */
  2699. X    struct stat    statb;    /* stat buffer, used to examine inode */
  2700. X    REG BLK        *this;    /* pointer to the current block buffer */
  2701. X    REG BLK        *next;    /* pointer to the next block buffer */
  2702. X    int        inbuf;    /* number of characters in a buffer */
  2703. X    int        nread;    /* number of bytes read */
  2704. X    REG int        j, k;
  2705. X    int        i;
  2706. X    int        sum;    /* used for calculating a checksum for this */
  2707. X    char        *scan;
  2708. X    long        nbytes;
  2709. X
  2710. X    /* switching to a different file certainly counts as a change */
  2711. X    changes++;
  2712. X    redraw(MARK_UNSET, FALSE);
  2713. X
  2714. X    /* open the original file for reading */
  2715. X    *origname = '\0';
  2716. X    if (filename && *filename)
  2717. X    {
  2718. X        strcpy(origname, filename);
  2719. X        origfd = open(origname, O_RDONLY);
  2720. X        if (origfd < 0 && errno != ENOENT)
  2721. X        {
  2722. X            msg("Can't open \"%s\"", origname);
  2723. X            return tmpstart("");
  2724. X        }
  2725. X        if (origfd >= 0)
  2726. X        {
  2727. X            if (stat(origname, &statb) < 0)
  2728. X            {
  2729. X                FAIL("Can't stat \"%s\"", origname);
  2730. X            }
  2731. X#if TOS
  2732. X            if (origfd >= 0 && (statb.st_mode & S_IJDIR))
  2733. X#else
  2734. X# if OSK
  2735. X            if (origfd >= 0 && (statb.st_mode & S_IFDIR))
  2736. X# else
  2737. X            if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
  2738. X# endif
  2739. X#endif
  2740. X            {
  2741. X                msg("\"%s\" is not a regular file", origname);
  2742. X                return tmpstart("");
  2743. X            }
  2744. X        }
  2745. X        else
  2746. X        {
  2747. X            stat(".", &statb);
  2748. X        }
  2749. X        if (origfd >= 0)
  2750. X        {
  2751. X            origtime = statb.st_mtime;
  2752. X#if MSDOS || OSK
  2753. X            if (*o_readonly || !(statb.st_mode & S_IWRITE))
  2754. X#endif
  2755. X#if TOS
  2756. X            if (*o_readonly || (statb.st_mode & S_IJRON))
  2757. X#endif
  2758. X#if ANY_UNIX
  2759. X            if (*o_readonly || !(statb.st_mode &
  2760. X                  (statb.st_uid != geteuid() ? 0022 : 0200)))
  2761. X#endif
  2762. X            {
  2763. X                setflag(file, READONLY);
  2764. X            }
  2765. X        }
  2766. X        else
  2767. X        {
  2768. X            origtime = 0L;
  2769. X        }
  2770. X    }
  2771. X    else
  2772. X    {
  2773. X        setflag(file, NOFILE);
  2774. X        origfd = -1;
  2775. X        origtime = 0L;
  2776. X        stat(".", &statb);
  2777. X    }
  2778. X
  2779. X    /* generate a checksum from the file's name */
  2780. X    for (sum = 0, scan = origname + strlen(origname);
  2781. X         --scan >= origname && (isascii(*scan) && isalnum(*scan) || *scan == '.');
  2782. X         sum = sum + *scan)
  2783. X    {
  2784. X    }
  2785. X    sum &= 0xf;
  2786. X
  2787. X    /* make a name for the tmp file */
  2788. X#if MSDOS || TOS
  2789. X    /* MS-Dos doesn't allow multiple slashes, but supports drives
  2790. X     * with current directories.
  2791. X     * This relies on TMPNAME beginning with "%s\\"!!!!
  2792. X     */
  2793. X    strcpy(tmpname, o_directory);
  2794. X    if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
  2795. X        tmpname[i++]=SLASH;
  2796. X    sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev);
  2797. X#else
  2798. X    sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev);
  2799. X#endif
  2800. X
  2801. X    /* make sure nobody else is editing the same file */
  2802. X    if (access(tmpname, 0) == 0)
  2803. X    {
  2804. X        if (*origname)
  2805. X        {
  2806. X            msg("\"%s\" is busy", filename);
  2807. X            return tmpstart("");
  2808. X        }
  2809. X        FAIL("\"%s\" is busy", filename);
  2810. X    }
  2811. X
  2812. X    /* create the temp file */
  2813. X#if ANY_UNIX
  2814. X    close(creat(tmpname, 0600));        /* only we can read it */
  2815. X#else
  2816. X    close(creat(tmpname, FILEPERMS));    /* anybody body can read it, alas */
  2817. X#endif
  2818. X    tmpfd = open(tmpname, O_RDWR | O_BINARY);
  2819. X    if (tmpfd < 0)
  2820. X    {
  2821. X        FAIL("Can't create temporary file, errno=%d", errno);
  2822. X        return 1;
  2823. X    }
  2824. X
  2825. X    /* allocate space for the header in the file */
  2826. X    write(tmpfd, hdr.c, (unsigned)BLKSIZE);
  2827. X
  2828. X#ifndef NO_RECYCLE
  2829. X    /* initialize the block allocator */
  2830. X    /* This must already be done here, before the first attempt
  2831. X     * to write to the new file! GB */
  2832. X    garbage();
  2833. X#endif
  2834. X
  2835. X    /* initialize lnum[] */
  2836. X    for (i = 1; i < MAXBLKS; i++)
  2837. X    {
  2838. X        lnum[i] = INFINITY;
  2839. X    }
  2840. X    lnum[0] = 0;
  2841. X
  2842. X    /* if there is no original file, then create a 1-line file */
  2843. X    if (origfd < 0)
  2844. X    {
  2845. X        hdr.n[0] = 0;    /* invalid inode# denotes new file */
  2846. X
  2847. X        this = blkget(1);     /* get the new text block */
  2848. X        strcpy(this->c, "\n");    /* put a line in it */
  2849. X
  2850. X        lnum[1] = 1L;    /* block 1 ends with line 1 */
  2851. X        nlines = 1L;    /* there is 1 line in the file */
  2852. X        nbytes = 1L;
  2853. X
  2854. X        if (*origname)
  2855. X        {
  2856. X            msg("\"%s\" [NEW FILE]  1 line, 1 char", origname);
  2857. X        }
  2858. X        else
  2859. X        {
  2860. X            msg("\"[NO FILE]\"  1 line, 1 char");
  2861. X        }
  2862. X    }
  2863. X    else /* there is an original file -- read it in */
  2864. X    {
  2865. X        hdr.n[0] = statb.st_ino;
  2866. X        nbytes = nlines = 0;
  2867. X
  2868. X        /* preallocate 1 "next" buffer */
  2869. X        i = 1;
  2870. X        next = blkget(i);
  2871. X        inbuf = 0;
  2872. X
  2873. X        /* loop, moving blocks from orig to tmp */
  2874. X        for (;;)
  2875. X        {
  2876. X            /* "next" buffer becomes "this" buffer */
  2877. X            this = next;
  2878. X
  2879. X            /* read [more] text into this block */
  2880. X            nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
  2881. X            if (nread < 0)
  2882. X            {
  2883. X                close(origfd);
  2884. X                close(tmpfd);
  2885. X                tmpfd = -1;
  2886. X                unlink(tmpname);
  2887. X                FAIL("Error reading \"%s\"", origname);
  2888. X            }
  2889. X
  2890. X            /* convert NUL characters to something else */
  2891. X            for (k = inbuf; k < inbuf + nread; k++)
  2892. X            {
  2893. X                if (!this->c[k])
  2894. X                {
  2895. X                    setflag(file, HADNUL);
  2896. X                    this->c[k] = 0x80;
  2897. X                }
  2898. X            }
  2899. X            inbuf += nread;
  2900. X
  2901. X            /* if the buffer is empty, quit */
  2902. X            if (inbuf == 0)
  2903. X            {
  2904. X                goto FoundEOF;
  2905. X            }
  2906. X
  2907. X#if MSDOS || TOS
  2908. X/* BAH! MS text mode read fills inbuf, then compresses eliminating \r
  2909. X   but leaving garbage at end of buf. The same is true for TURBOC. GB. */
  2910. X
  2911. X            memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
  2912. X#endif
  2913. X
  2914. X            /* search backward for last newline */
  2915. X            for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
  2916. X            {
  2917. X            }
  2918. X            if (k++ < 0)
  2919. X            {
  2920. X                if (inbuf >= BLKSIZE - 1)
  2921. X                {
  2922. X                    k = 80;
  2923. X                }
  2924. X                else
  2925. X                {
  2926. X                    k = inbuf;
  2927. X                }
  2928. X            }
  2929. X
  2930. X            /* allocate next buffer */
  2931. X            next = blkget(++i);
  2932. X
  2933. X            /* move fragmentary last line to next buffer */
  2934. X            inbuf -= k;
  2935. X            for (j = 0; k < BLKSIZE; j++, k++)
  2936. X            {
  2937. X                next->c[j] = this->c[k];
  2938. X                this->c[k] = 0;
  2939. X            }
  2940. X
  2941. X            /* if necessary, add a newline to this buf */
  2942. X            for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
  2943. X            {
  2944. X            }
  2945. X            if (this->c[k] != '\n')
  2946. X            {
  2947. X                setflag(file, ADDEDNL);
  2948. X                this->c[k + 1] = '\n';
  2949. X            }
  2950. X
  2951. X            /* count the lines in this block */
  2952. X            for (k = 0; k < BLKSIZE && this->c[k]; k++)
  2953. X            {
  2954. X                if (this->c[k] == '\n')
  2955. X                {
  2956. X                    nlines++;
  2957. X                }
  2958. X                nbytes++;
  2959. X            }
  2960. X            lnum[i - 1] = nlines;
  2961. X        }
  2962. XFoundEOF:
  2963. X
  2964. X        /* if this is a zero-length file, add 1 line */
  2965. X        if (nlines == 0)
  2966. X        {
  2967. X            this = blkget(1);     /* get the new text block */
  2968. X            strcpy(this->c, "\n");    /* put a line in it */
  2969. X
  2970. X            lnum[1] = 1;    /* block 1 ends with line 1 */
  2971. X            nlines = 1;    /* there is 1 line in the file */
  2972. X            nbytes = 1;
  2973. X        }
  2974. X
  2975. X#if MSDOS || TOS
  2976. X        /* each line has an extra CR that we didn't count yet */
  2977. X        nbytes += nlines;
  2978. X#endif
  2979. X
  2980. X        /* report the number of lines in the file */
  2981. X        msg("\"%s\" %s %ld line%s, %ld char%s",
  2982. X            origname,
  2983. X            (tstflag(file, READONLY) ? "[READONLY]" : ""),
  2984. X            nlines,
  2985. X            nlines == 1 ? "" : "s",
  2986. X            nbytes,
  2987. X            nbytes == 1 ? "" : "s");
  2988. X    }
  2989. X
  2990. X    /* initialize the cursor to start of line 1 */
  2991. X    cursor = MARK_FIRST;
  2992. X
  2993. X    /* close the original file */
  2994. X    close(origfd);
  2995. X
  2996. X    /* any other messages? */
  2997. X    if (tstflag(file, HADNUL))
  2998. X    {
  2999. X        msg("This file contained NULs.  They've been changed to \\x80 chars");
  3000. X    }
  3001. X    if (tstflag(file, ADDEDNL))
  3002. X    {
  3003. X        msg("Newline characters have been inserted to break up long lines");
  3004. X    }
  3005. X
  3006. X#ifndef NO_MODELINE
  3007. X    if (nlines > 10)
  3008. X    {
  3009. X        do_modeline(1L, 5L);
  3010. X        do_modeline(nlines - 4L, nlines);
  3011. X    }
  3012. X    else
  3013. X    {
  3014. X        do_modeline(1L, nlines);
  3015. X    }
  3016. X#endif
  3017. X    return 0;
  3018. X}
  3019. X
  3020. X
  3021. X
  3022. X/* This function copies the temp file back onto an original file.
  3023. X * Returns TRUE if successful, or FALSE if the file could NOT be saved.
  3024. X */
  3025. Xint tmpsave(filename, bang)
  3026. X    char    *filename;    /* the name to save it to */
  3027. X    int    bang;        /* forced write? */
  3028. X{
  3029. X    int        fd;    /* fd of the file we're writing to */
  3030. X    REG int        len;    /* length of a text block */
  3031. X    REG BLK        *this;    /* a text block */
  3032. X    long        bytes;    /* byte counter */
  3033. X    REG int        i;
  3034. X
  3035. X    /* if no filename is given, assume the original file name */
  3036. X    if (!filename || !*filename)
  3037. X    {
  3038. X        filename = origname;
  3039. X    }
  3040. X
  3041. X    /* if still no file name, then fail */
  3042. X    if (!*filename)
  3043. X    {
  3044. X        msg("Don't know a name for this file -- NOT WRITTEN");
  3045. X        return FALSE;
  3046. X    }
  3047. X
  3048. X    /* can't rewrite a READONLY file */
  3049. X    if (!strcmp(filename, origname) && *o_readonly && !bang)
  3050. X    {
  3051. X        msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
  3052. X        return FALSE;
  3053. X    }
  3054. X
  3055. X    /* open the file */
  3056. X    if (*filename == '>' && filename[1] == '>')
  3057. X    {
  3058. X        filename += 2;
  3059. X        while (*filename == ' ' || *filename == '\t')
  3060. X        {
  3061. X            filename++;
  3062. X        }
  3063. X#ifdef O_APPEND
  3064. X        fd = open(filename, O_WRONLY|O_APPEND);
  3065. X#else
  3066. X        fd = open(filename, O_WRONLY);
  3067. X        lseek(fd, 0L, 2);
  3068. X#endif
  3069. X    }
  3070. X    else
  3071. X    {
  3072. X        /* either the file must not exist, or it must be the original
  3073. X         * file, or we must have a bang
  3074. X         */
  3075. X        if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang)
  3076. X        {
  3077. X            msg("File already exists - Use :w! to overwrite");
  3078. X            return FALSE;
  3079. X        }
  3080. X        fd = creat(filename, FILEPERMS);
  3081. X    }
  3082. X    if (fd < 0)
  3083. X    {
  3084. X        msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
  3085. X        return FALSE;
  3086. X    }
  3087. X
  3088. X    /* write each text block to the file */
  3089. X    bytes = 0L;
  3090. X    for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
  3091. X    {
  3092. X        for (len = 0; len < BLKSIZE && this->c[len]; len++)
  3093. X        {
  3094. X        }
  3095. X        twrite(fd, this->c, len);
  3096. X        bytes += len;
  3097. X    }
  3098. X
  3099. X    /* reset the "modified" flag */
  3100. X    clrflag(file, MODIFIED);
  3101. X    significant = FALSE;
  3102. X
  3103. X    /* report lines & characters */
  3104. X#if MSDOS || TOS
  3105. X    bytes += nlines; /* for the inserted carriage returns */
  3106. X#endif
  3107. X    if (strncmp(filename, o_directory, strlen(o_directory)))
  3108. X    {
  3109. X        msg("Wrote \"%s\"  %ld lines, %ld characters", filename, nlines, bytes);
  3110. X    }
  3111. X
  3112. X    /* close the file */
  3113. X    close(fd);
  3114. X
  3115. X    return TRUE;
  3116. X}
  3117. X
  3118. X
  3119. X/* This function deletes the temporary file.  If the file has been modified
  3120. X * and "bang" is FALSE, then it returns FALSE without doing anything; else
  3121. X * it returns TRUE.
  3122. X *
  3123. X * If the "autowrite" option is set, then instead of returning FALSE when
  3124. X * the file has been modified and "bang" is false, it will call tmpend().
  3125. X */
  3126. Xint tmpabort(bang)
  3127. X    int    bang;
  3128. X{
  3129. X    /* if there is no file, return successfully */
  3130. X    if (tmpfd < 0)
  3131. X    {
  3132. X        return TRUE;
  3133. X    }
  3134. X
  3135. X    /* see if we must return FALSE -- can't quit */
  3136. X    if (!bang && tstflag(file, MODIFIED))
  3137. X    {
  3138. X        /* if "autowrite" is set, then act like tmpend() */
  3139. X        if (*o_autowrite)
  3140. X            return tmpend(bang);
  3141. X        else
  3142. X            return FALSE;
  3143. X    }
  3144. X
  3145. X    /* delete the tmp file */
  3146. X    cutswitch(tmpname);
  3147. X    close(tmpfd);
  3148. X    tmpfd = -1;
  3149. X    unlink(tmpname);
  3150. X    strcpy(prevorig, origname);
  3151. X    prevline = markline(cursor);
  3152. X    *origname = '\0';
  3153. X    origtime = 0L;
  3154. X    blkinit();
  3155. X    nlines = 0;
  3156. X    initflags();
  3157. X    return TRUE;
  3158. X}
  3159. X
  3160. X/* This function saves the file if it has been modified, and then deletes
  3161. X * the temporary file. Returns TRUE if successful, or FALSE if the file
  3162. X * needs to be saved but can't be.  When it returns FALSE, it will not have
  3163. X * deleted the tmp file, either.
  3164. X */
  3165. Xint tmpend(bang)
  3166. X    int    bang;
  3167. X{
  3168. X    /* save the file if it has been modified */
  3169. X    if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
  3170. X    {
  3171. X        return FALSE;
  3172. X    }
  3173. X
  3174. X    /* delete the tmp file */
  3175. X    tmpabort(TRUE);
  3176. X
  3177. X    return TRUE;
  3178. X}
  3179. X
  3180. X
  3181. X/* If the tmp file has been changed, then this function will force those
  3182. X * changes to be written to the disk, so that the tmp file will survive a
  3183. X * system crash or power failure.
  3184. X */
  3185. X#if MSDOS || TOS || OSK
  3186. Xsync()
  3187. X{
  3188. X# if OSK
  3189. X    /* OS9 doesn't need an explicit sync operation, but the linker
  3190. X     * demands something called sync(), so this is a dummy function.
  3191. X     */
  3192. X#else
  3193. X    /* MS-DOS and TOS don't flush their buffers until the file is closed,
  3194. X     * so here we close the tmp file and then immediately reopen it.
  3195. X     */
  3196. X    close(tmpfd);
  3197. X    tmpfd = open(tmpname, O_RDWR | O_BINARY);
  3198. X#endif
  3199. X}
  3200. X#endif
  3201. eof
  3202. if test `wc -c <tmp.c` -ne 12770
  3203. then
  3204. echo tmp.c damaged!
  3205. fi
  3206. fi
  3207.  
  3208. exit 0
  3209. -------------------------------------------------------------------------------
  3210. Steve Kirkendall     kirkenda@cs.pdx.edu      Grad student at Portland State U.
  3211.