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

  1.  
  2. : This is a shar archive.  Extract with sh, not csh.
  3. : The rest of this file will extract:
  4. : param.c ptrfunc.c regexp.c regsub.c screen.c
  5. echo extracting - param.c
  6. sed 's/^X//' > param.c << '!EOR!'
  7. X/* $Header: /nw/tony/src/stevie/src/RCS/param.c,v 1.10 89/08/02 10:59:10 tony Exp $
  8. X *
  9. X * Code to handle user-settable parameters. This is all pretty much table-
  10. X * driven. To add a new parameter, put it in the params array, and add a
  11. X * macro for it in param.h. If it's a numeric parameter, add any necessary
  12. X * bounds checks to doset(). String parameters aren't currently supported.
  13. X */
  14. X
  15. X#include "stevie.h"
  16. X
  17. Xstruct    param    params[] = {
  18. X
  19. X    { "tabstop",    "ts",        8,    P_NUM },
  20. X    { "scroll",    "scroll",    12,    P_NUM },
  21. X    { "report",    "report",    5,    P_NUM },
  22. X    { "lines",    "lines",    25,    P_NUM },
  23. X
  24. X    { "vbell",    "vb",        TRUE,    P_BOOL },
  25. X    { "showmatch",    "sm",        FALSE,    P_BOOL },
  26. X    { "wrapscan",    "ws",        TRUE,    P_BOOL },
  27. X    { "errorbells",    "eb",        FALSE,    P_BOOL },
  28. X    { "showmode",    "mo",        FALSE,    P_BOOL },
  29. X    { "backup",    "bk",        FALSE,    P_BOOL },
  30. X    { "return",    "cr",        TRUE,    P_BOOL },
  31. X    { "list",    "list",        FALSE,    P_BOOL },
  32. X    { "ignorecase",    "ic",        FALSE,    P_BOOL },
  33. X    { "autoindent",    "ai",        FALSE,    P_BOOL },
  34. X    { "number",    "nu",        FALSE,    P_BOOL },
  35. X    { "modelines",    "ml",        FALSE,    P_BOOL },
  36. X    { "tildeop",    "to",        FALSE,    P_BOOL },
  37. X    { "terse",    "terse",    FALSE,    P_BOOL },
  38. X    { "tagstack",    "tg",        FALSE,    P_BOOL },
  39. X    { "color",    "co",        -1,    P_NUM  },
  40. X    { "",        "",        0,    0, }        /* end marker */
  41. X
  42. X};
  43. X
  44. Xstatic    void    showparms();
  45. X
  46. Xvoid
  47. Xdoset(arg)
  48. Xchar    *arg;        /* parameter string */
  49. X{
  50. X    register int    i;
  51. X    register char    *s;
  52. X    bool_t    did_lines = FALSE;
  53. X    bool_t    state = TRUE;        /* new state of boolean parms. */
  54. X
  55. X    if (arg == NULL) {
  56. X        showparms(FALSE);
  57. X        return;
  58. X    }
  59. X    if (strncmp(arg, "all", 3) == 0) {
  60. X        showparms(TRUE);
  61. X        return;
  62. X    }
  63. X    if (strncmp(arg, "no", 2) == 0) {
  64. X        state = FALSE;
  65. X        arg += 2;
  66. X    }
  67. X
  68. X    for (i=0; params[i].fullname[0] != NUL ;i++) {
  69. X        s = params[i].fullname;
  70. X        if (strncmp(arg, s, strlen(s)) == 0)    /* matched full name */
  71. X            break;
  72. X        s = params[i].shortname;
  73. X        if (strncmp(arg, s, strlen(s)) == 0)    /* matched short name */
  74. X            break;
  75. X    }
  76. X
  77. X    if (params[i].fullname[0] != NUL) {    /* found a match */
  78. X        if (params[i].flags & P_NUM) {
  79. X            did_lines = (i == P_LI);
  80. X            if (arg[strlen(s)] != '=' || state == FALSE)
  81. X                emsg("Invalid set of numeric parameter");
  82. X            else {
  83. X                params[i].value = atoi(arg+strlen(s)+1);
  84. X                params[i].flags |= P_CHANGED;
  85. X                if (i==P_CO)  setcolor (P(P_CO));
  86. X            }
  87. X        } else /* boolean */ {
  88. X            if (arg[strlen(s)] == '=')
  89. X                emsg("Invalid set of boolean parameter");
  90. X            else {
  91. X                params[i].value = state;
  92. X                params[i].flags |= P_CHANGED;
  93. X            }
  94. X        }
  95. X    } else
  96. X        emsg("Unrecognized 'set' option");
  97. X
  98. X    /*
  99. X     * Update the screen in case we changed something like "tabstop"
  100. X     * or "list" that will change its appearance.
  101. X     */
  102. X    updatescreen();
  103. X
  104. X    if (did_lines) {
  105. X        Rows = P(P_LI);
  106. X        P(P_LI) = Rows = setrows( Rows );
  107. X                    /* setrows() is system-dependent.
  108. X                     * This assures no impossible values
  109. X                     * will be set.
  110. X                     */
  111. X        if (screenalloc() == -1) return;    /* allocate new screen buffers */
  112. X        screenclear();
  113. X        updatescreen();
  114. X    }
  115. X    /*
  116. X     * Check the bounds for numeric parameters here
  117. X     */
  118. X    if (P(P_TS) <= 0 || P(P_TS) > 32) {
  119. X        emsg("Invalid tab size specified");
  120. X        P(P_TS) = 8;
  121. X        return;
  122. X    }
  123. X
  124. X    if (P(P_SS) <= 0 || P(P_SS) > Rows) {
  125. X        emsg("Invalid scroll size specified");
  126. X        P(P_SS) = 12;
  127. X        return;
  128. X    }
  129. X
  130. X#ifndef    TILDEOP
  131. X    if (P(P_TO)) {
  132. X        emsg("Tilde-operator not enabled");
  133. X        P(P_TO) = FALSE;
  134. X        return;
  135. X    }
  136. X#endif
  137. X    /*
  138. X     * Check for another argument, and call doset() recursively, if
  139. X     * found. If any argument results in an error, no further
  140. X     * parameters are processed.
  141. X     */
  142. X    while (*arg != ' ' && *arg != '\t') {    /* skip to next white space */
  143. X        if (*arg == NUL)
  144. X            return;            /* end of parameter list */
  145. X        arg++;
  146. X    }
  147. X    while (*arg == ' ' || *arg == '\t')    /* skip to next non-white */
  148. X        arg++;
  149. X
  150. X    if (*arg)
  151. X        doset(arg);    /* recurse on next parameter */
  152. X}
  153. X
  154. Xstatic    void
  155. Xshowparms(all)
  156. Xbool_t    all;    /* show ALL parameters */
  157. X{
  158. X    register struct    param    *p;
  159. X    char    buf[64];
  160. X
  161. X    gotocmd(TRUE, 0);
  162. X    outstr("Parameters:\r\n");
  163. X
  164. X    for (p = ¶ms[0]; p->fullname[0] != NUL ;p++) {
  165. X        if (!all && ((p->flags & P_CHANGED) == 0))
  166. X            continue;
  167. X        if (p->flags & P_BOOL)
  168. X            sprintf(buf, "\t%s%s\r\n",
  169. X                (p->value ? "" : "no"), p->fullname);
  170. X        else
  171. X            sprintf(buf, "\t%s=%d\r\n", p->fullname, p->value);
  172. X
  173. X        outstr(buf);
  174. X    }
  175. X    wait_return();
  176. X}
  177. !EOR!
  178. echo extracting - ptrfunc.c
  179. sed 's/^X//' > ptrfunc.c << '!EOR!'
  180. X/* $Header: /nw/tony/src/stevie/src/RCS/ptrfunc.c,v 1.5 89/03/11 22:43:12 tony Exp $
  181. X *
  182. X * The routines in this file attempt to imitate many of the operations
  183. X * that used to be performed on simple character pointers and are now
  184. X * performed on LPTR's. This makes it easier to modify other sections
  185. X * of the code. Think of an LPTR as representing a position in the file.
  186. X * Positions can be incremented, decremented, compared, etc. through
  187. X * the functions implemented here.
  188. X */
  189. X
  190. X#include "stevie.h"
  191. X
  192. X/*
  193. X * inc(p)
  194. X *
  195. X * Increment the line pointer 'p' crossing line boundaries as necessary.
  196. X * Return 1 when crossing a line, -1 when at end of file, 0 otherwise.
  197. X */
  198. Xint
  199. Xinc(lp)
  200. Xregister LPTR    *lp;
  201. X{
  202. X    register char    *p;
  203. X
  204. X    if (lp && lp->linep)
  205. X        p = &(lp->linep->s[lp->index]);
  206. X    else
  207. X        return -1;
  208. X
  209. X    if (*p != NUL) {            /* still within line */
  210. X        lp->index++;
  211. X        return ((p[1] != NUL) ? 0 : 1);
  212. X    }
  213. X
  214. X    if (lp->linep->next != Fileend->linep) {  /* there is a next line */
  215. X        lp->index = 0;
  216. X        lp->linep = lp->linep->next;
  217. X        return 1;
  218. X    }
  219. X
  220. X    return -1;
  221. X}
  222. X
  223. X/*
  224. X * dec(p)
  225. X *
  226. X * Decrement the line pointer 'p' crossing line boundaries as necessary.
  227. X * Return 1 when crossing a line, -1 when at start of file, 0 otherwise.
  228. X */
  229. Xint
  230. Xdec(lp)
  231. Xregister LPTR    *lp;
  232. X{
  233. X    if (lp->index > 0) {            /* still within line */
  234. X        lp->index--;
  235. X        return 0;
  236. X    }
  237. X
  238. X    if (lp->linep->prev != Filetop->linep) { /* there is a prior line */
  239. X        lp->linep = lp->linep->prev;
  240. X        lp->index = strlen(lp->linep->s);
  241. X        return 1;
  242. X    }
  243. X
  244. X    lp->index = 0;                /* stick at first char */
  245. X    return -1;                /* at start of file */
  246. X}
  247. X
  248. X/*
  249. X * gchar(lp) - get the character at position "lp"
  250. X */
  251. Xint
  252. Xgchar(lp)
  253. Xregister LPTR    *lp;
  254. X{
  255. X    if (lp && lp->linep)
  256. X        return (lp->linep->s[lp->index]);
  257. X    else
  258. X        return 0;
  259. X}
  260. X
  261. X/*
  262. X * pchar(lp, c) - put character 'c' at position 'lp'
  263. X */
  264. Xvoid
  265. Xpchar(lp, c)
  266. Xregister LPTR    *lp;
  267. Xchar    c;
  268. X{
  269. X    lp->linep->s[lp->index] = c;
  270. X}
  271. X
  272. X/*
  273. X * pswap(a, b) - swap two position pointers
  274. X */
  275. Xvoid
  276. Xpswap(a, b)
  277. Xregister LPTR    *a, *b;
  278. X{
  279. X    LPTR    tmp;
  280. X
  281. X    tmp = *a;
  282. X    *a  = *b;
  283. X    *b  = tmp;
  284. X}
  285. X
  286. X/*
  287. X * Position comparisons
  288. X */
  289. X
  290. Xbool_t
  291. Xlt(a, b)
  292. Xregister LPTR    *a, *b;
  293. X{
  294. X    register int    an, bn;
  295. X
  296. X    an = LINEOF(a);
  297. X    bn = LINEOF(b);
  298. X
  299. X    if (an != bn)
  300. X        return (an < bn);
  301. X    else
  302. X        return (a->index < b->index);
  303. X}
  304. X
  305. X#if 0
  306. Xbool_t
  307. Xgt(a, b)
  308. XLPTR    *a, *b;
  309. X{
  310. X    register int an, bn;
  311. X
  312. X    an = LINEOF(a);
  313. X    bn = LINEOF(b);
  314. X
  315. X    if (an != bn)
  316. X        return (an > bn);
  317. X    else
  318. X        return (a->index > b->index);
  319. X}
  320. X#endif
  321. X
  322. Xbool_t
  323. Xequal(a, b)
  324. Xregister LPTR    *a, *b;
  325. X{
  326. X    return (a->linep == b->linep && a->index == b->index);
  327. X}
  328. X
  329. Xbool_t
  330. Xltoreq(a, b)
  331. Xregister LPTR    *a, *b;
  332. X{
  333. X    return (lt(a, b) || equal(a, b));
  334. X}
  335. X
  336. X#if 0
  337. Xbool_t
  338. Xgtoreq(a, b)
  339. XLPTR    *a, *b;
  340. X{
  341. X    return (gt(a, b) || equal(a, b));
  342. X}
  343. X#endif
  344. !EOR!
  345. echo extracting - regexp.c
  346. sed 's/^X//' > regexp.c << '!EOR!'
  347. X/* $Header: /nw/tony/src/stevie/src/RCS/regexp.c,v 1.5 89/07/07 16:27:11 tony Exp $
  348. X *
  349. X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
  350. X *
  351. X * This is NOT the original regular expression code as written by
  352. X * Henry Spencer. This code has been modified specifically for use
  353. X * with the STEVIE editor, and should not be used apart from compiling
  354. X * STEVIE. If you want a good regular expression library, get the
  355. X * original code. The copyright notice that follows is from the
  356. X * original.
  357. X *
  358. X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
  359. X *
  360. X *
  361. X * regcomp and regexec -- regsub and regerror are elsewhere
  362. X *
  363. X *    Copyright (c) 1986 by University of Toronto.
  364. X *    Written by Henry Spencer.  Not derived from licensed software.
  365. X *
  366. X *    Permission is granted to anyone to use this software for any
  367. X *    purpose on any computer system, and to redistribute it freely,
  368. X *    subject to the following restrictions:
  369. X *
  370. X *    1. The author is not responsible for the consequences of use of
  371. X *        this software, no matter how awful, even if they arise
  372. X *        from defects in it.
  373. X *
  374. X *    2. The origin of this software must not be misrepresented, either
  375. X *        by explicit claim or by omission.
  376. X *
  377. X *    3. Altered versions must be plainly marked as such, and must not
  378. X *        be misrepresented as being the original software.
  379. X *
  380. X * Beware that some of this code is subtly aware of the way operator
  381. X * precedence is structured in regular expressions.  Serious changes in
  382. X * regular-expression syntax might require a total rethink.
  383. X *
  384. X */
  385. X
  386. X#include "env.h"
  387. X
  388. X#include <stdio.h>
  389. X#include "regexp.h"
  390. X#include "regmagic.h"
  391. X
  392. X/*
  393. X * The "internal use only" fields in regexp.h are present to pass info from
  394. X * compile to execute that permits the execute phase to run lots faster on
  395. X * simple cases.  They are:
  396. X *
  397. X * regstart    char that must begin a match; '\0' if none obvious
  398. X * reganch    is the match anchored (at beginning-of-line only)?
  399. X * regmust    string (pointer into program) that match must include, or NULL
  400. X * regmlen    length of regmust string
  401. X *
  402. X * Regstart and reganch permit very fast decisions on suitable starting points
  403. X * for a match, cutting down the work a lot.  Regmust permits fast rejection
  404. X * of lines that cannot possibly match.  The regmust tests are costly enough
  405. X * that regcomp() supplies a regmust only if the r.e. contains something
  406. X * potentially expensive (at present, the only such thing detected is * or +
  407. X * at the start of the r.e., which can involve a lot of backup).  Regmlen is
  408. X * supplied because the test in regexec() needs it and regcomp() is computing
  409. X * it anyway.
  410. X */
  411. X
  412. X/*
  413. X * Structure for regexp "program".  This is essentially a linear encoding
  414. X * of a nondeterministic finite-state machine (aka syntax charts or
  415. X * "railroad normal form" in parsing technology).  Each node is an opcode
  416. X * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
  417. X * all nodes except BRANCH implement concatenation; a "next" pointer with
  418. X * a BRANCH on both ends of it is connecting two alternatives.  (Here we
  419. X * have one of the subtle syntax dependencies:  an individual BRANCH (as
  420. X * opposed to a collection of them) is never concatenated with anything
  421. X * because of operator precedence.)  The operand of some types of node is
  422. X * a literal string; for others, it is a node leading into a sub-FSM.  In
  423. X * particular, the operand of a BRANCH node is the first node of the branch.
  424. X * (NB this is *not* a tree structure:  the tail of the branch connects
  425. X * to the thing following the set of BRANCHes.)  The opcodes are:
  426. X */
  427. X
  428. X/* definition    number    opnd?    meaning */
  429. X#define    END    0    /* no    End of program. */
  430. X#define    BOL    1    /* no    Match "" at beginning of line. */
  431. X#define    EOL    2    /* no    Match "" at end of line. */
  432. X#define    ANY    3    /* no    Match any one character. */
  433. X#define    ANYOF    4    /* str    Match any character in this string. */
  434. X#define    ANYBUT    5    /* str    Match any character not in this string. */
  435. X#define    BRANCH    6    /* node    Match this alternative, or the next... */
  436. X#define    BACK    7    /* no    Match "", "next" ptr points backward. */
  437. X#define    EXACTLY    8    /* str    Match this string. */
  438. X#define    NOTHING    9    /* no    Match empty string. */
  439. X#define    STAR    10    /* node    Match this (simple) thing 0 or more times. */
  440. X#define    PLUS    11    /* node    Match this (simple) thing 1 or more times. */
  441. X#define    OPEN    20    /* no    Mark this point in input as start of #n. */
  442. X            /*    OPEN+1 is number 1, etc. */
  443. X#define    CLOSE    30    /* no    Analogous to OPEN. */
  444. X
  445. X/*
  446. X * Opcode notes:
  447. X *
  448. X * BRANCH    The set of branches constituting a single choice are hooked
  449. X *        together with their "next" pointers, since precedence prevents
  450. X *        anything being concatenated to any individual branch.  The
  451. X *        "next" pointer of the last BRANCH in a choice points to the
  452. X *        thing following the whole choice.  This is also where the
  453. X *        final "next" pointer of each individual branch points; each
  454. X *        branch starts with the operand node of a BRANCH node.
  455. X *
  456. X * BACK        Normal "next" pointers all implicitly point forward; BACK
  457. X *        exists to make loop structures possible.
  458. X *
  459. X * STAR,PLUS    '?', and complex '*' and '+', are implemented as circular
  460. X *        BRANCH structures using BACK.  Simple cases (one character
  461. X *        per match) are implemented with STAR and PLUS for speed
  462. X *        and to minimize recursive plunges.
  463. X *
  464. X * OPEN,CLOSE    ...are numbered at compile time.
  465. X */
  466. X
  467. X/*
  468. X * A node is one char of opcode followed by two chars of "next" pointer.
  469. X * "Next" pointers are stored as two 8-bit pieces, high order first.  The
  470. X * value is a positive offset from the opcode of the node containing it.
  471. X * An operand, if any, simply follows the node.  (Note that much of the
  472. X * code generation knows about this implicit relationship.)
  473. X *
  474. X * Using two bytes for the "next" pointer is vast overkill for most things,
  475. X * but allows patterns to get big without disasters.
  476. X */
  477. X#define    OP(p)    (*(p))
  478. X#define    NEXT(p)    (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
  479. X#define    OPERAND(p)    ((p) + 3)
  480. X
  481. X/*
  482. X * See regmagic.h for one further detail of program structure.
  483. X */
  484. X
  485. X
  486. X/*
  487. X * Utility definitions.
  488. X */
  489. X#ifndef CHARBITS
  490. X#define    UCHARAT(p)    ((int)*(unsigned char *)(p))
  491. X#else
  492. X#define    UCHARAT(p)    ((int)*(p)&CHARBITS)
  493. X#endif
  494. X
  495. X#define    FAIL(m)    { regerror(m); return(NULL); }
  496. X#define    ISMULT(c)    ((c) == '*' || (c) == '+' || (c) == '?')
  497. X#define    META    "^$.[()|?+*\\"
  498. X
  499. X/*
  500. X * Flags to be passed up and down.
  501. X */
  502. X#define    HASWIDTH    01    /* Known never to match null string. */
  503. X#define    SIMPLE        02    /* Simple enough to be STAR/PLUS operand. */
  504. X#define    SPSTART        04    /* Starts with * or +. */
  505. X#define    WORST        0    /* Worst case. */
  506. X
  507. X#ifndef    ORIGINAL
  508. X/*
  509. X * The following supports the ability to ignore case in searches.
  510. X */
  511. X
  512. X#include <ctype.h>
  513. X
  514. Xint reg_ic = 0;            /* set by callers to ignore case */
  515. X
  516. X/*
  517. X * mkup - convert to upper case IF we're doing caseless compares
  518. X */
  519. X#define    mkup(c)        ((reg_ic && islower(c)) ? toupper(c) : (c))
  520. X
  521. X#endif
  522. X
  523. X/*
  524. X * Global work variables for regcomp().
  525. X */
  526. Xstatic char *regparse;        /* Input-scan pointer. */
  527. Xstatic int regnpar;        /* () count. */
  528. Xstatic char regdummy;
  529. Xstatic char *regcode;        /* Code-emit pointer; ®dummy = don't. */
  530. Xstatic long regsize;        /* Code size. */
  531. X
  532. X/*
  533. X * Forward declarations for regcomp()'s friends.
  534. X */
  535. X#ifndef STATIC
  536. X#define    STATIC    static
  537. X#endif
  538. XSTATIC char *reg();
  539. XSTATIC char *regbranch();
  540. XSTATIC char *regpiece();
  541. XSTATIC char *regatom();
  542. XSTATIC char *regnode();
  543. XSTATIC char *regnext();
  544. XSTATIC void regc();
  545. XSTATIC void reginsert();
  546. XSTATIC void regtail();
  547. XSTATIC void regoptail();
  548. X#ifdef STRCSPN
  549. XSTATIC int strcspn();
  550. X#endif
  551. X
  552. X/*
  553. X - regcomp - compile a regular expression into internal code
  554. X *
  555. X * We can't allocate space until we know how big the compiled form will be,
  556. X * but we can't compile it (and thus know how big it is) until we've got a
  557. X * place to put the code.  So we cheat:  we compile it twice, once with code
  558. X * generation turned off and size counting turned on, and once "for real".
  559. X * This also means that we don't allocate space until we are sure that the
  560. X * thing really will compile successfully, and we never have to move the
  561. X * code and thus invalidate pointers into it.  (Note that it has to be in
  562. X * one piece because free() must be able to free it all.)
  563. X *
  564. X * Beware that the optimization-preparation code in here knows about some
  565. X * of the structure of the compiled regexp.
  566. X */
  567. Xregexp *
  568. Xregcomp(exp)
  569. Xchar *exp;
  570. X{
  571. X    register regexp *r;
  572. X    register char *scan;
  573. X    register char *longest;
  574. X    register int len;
  575. X    int flags;
  576. X    extern char *malloc();
  577. X
  578. X    if (exp == NULL)
  579. X        FAIL("NULL argument");
  580. X
  581. X    /* First pass: determine size, legality. */
  582. X    regparse = exp;
  583. X    regnpar = 1;
  584. X    regsize = 0L;
  585. X    regcode = ®dummy;
  586. X    regc(MAGIC);
  587. X    if (reg(0, &flags) == NULL)
  588. X        return(NULL);
  589. X
  590. X    /* Small enough for pointer-storage convention? */
  591. X    if (regsize >= 32767L)        /* Probably could be 65535L. */
  592. X        FAIL("regexp too big");
  593. X
  594. X    /* Allocate space. */
  595. X    r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
  596. X    if (r == NULL)
  597. X        FAIL("out of space");
  598. X
  599. X    /* Second pass: emit code. */
  600. X    regparse = exp;
  601. X    regnpar = 1;
  602. X    regcode = r->program;
  603. X    regc(MAGIC);
  604. X    if (reg(0, &flags) == NULL)
  605. X        return(NULL);
  606. X
  607. X    /* Dig out information for optimizations. */
  608. X    r->regstart = '\0';    /* Worst-case defaults. */
  609. X    r->reganch = 0;
  610. X    r->regmust = NULL;
  611. X    r->regmlen = 0;
  612. X    scan = r->program+1;            /* First BRANCH. */
  613. X    if (OP(regnext(scan)) == END) {        /* Only one top-level choice. */
  614. X        scan = OPERAND(scan);
  615. X
  616. X        /* Starting-point info. */
  617. X        if (OP(scan) == EXACTLY)
  618. X            r->regstart = *OPERAND(scan);
  619. X        else if (OP(scan) == BOL)
  620. X            r->reganch++;
  621. X
  622. X        /*
  623. X         * If there's something expensive in the r.e., find the
  624. X         * longest literal string that must appear and make it the
  625. X         * regmust.  Resolve ties in favor of later strings, since
  626. X         * the regstart check works with the beginning of the r.e.
  627. X         * and avoiding duplication strengthens checking.  Not a
  628. X         * strong reason, but sufficient in the absence of others.
  629. X         */
  630. X        if (flags&SPSTART) {
  631. X            longest = NULL;
  632. X            len = 0;
  633. X            for (; scan != NULL; scan = regnext(scan))
  634. X                if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
  635. X                    longest = OPERAND(scan);
  636. X                    len = strlen(OPERAND(scan));
  637. X                }
  638. X            r->regmust = longest;
  639. X            r->regmlen = len;
  640. X        }
  641. X    }
  642. X
  643. X    return(r);
  644. X}
  645. X
  646. X/*
  647. X - reg - regular expression, i.e. main body or parenthesized thing
  648. X *
  649. X * Caller must absorb opening parenthesis.
  650. X *
  651. X * Combining parenthesis handling with the base level of regular expression
  652. X * is a trifle forced, but the need to tie the tails of the branches to what
  653. X * follows makes it hard to avoid.
  654. X */
  655. Xstatic char *
  656. Xreg(paren, flagp)
  657. Xint paren;            /* Parenthesized? */
  658. Xint *flagp;
  659. X{
  660. X    register char *ret;
  661. X    register char *br;
  662. X    register char *ender;
  663. X    register int parno;
  664. X    int flags;
  665. X
  666. X    *flagp = HASWIDTH;    /* Tentatively. */
  667. X
  668. X    /* Make an OPEN node, if parenthesized. */
  669. X    if (paren) {
  670. X        if (regnpar >= NSUBEXP)
  671. X            FAIL("too many ()");
  672. X        parno = regnpar;
  673. X        regnpar++;
  674. X        ret = regnode(OPEN+parno);
  675. X    } else
  676. X        ret = NULL;
  677. X
  678. X    /* Pick up the branches, linking them together. */
  679. X    br = regbranch(&flags);
  680. X    if (br == NULL)
  681. X        return(NULL);
  682. X    if (ret != NULL)
  683. X        regtail(ret, br);    /* OPEN -> first. */
  684. X    else
  685. X        ret = br;
  686. X    if (!(flags&HASWIDTH))
  687. X        *flagp &= ~HASWIDTH;
  688. X    *flagp |= flags&SPSTART;
  689. X    while (*regparse == '|') {
  690. X        regparse++;
  691. X        br = regbranch(&flags);
  692. X        if (br == NULL)
  693. X            return(NULL);
  694. X        regtail(ret, br);    /* BRANCH -> BRANCH. */
  695. X        if (!(flags&HASWIDTH))
  696. X            *flagp &= ~HASWIDTH;
  697. X        *flagp |= flags&SPSTART;
  698. X    }
  699. X
  700. X    /* Make a closing node, and hook it on the end. */
  701. X    ender = regnode((paren) ? CLOSE+parno : END);    
  702. X    regtail(ret, ender);
  703. X
  704. X    /* Hook the tails of the branches to the closing node. */
  705. X    for (br = ret; br != NULL; br = regnext(br))
  706. X        regoptail(br, ender);
  707. X
  708. X    /* Check for proper termination. */
  709. X    if (paren && *regparse++ != ')') {
  710. X        FAIL("unmatched ()");
  711. X    } else if (!paren && *regparse != '\0') {
  712. X        if (*regparse == ')') {
  713. X            FAIL("unmatched ()");
  714. X        } else
  715. X            FAIL("junk on end");    /* "Can't happen". */
  716. X        /* NOTREACHED */
  717. X    }
  718. X
  719. X    return(ret);
  720. X}
  721. X
  722. X/*
  723. X - regbranch - one alternative of an | operator
  724. X *
  725. X * Implements the concatenation operator.
  726. X */
  727. Xstatic char *
  728. Xregbranch(flagp)
  729. Xint *flagp;
  730. X{
  731. X    register char *ret;
  732. X    register char *chain;
  733. X    register char *latest;
  734. X    int flags;
  735. X
  736. X    *flagp = WORST;        /* Tentatively. */
  737. X
  738. X    ret = regnode(BRANCH);
  739. X    chain = NULL;
  740. X    while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
  741. X        latest = regpiece(&flags);
  742. X        if (latest == NULL)
  743. X            return(NULL);
  744. X        *flagp |= flags&HASWIDTH;
  745. X        if (chain == NULL)    /* First piece. */
  746. X            *flagp |= flags&SPSTART;
  747. X        else
  748. X            regtail(chain, latest);
  749. X        chain = latest;
  750. X    }
  751. X    if (chain == NULL)    /* Loop ran zero times. */
  752. X        (void) regnode(NOTHING);
  753. X
  754. X    return(ret);
  755. X}
  756. X
  757. X/*
  758. X - regpiece - something followed by possible [*+?]
  759. X *
  760. X * Note that the branching code sequences used for ? and the general cases
  761. X * of * and + are somewhat optimized:  they use the same NOTHING node as
  762. X * both the endmarker for their branch list and the body of the last branch.
  763. X * It might seem that this node could be dispensed with entirely, but the
  764. X * endmarker role is not redundant.
  765. X */
  766. Xstatic char *
  767. Xregpiece(flagp)
  768. Xint *flagp;
  769. X{
  770. X    register char *ret;
  771. X    register char op;
  772. X    register char *next;
  773. X    int flags;
  774. X
  775. X    ret = regatom(&flags);
  776. X    if (ret == NULL)
  777. X        return(NULL);
  778. X
  779. X    op = *regparse;
  780. X    if (!ISMULT(op)) {
  781. X        *flagp = flags;
  782. X        return(ret);
  783. X    }
  784. X
  785. X    if (!(flags&HASWIDTH) && op != '?')
  786. X        FAIL("*+ operand could be empty");
  787. X    *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
  788. X
  789. X    if (op == '*' && (flags&SIMPLE))
  790. X        reginsert(STAR, ret);
  791. X    else if (op == '*') {
  792. X        /* Emit x* as (x&|), where & means "self". */
  793. X        reginsert(BRANCH, ret);            /* Either x */
  794. X        regoptail(ret, regnode(BACK));        /* and loop */
  795. X        regoptail(ret, ret);            /* back */
  796. X        regtail(ret, regnode(BRANCH));        /* or */
  797. X        regtail(ret, regnode(NOTHING));        /* null. */
  798. X    } else if (op == '+' && (flags&SIMPLE))
  799. X        reginsert(PLUS, ret);
  800. X    else if (op == '+') {
  801. X        /* Emit x+ as x(&|), where & means "self". */
  802. X        next = regnode(BRANCH);            /* Either */
  803. X        regtail(ret, next);
  804. X        regtail(regnode(BACK), ret);        /* loop back */
  805. X        regtail(next, regnode(BRANCH));        /* or */
  806. X        regtail(ret, regnode(NOTHING));        /* null. */
  807. X    } else if (op == '?') {
  808. X        /* Emit x? as (x|) */
  809. X        reginsert(BRANCH, ret);            /* Either x */
  810. X        regtail(ret, regnode(BRANCH));        /* or */
  811. X        next = regnode(NOTHING);        /* null. */
  812. X        regtail(ret, next);
  813. X        regoptail(ret, next);
  814. X    }
  815. X    regparse++;
  816. X    if (ISMULT(*regparse))
  817. X        FAIL("nested *?+");
  818. X
  819. X    return(ret);
  820. X}
  821. X
  822. X/*
  823. X - regatom - the lowest level
  824. X *
  825. X * Optimization:  gobbles an entire sequence of ordinary characters so that
  826. X * it can turn them into a single node, which is smaller to store and
  827. X * faster to run.  Backslashed characters are exceptions, each becoming a
  828. X * separate node; the code is simpler that way and it's not worth fixing.
  829. X */
  830. Xstatic char *
  831. Xregatom(flagp)
  832. Xint *flagp;
  833. X{
  834. X    register char *ret;
  835. X    int flags;
  836. X
  837. X    *flagp = WORST;        /* Tentatively. */
  838. X
  839. X    switch (*regparse++) {
  840. X    case '^':
  841. X        ret = regnode(BOL);
  842. X        break;
  843. X    case '$':
  844. X        ret = regnode(EOL);
  845. X        break;
  846. X    case '.':
  847. X        ret = regnode(ANY);
  848. X        *flagp |= HASWIDTH|SIMPLE;
  849. X        break;
  850. X    case '[': {
  851. X            register int class;
  852. X            register int classend;
  853. X
  854. X            if (*regparse == '^') {    /* Complement of range. */
  855. X                ret = regnode(ANYBUT);
  856. X                regparse++;
  857. X            } else
  858. X                ret = regnode(ANYOF);
  859. X            if (*regparse == ']' || *regparse == '-')
  860. X                regc(*regparse++);
  861. X            while (*regparse != '\0' && *regparse != ']') {
  862. X                if (*regparse == '-') {
  863. X                    regparse++;
  864. X                    if (*regparse == ']' || *regparse == '\0')
  865. X                        regc('-');
  866. X                    else {
  867. X                        class = UCHARAT(regparse-2)+1;
  868. X                        classend = UCHARAT(regparse);
  869. X                        if (class > classend+1)
  870. X                            FAIL("invalid [] range");
  871. X                        for (; class <= classend; class++)
  872. X                            regc(class);
  873. X                        regparse++;
  874. X                    }
  875. X                } else
  876. X                    regc(*regparse++);
  877. X            }
  878. X            regc('\0');
  879. X            if (*regparse != ']')
  880. X                FAIL("unmatched []");
  881. X            regparse++;
  882. X            *flagp |= HASWIDTH|SIMPLE;
  883. X        }
  884. X        break;
  885. X    case '(':
  886. X        ret = reg(1, &flags);
  887. X        if (ret == NULL)
  888. X            return(NULL);
  889. X        *flagp |= flags&(HASWIDTH|SPSTART);
  890. X        break;
  891. X    case '\0':
  892. X    case '|':
  893. X    case ')':
  894. X        FAIL("internal urp");    /* Supposed to be caught earlier. */
  895. X        break;
  896. X    case '?':
  897. X    case '+':
  898. X    case '*':
  899. X        FAIL("?+* follows nothing");
  900. X        break;
  901. X    case '\\':
  902. X        if (*regparse == '\0')
  903. X            FAIL("trailing \\");
  904. X        ret = regnode(EXACTLY);
  905. X        regc(*regparse++);
  906. X        regc('\0');
  907. X        *flagp |= HASWIDTH|SIMPLE;
  908. X        break;
  909. X    default: {
  910. X            register int len;
  911. X            register char ender;
  912. X
  913. X            regparse--;
  914. X            len = strcspn(regparse, META);
  915. X            if (len <= 0)
  916. X                FAIL("internal disaster");
  917. X            ender = *(regparse+len);
  918. X            if (len > 1 && ISMULT(ender))
  919. X                len--;        /* Back off clear of ?+* operand. */
  920. X            *flagp |= HASWIDTH;
  921. X            if (len == 1)
  922. X                *flagp |= SIMPLE;
  923. X            ret = regnode(EXACTLY);
  924. X            while (len > 0) {
  925. X                regc(*regparse++);
  926. X                len--;
  927. X            }
  928. X            regc('\0');
  929. X        }
  930. X        break;
  931. X    }
  932. X
  933. X    return(ret);
  934. X}
  935. X
  936. X/*
  937. X - regnode - emit a node
  938. X */
  939. Xstatic char *            /* Location. */
  940. Xregnode(op)
  941. Xchar op;
  942. X{
  943. X    register char *ret;
  944. X    register char *ptr;
  945. X
  946. X    ret = regcode;
  947. X    if (ret == ®dummy) {
  948. X        regsize += 3;
  949. X        return(ret);
  950. X    }
  951. X
  952. X    ptr = ret;
  953. X    *ptr++ = op;
  954. X    *ptr++ = '\0';        /* Null "next" pointer. */
  955. X    *ptr++ = '\0';
  956. X    regcode = ptr;
  957. X
  958. X    return(ret);
  959. X}
  960. X
  961. X/*
  962. X - regc - emit (if appropriate) a byte of code
  963. X */
  964. Xstatic void
  965. Xregc(b)
  966. Xchar b;
  967. X{
  968. X    if (regcode != ®dummy)
  969. X        *regcode++ = b;
  970. X    else
  971. X        regsize++;
  972. X}
  973. X
  974. X/*
  975. X - reginsert - insert an operator in front of already-emitted operand
  976. X *
  977. X * Means relocating the operand.
  978. X */
  979. Xstatic void
  980. Xreginsert(op, opnd)
  981. Xchar op;
  982. Xchar *opnd;
  983. X{
  984. X    register char *src;
  985. X    register char *dst;
  986. X    register char *place;
  987. X
  988. X    if (regcode == ®dummy) {
  989. X        regsize += 3;
  990. X        return;
  991. X    }
  992. X
  993. X    src = regcode;
  994. X    regcode += 3;
  995. X    dst = regcode;
  996. X    while (src > opnd)
  997. X        *--dst = *--src;
  998. X
  999. X    place = opnd;        /* Op node, where operand used to be. */
  1000. X    *place++ = op;
  1001. X    *place++ = '\0';
  1002. X    *place++ = '\0';
  1003. X}
  1004. X
  1005. X/*
  1006. X - regtail - set the next-pointer at the end of a node chain
  1007. X */
  1008. Xstatic void
  1009. Xregtail(p, val)
  1010. Xchar *p;
  1011. Xchar *val;
  1012. X{
  1013. X    register char *scan;
  1014. X    register char *temp;
  1015. X    register int offset;
  1016. X
  1017. X    if (p == ®dummy)
  1018. X        return;
  1019. X
  1020. X    /* Find last node. */
  1021. X    scan = p;
  1022. X    for (;;) {
  1023. X        temp = regnext(scan);
  1024. X        if (temp == NULL)
  1025. X            break;
  1026. X        scan = temp;
  1027. X    }
  1028. X
  1029. X    if (OP(scan) == BACK)
  1030. X        offset = scan - val;
  1031. X    else
  1032. X        offset = val - scan;
  1033. X    *(scan+1) = (offset>>8)&0377;
  1034. X    *(scan+2) = offset&0377;
  1035. X}
  1036. X
  1037. X/*
  1038. X - regoptail - regtail on operand of first argument; nop if operandless
  1039. X */
  1040. Xstatic void
  1041. Xregoptail(p, val)
  1042. Xchar *p;
  1043. Xchar *val;
  1044. X{
  1045. X    /* "Operandless" and "op != BRANCH" are synonymous in practice. */
  1046. X    if (p == NULL || p == ®dummy || OP(p) != BRANCH)
  1047. X        return;
  1048. X    regtail(OPERAND(p), val);
  1049. X}
  1050. X
  1051. X/*
  1052. X * regexec and friends
  1053. X */
  1054. X
  1055. X/*
  1056. X * Global work variables for regexec().
  1057. X */
  1058. Xstatic char *reginput;        /* String-input pointer. */
  1059. Xstatic char *regbol;        /* Beginning of input, for ^ check. */
  1060. Xstatic char **regstartp;    /* Pointer to startp array. */
  1061. Xstatic char **regendp;        /* Ditto for endp. */
  1062. X
  1063. X/*
  1064. X * Forwards.
  1065. X */
  1066. XSTATIC int regtry();
  1067. XSTATIC int regmatch();
  1068. XSTATIC int regrepeat();
  1069. X
  1070. X#ifdef DEBUG
  1071. Xint regnarrate = 0;
  1072. Xvoid regdump();
  1073. XSTATIC char *regprop();
  1074. X#endif
  1075. X
  1076. X/*
  1077. X - regexec - match a regexp against a string
  1078. X */
  1079. Xint
  1080. Xregexec(prog, string, at_bol)
  1081. Xregister regexp *prog;
  1082. Xregister char *string;
  1083. Xint at_bol;
  1084. X{
  1085. X    register char *s;
  1086. X    extern char *cstrchr();
  1087. X
  1088. X    /* Be paranoid... */
  1089. X    if (prog == NULL || string == NULL) {
  1090. X        regerror("NULL parameter");
  1091. X        return(0);
  1092. X    }
  1093. X
  1094. X    /* Check validity of program. */
  1095. X    if (UCHARAT(prog->program) != MAGIC) {
  1096. X        regerror("corrupted program");
  1097. X        return(0);
  1098. X    }
  1099. X
  1100. X    /* If there is a "must appear" string, look for it. */
  1101. X    if (prog->regmust != NULL) {
  1102. X        s = string;
  1103. X        while ((s = cstrchr(s, prog->regmust[0])) != NULL) {
  1104. X            if (cstrncmp(s, prog->regmust, prog->regmlen) == 0)
  1105. X                break;    /* Found it. */
  1106. X            s++;
  1107. X        }
  1108. X        if (s == NULL)    /* Not present. */
  1109. X            return(0);
  1110. X    }
  1111. X
  1112. X    /* Mark beginning of line for ^ . */
  1113. X    if (at_bol)
  1114. X        regbol = string;    /* is possible to match bol */
  1115. X    else
  1116. X        regbol = NULL;        /* we aren't there, so don't match it */
  1117. X
  1118. X    /* Simplest case:  anchored match need be tried only once. */
  1119. X    if (prog->reganch)
  1120. X        return(regtry(prog, string));
  1121. X
  1122. X    /* Messy cases:  unanchored match. */
  1123. X    s = string;
  1124. X    if (prog->regstart != '\0')
  1125. X        /* We know what char it must start with. */
  1126. X        while ((s = cstrchr(s, prog->regstart)) != NULL) {
  1127. X            if (regtry(prog, s))
  1128. X                return(1);
  1129. X            s++;
  1130. X        }
  1131. X    else
  1132. X        /* We don't -- general case. */
  1133. X        do {
  1134. X            if (regtry(prog, s))
  1135. X                return(1);
  1136. X        } while (*s++ != '\0');
  1137. X
  1138. X    /* Failure. */
  1139. X    return(0);
  1140. X}
  1141. X
  1142. X/*
  1143. X - regtry - try match at specific point
  1144. X */
  1145. Xstatic int            /* 0 failure, 1 success */
  1146. Xregtry(prog, string)
  1147. Xregexp *prog;
  1148. Xchar *string;
  1149. X{
  1150. X    register int i;
  1151. X    register char **sp;
  1152. X    register char **ep;
  1153. X
  1154. X    reginput = string;
  1155. X    regstartp = prog->startp;
  1156. X    regendp = prog->endp;
  1157. X
  1158. X    sp = prog->startp;
  1159. X    ep = prog->endp;
  1160. X    for (i = NSUBEXP; i > 0; i--) {
  1161. X        *sp++ = NULL;
  1162. X        *ep++ = NULL;
  1163. X    }
  1164. X    if (regmatch(prog->program + 1)) {
  1165. X        prog->startp[0] = string;
  1166. X        prog->endp[0] = reginput;
  1167. X        return(1);
  1168. X    } else
  1169. X        return(0);
  1170. X}
  1171. X
  1172. X/*
  1173. X - regmatch - main matching routine
  1174. X *
  1175. X * Conceptually the strategy is simple:  check to see whether the current
  1176. X * node matches, call self recursively to see whether the rest matches,
  1177. X * and then act accordingly.  In practice we make some effort to avoid
  1178. X * recursion, in particular by going through "ordinary" nodes (that don't
  1179. X * need to know whether the rest of the match failed) by a loop instead of
  1180. X * by recursion.
  1181. X */
  1182. Xstatic int            /* 0 failure, 1 success */
  1183. Xregmatch(prog)
  1184. Xchar *prog;
  1185. X{
  1186. X    register char *scan;    /* Current node. */
  1187. X    char *next;        /* Next node. */
  1188. X    extern char *strchr();
  1189. X
  1190. X    scan = prog;
  1191. X#ifdef DEBUG
  1192. X    if (scan != NULL && regnarrate)
  1193. X        fprintf(stderr, "%s(\n", regprop(scan));
  1194. X#endif
  1195. X    while (scan != NULL) {
  1196. X#ifdef DEBUG
  1197. X        if (regnarrate)
  1198. X            fprintf(stderr, "%s...\n", regprop(scan));
  1199. X#endif
  1200. X        next = regnext(scan);
  1201. X
  1202. X        switch (OP(scan)) {
  1203. X        case BOL:
  1204. X            if (reginput != regbol)
  1205. X                return(0);
  1206. X            break;
  1207. X        case EOL:
  1208. X            if (*reginput != '\0')
  1209. X                return(0);
  1210. X            break;
  1211. X        case ANY:
  1212. X            if (*reginput == '\0')
  1213. X                return(0);
  1214. X            reginput++;
  1215. X            break;
  1216. X        case EXACTLY: {
  1217. X                register int len;
  1218. X                register char *opnd;
  1219. X
  1220. X                opnd = OPERAND(scan);
  1221. X                /* Inline the first character, for speed. */
  1222. X                if (mkup(*opnd) != mkup(*reginput))
  1223. X                    return(0);
  1224. X                len = strlen(opnd);
  1225. X                if (len > 1 && cstrncmp(opnd,reginput,len) != 0)
  1226. X                    return(0);
  1227. X                reginput += len;
  1228. X            }
  1229. X            break;
  1230. X        case ANYOF:
  1231. X             if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
  1232. X                return(0);
  1233. X            reginput++;
  1234. X            break;
  1235. X        case ANYBUT:
  1236. X             if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
  1237. X                return(0);
  1238. X            reginput++;
  1239. X            break;
  1240. X        case NOTHING:
  1241. X            break;
  1242. X        case BACK:
  1243. X            break;
  1244. X        case OPEN+1:
  1245. X        case OPEN+2:
  1246. X        case OPEN+3:
  1247. X        case OPEN+4:
  1248. X        case OPEN+5:
  1249. X        case OPEN+6:
  1250. X        case OPEN+7:
  1251. X        case OPEN+8:
  1252. X        case OPEN+9: {
  1253. X                register int no;
  1254. X                register char *save;
  1255. X
  1256. X                no = OP(scan) - OPEN;
  1257. X                save = reginput;
  1258. X
  1259. X                if (regmatch(next)) {
  1260. X                    /*
  1261. X                     * Don't set startp if some later
  1262. X                     * invocation of the same parentheses
  1263. X                     * already has.
  1264. X                     */
  1265. X                    if (regstartp[no] == NULL)
  1266. X                        regstartp[no] = save;
  1267. X                    return(1);
  1268. X                } else
  1269. X                    return(0);
  1270. X            }
  1271. X            break;
  1272. X        case CLOSE+1:
  1273. X        case CLOSE+2:
  1274. X        case CLOSE+3:
  1275. X        case CLOSE+4:
  1276. X        case CLOSE+5:
  1277. X        case CLOSE+6:
  1278. X        case CLOSE+7:
  1279. X        case CLOSE+8:
  1280. X        case CLOSE+9: {
  1281. X                register int no;
  1282. X                register char *save;
  1283. X
  1284. X                no = OP(scan) - CLOSE;
  1285. X                save = reginput;
  1286. X
  1287. X                if (regmatch(next)) {
  1288. X                    /*
  1289. X                     * Don't set endp if some later
  1290. X                     * invocation of the same parentheses
  1291. X                     * already has.
  1292. X                     */
  1293. X                    if (regendp[no] == NULL)
  1294. X                        regendp[no] = save;
  1295. X                    return(1);
  1296. X                } else
  1297. X                    return(0);
  1298. X            }
  1299. X            break;
  1300. X        case BRANCH: {
  1301. X                register char *save;
  1302. X
  1303. X                if (OP(next) != BRANCH)        /* No choice. */
  1304. X                    next = OPERAND(scan);    /* Avoid recursion. */
  1305. X                else {
  1306. X                    do {
  1307. X                        save = reginput;
  1308. X                        if (regmatch(OPERAND(scan)))
  1309. X                            return(1);
  1310. X                        reginput = save;
  1311. X                        scan = regnext(scan);
  1312. X                    } while (scan != NULL && OP(scan) == BRANCH);
  1313. X                    return(0);
  1314. X                    /* NOTREACHED */
  1315. X                }
  1316. X            }
  1317. X            break;
  1318. X        case STAR:
  1319. X        case PLUS: {
  1320. X                register char nextch;
  1321. X                register int no;
  1322. X                register char *save;
  1323. X                register int min;
  1324. X
  1325. X                /*
  1326. X                 * Lookahead to avoid useless match attempts
  1327. X                 * when we know what character comes next.
  1328. X                 */
  1329. X                nextch = '\0';
  1330. X                if (OP(next) == EXACTLY)
  1331. X                    nextch = *OPERAND(next);
  1332. X                min = (OP(scan) == STAR) ? 0 : 1;
  1333. X                save = reginput;
  1334. X                no = regrepeat(OPERAND(scan));
  1335. X                while (no >= min) {
  1336. X                    /* If it could work, try it. */
  1337. X                    if (nextch == '\0' || *reginput == nextch)
  1338. X                        if (regmatch(next))
  1339. X                            return(1);
  1340. X                    /* Couldn't or didn't -- back up. */
  1341. X                    no--;
  1342. X                    reginput = save + no;
  1343. X                }
  1344. X                return(0);
  1345. X            }
  1346. X            break;
  1347. X        case END:
  1348. X            return(1);    /* Success! */
  1349. X            break;
  1350. X        default:
  1351. X            regerror("memory corruption");
  1352. X            return(0);
  1353. X            break;
  1354. X        }
  1355. X
  1356. X        scan = next;
  1357. X    }
  1358. X
  1359. X    /*
  1360. X     * We get here only if there's trouble -- normally "case END" is
  1361. X     * the terminating point.
  1362. X     */
  1363. X    regerror("corrupted pointers");
  1364. X    return(0);
  1365. X}
  1366. X
  1367. X/*
  1368. X - regrepeat - repeatedly match something simple, report how many
  1369. X */
  1370. Xstatic int
  1371. Xregrepeat(p)
  1372. Xchar *p;
  1373. X{
  1374. X    register int count = 0;
  1375. X    register char *scan;
  1376. X    register char *opnd;
  1377. X
  1378. X    scan = reginput;
  1379. X    opnd = OPERAND(p);
  1380. X    switch (OP(p)) {
  1381. X    case ANY:
  1382. X        count = strlen(scan);
  1383. X        scan += count;
  1384. X        break;
  1385. X    case EXACTLY:
  1386. X        while (mkup(*opnd) == mkup(*scan)) {
  1387. X            count++;
  1388. X            scan++;
  1389. X        }
  1390. X        break;
  1391. X    case ANYOF:
  1392. X        while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
  1393. X            count++;
  1394. X            scan++;
  1395. X        }
  1396. X        break;
  1397. X    case ANYBUT:
  1398. X        while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
  1399. X            count++;
  1400. X            scan++;
  1401. X        }
  1402. X        break;
  1403. X    default:        /* Oh dear.  Called inappropriately. */
  1404. X        regerror("internal foulup");
  1405. X        count = 0;    /* Best compromise. */
  1406. X        break;
  1407. X    }
  1408. X    reginput = scan;
  1409. X
  1410. X    return(count);
  1411. X}
  1412. X
  1413. X/*
  1414. X - regnext - dig the "next" pointer out of a node
  1415. X */
  1416. Xstatic char *
  1417. Xregnext(p)
  1418. Xregister char *p;
  1419. X{
  1420. X    register int offset;
  1421. X
  1422. X    if (p == ®dummy)
  1423. X        return(NULL);
  1424. X
  1425. X    offset = NEXT(p);
  1426. X    if (offset == 0)
  1427. X        return(NULL);
  1428. X
  1429. X    if (OP(p) == BACK)
  1430. X        return(p-offset);
  1431. X    else
  1432. X        return(p+offset);
  1433. X}
  1434. X
  1435. X#ifdef DEBUG
  1436. X
  1437. XSTATIC char *regprop();
  1438. X
  1439. X/*
  1440. X - regdump - dump a regexp onto stdout in vaguely comprehensible form
  1441. X */
  1442. Xvoid
  1443. Xregdump(r)
  1444. Xregexp *r;
  1445. X{
  1446. X    register char *s;
  1447. X    register char op = EXACTLY;    /* Arbitrary non-END op. */
  1448. X    register char *next;
  1449. X    extern char *strchr();
  1450. X
  1451. X
  1452. X    s = r->program + 1;
  1453. X    while (op != END) {    /* While that wasn't END last time... */
  1454. X        op = OP(s);
  1455. X        printf("%2d%s", s-r->program, regprop(s));    /* Where, what. */
  1456. X        next = regnext(s);
  1457. X        if (next == NULL)        /* Next ptr. */
  1458. X            printf("(0)");
  1459. X        else 
  1460. X            printf("(%d)", (s-r->program)+(next-s));
  1461. X        s += 3;
  1462. X        if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
  1463. X            /* Literal string, where present. */
  1464. X            while (*s != '\0') {
  1465. X                putchar(*s);
  1466. X                s++;
  1467. X            }
  1468. X            s++;
  1469. X        }
  1470. X        putchar('\n');
  1471. X    }
  1472. X
  1473. X    /* Header fields of interest. */
  1474. X    if (r->regstart != '\0')
  1475. X        printf("start `%c' ", r->regstart);
  1476. X    if (r->reganch)
  1477. X        printf("anchored ");
  1478. X    if (r->regmust != NULL)
  1479. X        printf("must have \"%s\"", r->regmust);
  1480. X    printf("\n");
  1481. X}
  1482. X
  1483. X/*
  1484. X - regprop - printable representation of opcode
  1485. X */
  1486. Xstatic char *
  1487. Xregprop(op)
  1488. Xchar *op;
  1489. X{
  1490. X    register char *p;
  1491. X    static char buf[50];
  1492. X
  1493. X    (void) strcpy(buf, ":");
  1494. X
  1495. X    switch (OP(op)) {
  1496. X    case BOL:
  1497. X        p = "BOL";
  1498. X        break;
  1499. X    case EOL:
  1500. X        p = "EOL";
  1501. X        break;
  1502. X    case ANY:
  1503. X        p = "ANY";
  1504. X        break;
  1505. X    case ANYOF:
  1506. X        p = "ANYOF";
  1507. X        break;
  1508. X    case ANYBUT:
  1509. X        p = "ANYBUT";
  1510. X        break;
  1511. X    case BRANCH:
  1512. X        p = "BRANCH";
  1513. X        break;
  1514. X    case EXACTLY:
  1515. X        p = "EXACTLY";
  1516. X        break;
  1517. X    case NOTHING:
  1518. X        p = "NOTHING";
  1519. X        break;
  1520. X    case BACK:
  1521. X        p = "BACK";
  1522. X        break;
  1523. X    case END:
  1524. X        p = "END";
  1525. X        break;
  1526. X    case OPEN+1:
  1527. X    case OPEN+2:
  1528. X    case OPEN+3:
  1529. X    case OPEN+4:
  1530. X    case OPEN+5:
  1531. X    case OPEN+6:
  1532. X    case OPEN+7:
  1533. X    case OPEN+8:
  1534. X    case OPEN+9:
  1535. X        sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
  1536. X        p = NULL;
  1537. X        break;
  1538. X    case CLOSE+1:
  1539. X    case CLOSE+2:
  1540. X    case CLOSE+3:
  1541. X    case CLOSE+4:
  1542. X    case CLOSE+5:
  1543. X    case CLOSE+6:
  1544. X    case CLOSE+7:
  1545. X    case CLOSE+8:
  1546. X    case CLOSE+9:
  1547. X        sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
  1548. X        p = NULL;
  1549. X        break;
  1550. X    case STAR:
  1551. X        p = "STAR";
  1552. X        break;
  1553. X    case PLUS:
  1554. X        p = "PLUS";
  1555. X        break;
  1556. X    default:
  1557. X        regerror("corrupted opcode");
  1558. X        break;
  1559. X    }
  1560. X    if (p != NULL)
  1561. X        (void) strcat(buf, p);
  1562. X    return(buf);
  1563. X}
  1564. X#endif
  1565. X
  1566. X/*
  1567. X * The following is provided for those people who do not have strcspn() in
  1568. X * their C libraries.  They should get off their butts and do something
  1569. X * about it; at least one public-domain implementation of those (highly
  1570. X * useful) string routines has been published on Usenet.
  1571. X */
  1572. X#ifdef STRCSPN
  1573. X/*
  1574. X * strcspn - find length of initial segment of s1 consisting entirely
  1575. X * of characters not from s2
  1576. X */
  1577. X
  1578. Xstatic int
  1579. Xstrcspn(s1, s2)
  1580. Xchar *s1;
  1581. Xchar *s2;
  1582. X{
  1583. X    register char *scan1;
  1584. X    register char *scan2;
  1585. X    register int count;
  1586. X
  1587. X    count = 0;
  1588. X    for (scan1 = s1; *scan1 != '\0'; scan1++) {
  1589. X        for (scan2 = s2; *scan2 != '\0';)    /* ++ moved down. */
  1590. X            if (*scan1 == *scan2++)
  1591. X                return(count);
  1592. X        count++;
  1593. X    }
  1594. X    return(count);
  1595. X}
  1596. X#endif
  1597. X
  1598. Xint
  1599. Xcstrncmp(s1, s2, n)
  1600. Xchar    *s1, *s2;
  1601. Xint    n;
  1602. X{
  1603. X    char    *p, *S1, *S2, *strsave();
  1604. X    int    rval;
  1605. X
  1606. X    if (!reg_ic)
  1607. X        return (strncmp(s1, s2, n));
  1608. X
  1609. X    S1 = strsave(s1);
  1610. X    S2 = strsave(s2);
  1611. X
  1612. X    for (p = S1; *p ;p++)
  1613. X        if (islower(*p))
  1614. X            *p = toupper(*p);
  1615. X
  1616. X    for (p = S2; *p ;p++)
  1617. X        if (islower(*p))
  1618. X            *p = toupper(*p);
  1619. X
  1620. X    rval = strncmp(S1, S2, n);
  1621. X
  1622. X    free(S1);
  1623. X    free(S2);
  1624. X
  1625. X    return rval;
  1626. X}
  1627. X
  1628. Xchar *
  1629. Xcstrchr(s, c)
  1630. Xchar    *s;
  1631. Xchar    c;
  1632. X{
  1633. X    char    *p;
  1634. X
  1635. X    for (p = s; *p ;p++) {
  1636. X        if (mkup(*p) == mkup(c))
  1637. X            return p;
  1638. X    }
  1639. X    return NULL;
  1640. X}
  1641. !EOR!
  1642. echo extracting - regsub.c
  1643. sed 's/^X//' > regsub.c << '!EOR!'
  1644. X/* $Header: /nw/tony/src/stevie/src/RCS/regsub.c,v 1.4 89/03/11 22:43:30 tony Exp $
  1645. X *
  1646. X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
  1647. X *
  1648. X * This is NOT the original regular expression code as written by
  1649. X * Henry Spencer. This code has been modified specifically for use
  1650. X * with the STEVIE editor, and should not be used apart from compiling
  1651. X * STEVIE. If you want a good regular expression library, get the
  1652. X * original code. The copyright notice that follows is from the
  1653. X * original.
  1654. X *
  1655. X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
  1656. X *
  1657. X * regsub
  1658. X *
  1659. X *    Copyright (c) 1986 by University of Toronto.
  1660. X *    Written by Henry Spencer.  Not derived from licensed software.
  1661. X *
  1662. X *    Permission is granted to anyone to use this software for any
  1663. X *    purpose on any computer system, and to redistribute it freely,
  1664. X *    subject to the following restrictions:
  1665. X *
  1666. X *    1. The author is not responsible for the consequences of use of
  1667. X *        this software, no matter how awful, even if they arise
  1668. X *        from defects in it.
  1669. X *
  1670. X *    2. The origin of this software must not be misrepresented, either
  1671. X *        by explicit claim or by omission.
  1672. X *
  1673. X *    3. Altered versions must be plainly marked as such, and must not
  1674. X *        be misrepresented as being the original software.
  1675. X *
  1676. X */
  1677. X
  1678. X#include <stdio.h>
  1679. X#include "regexp.h"
  1680. X#include "regmagic.h"
  1681. X
  1682. X#ifndef CHARBITS
  1683. X#define    UCHARAT(p)    ((int)*(unsigned char *)(p))
  1684. X#else
  1685. X#define    UCHARAT(p)    ((int)*(p)&CHARBITS)
  1686. X#endif
  1687. X
  1688. X/*
  1689. X - regsub - perform substitutions after a regexp match
  1690. X */
  1691. Xvoid
  1692. Xregsub(prog, source, dest)
  1693. Xregexp *prog;
  1694. Xchar *source;
  1695. Xchar *dest;
  1696. X{
  1697. X    register char *src;
  1698. X    register char *dst;
  1699. X    register char c;
  1700. X    register int no;
  1701. X    register int len;
  1702. X    extern char *strncpy();
  1703. X
  1704. X    if (prog == NULL || source == NULL || dest == NULL) {
  1705. X        regerror("NULL parm to regsub");
  1706. X        return;
  1707. X    }
  1708. X    if (UCHARAT(prog->program) != MAGIC) {
  1709. X        regerror("damaged regexp fed to regsub");
  1710. X        return;
  1711. X    }
  1712. X
  1713. X    src = source;
  1714. X    dst = dest;
  1715. X    while ((c = *src++) != '\0') {
  1716. X        if (c == '&')
  1717. X            no = 0;
  1718. X        else if (c == '\\' && '0' <= *src && *src <= '9')
  1719. X            no = *src++ - '0';
  1720. X        else
  1721. X            no = -1;
  1722. X         if (no < 0) {    /* Ordinary character. */
  1723. X             if (c == '\\' && (*src == '\\' || *src == '&'))
  1724. X                 c = *src++;
  1725. X             *dst++ = c;
  1726. X         } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
  1727. X            len = prog->endp[no] - prog->startp[no];
  1728. X            (void) strncpy(dst, prog->startp[no], len);
  1729. X            dst += len;
  1730. X            if (len != 0 && *(dst-1) == '\0') {    /* strncpy hit NUL. */
  1731. X                regerror("damaged match string");
  1732. X                return;
  1733. X            }
  1734. X        }
  1735. X    }
  1736. X    *dst++ = '\0';
  1737. X}
  1738. !EOR!
  1739. echo extracting - screen.c
  1740. sed 's/^X//' > screen.c << '!EOR!'
  1741. X/* $Header: /nw/tony/src/stevie/src/RCS/screen.c,v 1.8 89/08/02 09:26:33 tony Exp $
  1742. X *
  1743. X * Routines to manipulate the screen representations.
  1744. X */
  1745. X
  1746. X#include "stevie.h"
  1747. X
  1748. X/*
  1749. X * This gets set if we ignored an update request while input was pending.
  1750. X * We check this when the input is drained to see if the screen should be
  1751. X * updated.
  1752. X */
  1753. Xbool_t    need_redraw = FALSE;
  1754. X
  1755. X/*
  1756. X * The following variable is set (in filetonext) to the number of physical
  1757. X * lines taken by the line the cursor is on. We use this to avoid extra
  1758. X * calls to plines(). The optimized routines lfiletonext() and lnexttoscreen()
  1759. X * make sure that the size of the cursor line hasn't changed. If so, lines
  1760. X * below the cursor will move up or down and we need to call the routines
  1761. X * filetonext() and nexttoscreen() to examine the entire screen.
  1762. X */
  1763. Xstatic    int    Cline_size;    /* size (in rows) of the cursor line */
  1764. Xstatic    int    Cline_row;    /* starting row of the cursor line */
  1765. X
  1766. Xstatic    char    *mkline();    /* calculate line string for "number" mode */
  1767. X
  1768. X/*
  1769. X * filetonext()
  1770. X *
  1771. X * Based on the current value of Topchar, transfer a screenfull of
  1772. X * stuff from Filemem to Nextscreen, and update Botchar.
  1773. X */
  1774. X
  1775. Xstatic void
  1776. Xfiletonext()
  1777. X{
  1778. X    register int    row, col;
  1779. X    register char    *screenp = Nextscreen;
  1780. X    LPTR    memp;
  1781. X    LPTR    save;            /* save pos. in case line won't fit */
  1782. X    register char    *endscreen;
  1783. X    register char    *nextrow;
  1784. X    char    extra[16];
  1785. X    int    nextra = 0;
  1786. X    register int    c;
  1787. X    int    n;
  1788. X    bool_t    done;        /* if TRUE, we hit the end of the file */
  1789. X    bool_t    didline;    /* if TRUE, we finished the last line */
  1790. X    int    srow;        /* starting row of the current line */
  1791. X    int    lno;        /* number of the line we're doing */
  1792. X    int    coff;        /* column offset */
  1793. X
  1794. X    coff = P(P_NU) ? 8 : 0;
  1795. X
  1796. X    save = memp = *Topchar;
  1797. X
  1798. X    if (P(P_NU))
  1799. X        lno = cntllines(Filemem, Topchar);
  1800. X
  1801. X    /*
  1802. X     * The number of rows shown is Rows-1.
  1803. X     * The last line is the status/command line.
  1804. X     */
  1805. X    endscreen = &screenp[(Rows-1)*Columns];
  1806. X
  1807. X    done = didline = FALSE;
  1808. X    srow = row = col = 0;
  1809. X    /*
  1810. X     * We go one past the end of the screen so we can find out if the
  1811. X     * last line fit on the screen or not.
  1812. X     */
  1813. X    while ( screenp <= endscreen && !done) {
  1814. X
  1815. X
  1816. X        if (P(P_NU) && col == 0 && memp.index == 0) {
  1817. X            strcpy(extra, mkline(lno++));
  1818. X            nextra = 8;
  1819. X        }
  1820. X
  1821. X        /* Get the next character to put on the screen. */
  1822. X
  1823. X        /* The 'extra' array contains the extra stuff that is */
  1824. X        /* inserted to represent special characters (tabs, and */
  1825. X        /* other non-printable stuff.  The order in the 'extra' */
  1826. X        /* array is reversed. */
  1827. X
  1828. X        if ( nextra > 0 )
  1829. X            c = extra[--nextra];
  1830. X        else {
  1831. X            c = (unsigned)(0xff & gchar(&memp));
  1832. X            if (inc(&memp) == -1)
  1833. X                done = 1;
  1834. X            /* when getting a character from the file, we */
  1835. X            /* may have to turn it into something else on */
  1836. X            /* the way to putting it into 'Nextscreen'. */
  1837. X            if ( c == TAB && !P(P_LS) ) {
  1838. X                strcpy(extra,"        ");
  1839. X                /* tab amount depends on current column */
  1840. X                nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
  1841. X                c = ' ';
  1842. X            }
  1843. X            else if ( c == NUL && P(P_LS) ) {
  1844. X                extra[0] = NUL;
  1845. X                nextra = 1;
  1846. X                c = '$';
  1847. X            } else if ( (n = chars[c].ch_size) > 1 ) {
  1848. X                char *p;
  1849. X                nextra = 0;
  1850. X                p = chars[c].ch_str;
  1851. X                /* copy 'ch-str'ing into 'extra' in reverse */
  1852. X                while ( n > 1 )
  1853. X                    extra[nextra++] = p[--n];
  1854. X                c = p[0];
  1855. X            }
  1856. X        }
  1857. X
  1858. X        if (screenp == endscreen) {
  1859. X            /*
  1860. X             * We're one past the end of the screen. If the
  1861. X             * current character is null, then we really did
  1862. X             * finish, so set didline = TRUE. In either case,
  1863. X             * break out because we're done.
  1864. X             */
  1865. X            dec(&memp);
  1866. X            if (memp.index != 0 && c == NUL) {
  1867. X                didline = TRUE;
  1868. X                inc(&memp);
  1869. X            }
  1870. X            break;
  1871. X        }
  1872. X
  1873. X        if ( c == NUL ) {
  1874. X            srow = ++row;
  1875. X            /*
  1876. X             * Save this position in case the next line won't
  1877. X             * fit on the screen completely.
  1878. X             */
  1879. X            save = memp;
  1880. X            /* get pointer to start of next row */
  1881. X            nextrow = &Nextscreen[row*Columns];
  1882. X            /* blank out the rest of this row */
  1883. X            while ( screenp != nextrow )
  1884. X                *screenp++ = ' ';
  1885. X            col = 0;
  1886. X            continue;
  1887. X        }
  1888. X        if ( col >= Columns ) {
  1889. X            row++;
  1890. X            col = 0;
  1891. X        }
  1892. X        /* store the character in Nextscreen */
  1893. X        *screenp++ = c;
  1894. X        col++;
  1895. X    }
  1896. X    /*
  1897. X     * If we didn't hit the end of the file, and we didn't finish
  1898. X     * the last line we were working on, then the line didn't fit.
  1899. X     */
  1900. X    if (!done && !didline) {
  1901. X        /*
  1902. X         * Clear the rest of the screen and mark the unused lines.
  1903. X         */
  1904. X        screenp = &Nextscreen[srow * Columns];
  1905. X        while (screenp < endscreen)
  1906. X            *screenp++ = ' ';
  1907. X        for (; srow < (Rows-1) ;srow++)
  1908. X            Nextscreen[srow * Columns] = '@';
  1909. X        *Botchar = save;
  1910. X        return;
  1911. X    }
  1912. X    /* make sure the rest of the screen is blank */
  1913. X    while ( screenp < endscreen )
  1914. X        *screenp++ = ' ';
  1915. X    /* put '~'s on rows that aren't part of the file. */
  1916. X    if ( col != 0 )
  1917. X        row++;
  1918. X    while ( row < Rows ) {
  1919. X        Nextscreen[row*Columns] = '~';
  1920. X        row++;
  1921. X    }
  1922. X    if (done)    /* we hit the end of the file */
  1923. X        *Botchar = *Fileend;
  1924. X    else
  1925. X        *Botchar = memp;    /* FIX - prev? */
  1926. X}
  1927. X
  1928. X/*
  1929. X * nexttoscreen
  1930. X *
  1931. X * Transfer the contents of Nextscreen to the screen, using Realscreen
  1932. X * to avoid unnecessary output.
  1933. X */
  1934. Xstatic void
  1935. Xnexttoscreen()
  1936. X{
  1937. X    register char    *np = Nextscreen;
  1938. X    register char    *rp = Realscreen;
  1939. X    register char    *endscreen;
  1940. X    register int    row = 0, col = 0;
  1941. X    int    gorow = -1, gocol = -1;
  1942. X
  1943. X    if (anyinput()) {
  1944. X        need_redraw = TRUE;
  1945. X        return;
  1946. X    }
  1947. X
  1948. X    endscreen = &np[(Rows-1)*Columns];
  1949. X
  1950. X    CUROFF;        /* disable cursor */
  1951. X
  1952. X    for ( ; np < endscreen ; np++,rp++ ) {
  1953. X        /* If desired screen (contents of Nextscreen) does not */
  1954. X        /* match what's really there, put it there. */
  1955. X        if ( *np != *rp ) {
  1956. X            /* if we are positioned at the right place, */
  1957. X            /* we don't have to use windgoto(). */
  1958. X            if (gocol != col || gorow != row) {
  1959. X                /*
  1960. X                 * If we're just off by one, don't send
  1961. X                 * an entire esc. seq. (this happens a lot!)
  1962. X                 */
  1963. X                if (gorow == row && gocol+1 == col) {
  1964. X                    outchar(*(np-1));
  1965. X                    gocol++;
  1966. X                } else
  1967. X                    windgoto(gorow=row,gocol=col);
  1968. X            }
  1969. X            outchar(*rp = *np);
  1970. X            gocol++;
  1971. X        }
  1972. X        if ( ++col >= Columns ) {
  1973. X            col = 0;
  1974. X            row++;
  1975. X        }
  1976. X    }
  1977. X    CURON;        /* enable cursor again */
  1978. X}
  1979. X
  1980. X/*
  1981. X * lfiletonext() - like filetonext() but only for cursor line
  1982. X *
  1983. X * Returns true if the size of the cursor line (in rows) hasn't changed.
  1984. X * This determines whether or not we need to call filetonext() to examine
  1985. X * the entire screen for changes.
  1986. X */
  1987. Xstatic bool_t
  1988. Xlfiletonext()
  1989. X{
  1990. X    register int    row, col;
  1991. X    register char    *screenp;
  1992. X    LPTR    memp;
  1993. X    register char    *nextrow;
  1994. X    char    extra[16];
  1995. X    int    nextra = 0;
  1996. X    register int    c;
  1997. X    int    n;
  1998. X    bool_t    eof;
  1999. X    int    lno;        /* number of the line we're doing */
  2000. X    int    coff;        /* column offset */
  2001. X
  2002. X    coff = P(P_NU) ? 8 : 0;
  2003. X
  2004. X    /*
  2005. X     * This should be done more efficiently.
  2006. X     */
  2007. X    if (P(P_NU))
  2008. X        lno = cntllines(Filemem, Curschar);
  2009. X
  2010. X    screenp = Nextscreen + (Cline_row * Columns);
  2011. X
  2012. X    memp = *Curschar;
  2013. X    memp.index = 0;
  2014. X
  2015. X    eof = FALSE;
  2016. X    col = 0;
  2017. X    row = Cline_row;
  2018. X
  2019. X    while (!eof) {
  2020. X
  2021. X        if (P(P_NU) && col == 0 && memp.index == 0) {
  2022. X            strcpy(extra, mkline(lno));
  2023. X            nextra = 8;
  2024. X        }
  2025. X
  2026. X        /* Get the next character to put on the screen. */
  2027. X
  2028. X        /* The 'extra' array contains the extra stuff that is */
  2029. X        /* inserted to represent special characters (tabs, and */
  2030. X        /* other non-printable stuff.  The order in the 'extra' */
  2031. X        /* array is reversed. */
  2032. X
  2033. X        if ( nextra > 0 )
  2034. X            c = extra[--nextra];
  2035. X        else {
  2036. X            c = (unsigned)(0xff & gchar(&memp));
  2037. X            if (inc(&memp) == -1)
  2038. X                eof = TRUE;
  2039. X            /* when getting a character from the file, we */
  2040. X            /* may have to turn it into something else on */
  2041. X            /* the way to putting it into 'Nextscreen'. */
  2042. X            if ( c == TAB && !P(P_LS) ) {
  2043. X                strcpy(extra,"        ");
  2044. X                /* tab amount depends on current column */
  2045. X                nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
  2046. X                c = ' ';
  2047. X            } else if ( c == NUL && P(P_LS) ) {
  2048. X                extra[0] = NUL;
  2049. X                nextra = 1;
  2050. X                c = '$';
  2051. X            } else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
  2052. X                char *p;
  2053. X                nextra = 0;
  2054. X                p = chars[c].ch_str;
  2055. X                /* copy 'ch-str'ing into 'extra' in reverse */
  2056. X                while ( n > 1 )
  2057. X                    extra[nextra++] = p[--n];
  2058. X                c = p[0];
  2059. X            }
  2060. X        }
  2061. X
  2062. X        if ( c == NUL ) {
  2063. X            row++;
  2064. X            /* get pointer to start of next row */
  2065. X            nextrow = &Nextscreen[row*Columns];
  2066. X            /* blank out the rest of this row */
  2067. X            while ( screenp != nextrow )
  2068. X                *screenp++ = ' ';
  2069. X            col = 0;
  2070. X            break;
  2071. X        }
  2072. X
  2073. X        if ( col >= Columns ) {
  2074. X            row++;
  2075. X            col = 0;
  2076. X        }
  2077. X        /* store the character in Nextscreen */
  2078. X        *screenp++ = c;
  2079. X        col++;
  2080. X    }
  2081. X    return ((row - Cline_row) == Cline_size);
  2082. X}
  2083. X
  2084. X/*
  2085. X * lnexttoscreen
  2086. X *
  2087. X * Like nexttoscreen() but only for the cursor line.
  2088. X */
  2089. Xstatic void
  2090. Xlnexttoscreen()
  2091. X{
  2092. X    register char    *np = Nextscreen + (Cline_row * Columns);
  2093. X    register char    *rp = Realscreen + (Cline_row * Columns);
  2094. X    register char    *endline;
  2095. X    register int    row, col;
  2096. X    int    gorow = -1, gocol = -1;
  2097. X
  2098. X    if (anyinput()) {
  2099. X        need_redraw = TRUE;
  2100. X        return;
  2101. X    }
  2102. X
  2103. X    endline = np + (Cline_size * Columns);
  2104. X
  2105. X    row = Cline_row;
  2106. X    col = 0;
  2107. X
  2108. X    CUROFF;        /* disable cursor */
  2109. X
  2110. X    for ( ; np < endline ; np++,rp++ ) {
  2111. X        /* If desired screen (contents of Nextscreen) does not */
  2112. X        /* match what's really there, put it there. */
  2113. X        if ( *np != *rp ) {
  2114. X            /* if we are positioned at the right place, */
  2115. X            /* we don't have to use windgoto(). */
  2116. X            if (gocol != col || gorow != row) {
  2117. X                /*
  2118. X                 * If we're just off by one, don't send
  2119. X                 * an entire esc. seq. (this happens a lot!)
  2120. X                 */
  2121. X                if (gorow == row && gocol+1 == col) {
  2122. X                    outchar(*(np-1));
  2123. X                    gocol++;
  2124. X                } else
  2125. X                    windgoto(gorow=row,gocol=col);
  2126. X            }
  2127. X            outchar(*rp = *np);
  2128. X            gocol++;
  2129. X        }
  2130. X        if ( ++col >= Columns ) {
  2131. X            col = 0;
  2132. X            row++;
  2133. X        }
  2134. X    }
  2135. X    CURON;        /* enable cursor again */
  2136. X}
  2137. X
  2138. Xstatic char *
  2139. Xmkline(n)
  2140. Xregister int    n;
  2141. X{
  2142. X    static    char    lbuf[9];
  2143. X    register int    i = 2;
  2144. X
  2145. X    strcpy(lbuf, "        ");
  2146. X
  2147. X    lbuf[i++] = (n % 10) + '0';
  2148. X    n /= 10;
  2149. X    if (n != 0) {
  2150. X        lbuf[i++] = (n % 10) + '0';
  2151. X        n /= 10;
  2152. X    }
  2153. X    if (n != 0) {
  2154. X        lbuf[i++] = (n % 10) + '0';
  2155. X        n /= 10;
  2156. X    }
  2157. X    if (n != 0) {
  2158. X        lbuf[i++] = (n % 10) + '0';
  2159. X        n /= 10;
  2160. X    }
  2161. X    if (n != 0) {
  2162. X        lbuf[i++] = (n % 10) + '0';
  2163. X        n /= 10;
  2164. X    }
  2165. X    return lbuf;
  2166. X}
  2167. X
  2168. X/*
  2169. X * updateline() - update the line the cursor is on
  2170. X *
  2171. X * Updateline() is called after changes that only affect the line that
  2172. X * the cursor is on. This improves performance tremendously for normal
  2173. X * insert mode operation. The only thing we have to watch for is when
  2174. X * the cursor line grows or shrinks around a row boundary. This means
  2175. X * we have to repaint other parts of the screen appropriately. If
  2176. X * lfiletonext() returns FALSE, the size of the cursor line (in rows)
  2177. X * has changed and we have to call updatescreen() to do a complete job.
  2178. X */
  2179. Xvoid
  2180. Xupdateline()
  2181. X{
  2182. X    if (!lfiletonext())
  2183. X        updatescreen();    /* bag it, do the whole screen */
  2184. X    else
  2185. X        lnexttoscreen();
  2186. X}
  2187. X
  2188. Xvoid
  2189. Xupdatescreen()
  2190. X{
  2191. X    extern    bool_t    interactive;
  2192. X
  2193. X    if (interactive) {
  2194. X        filetonext();
  2195. X        nexttoscreen();
  2196. X    }
  2197. X}
  2198. X
  2199. X/*
  2200. X * prt_line() - print the given line
  2201. X */
  2202. Xvoid
  2203. Xprt_line(s)
  2204. Xchar    *s;
  2205. X{
  2206. X    register int    si = 0;
  2207. X    register int    c;
  2208. X    register int    col = 0;
  2209. X
  2210. X    char    extra[16];
  2211. X    int    nextra = 0;
  2212. X    int    n;
  2213. X
  2214. X    for (;;) {
  2215. X
  2216. X        if ( nextra > 0 )
  2217. X            c = extra[--nextra];
  2218. X        else {
  2219. X            c = s[si++];
  2220. X            if ( c == TAB && !P(P_LS) ) {
  2221. X                strcpy(extra, "        ");
  2222. X                /* tab amount depends on current column */
  2223. X                nextra = (P(P_TS) - 1) - col%P(P_TS);
  2224. X                c = ' ';
  2225. X            } else if ( c == NUL && P(P_LS) ) {
  2226. X                extra[0] = NUL;
  2227. X                nextra = 1;
  2228. X                c = '$';
  2229. X            } else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
  2230. X                char    *p;
  2231. X
  2232. X                nextra = 0;
  2233. X                p = chars[c].ch_str;
  2234. X                /* copy 'ch-str'ing into 'extra' in reverse */
  2235. X                while ( n > 1 )
  2236. X                    extra[nextra++] = p[--n];
  2237. X                c = p[0];
  2238. X            }
  2239. X        }
  2240. X
  2241. X        if ( c == NUL )
  2242. X            break;
  2243. X
  2244. X        outchar(c);
  2245. X        col++;
  2246. X    }
  2247. X}
  2248. X
  2249. Xvoid
  2250. Xscreenclear()
  2251. X{
  2252. X    register char    *rp, *np;
  2253. X    register char    *end;
  2254. X
  2255. X    CLS;        /* clear the display */
  2256. X
  2257. X    rp  = Realscreen;
  2258. X    end = Realscreen + Rows * Columns;
  2259. X    np  = Nextscreen;
  2260. X
  2261. X    /* blank out the stored screens */
  2262. X    while (rp != end)
  2263. X        *rp++ = *np++ = ' ';
  2264. X}
  2265. X
  2266. Xvoid
  2267. Xcursupdate()
  2268. X{
  2269. X    register LPTR    *p;
  2270. X    register int    icnt, c, nlines;
  2271. X    register int    i;
  2272. X    int    didinc;
  2273. X
  2274. X    if (bufempty()) {        /* special case - file is empty */
  2275. X        *Topchar  = *Filemem;
  2276. X        *Curschar = *Filemem;
  2277. X    } else if ( LINEOF(Curschar) < LINEOF(Topchar) ) {
  2278. X        nlines = cntllines(Curschar,Topchar);
  2279. X        /* if the cursor is above the top of */
  2280. X        /* the screen, put it at the top of the screen.. */
  2281. X        *Topchar = *Curschar;
  2282. X        Topchar->index = 0;
  2283. X        /* ... and, if we weren't very close to begin with, */
  2284. X        /* we scroll so that the line is close to the middle. */
  2285. X        if ( nlines > Rows/3 ) {
  2286. X            for (i=0, p = Topchar; i < Rows/3 ;i++, *Topchar = *p)
  2287. X                if ((p = prevline(p)) == NULL)
  2288. X                    break;
  2289. X        } else
  2290. X            s_ins(0, nlines-1);
  2291. X        updatescreen();
  2292. X    }
  2293. X    else if (LINEOF(Curschar) >= LINEOF(Botchar)) {
  2294. X        nlines = cntllines(Botchar,Curschar);
  2295. X        /* If the cursor is off the bottom of the screen, */
  2296. X        /* put it at the top of the screen.. */
  2297. X        /* ... and back up */
  2298. X        if ( nlines > Rows/3 ) {
  2299. X            p = Curschar;
  2300. X            for (i=0; i < (2*Rows)/3 ;i++)
  2301. X                if ((p = prevline(p)) == NULL)
  2302. X                    break;
  2303. X            *Topchar = *p;
  2304. X        } else {
  2305. X            scrollup(nlines);
  2306. X        }
  2307. X        updatescreen();
  2308. X    }
  2309. X
  2310. X    Cursrow = Curscol = Cursvcol = 0;
  2311. X    for ( p=Topchar; p->linep != Curschar->linep ;p = nextline(p) )
  2312. X        Cursrow += plines(p);
  2313. X
  2314. X    Cline_row = Cursrow;
  2315. X    Cline_size = plines(p);
  2316. X
  2317. X    if (P(P_NU))
  2318. X        Curscol = 8;
  2319. X
  2320. X    for (i=0; i <= Curschar->index ;i++) {
  2321. X        c = Curschar->linep->s[i];
  2322. X        /* A tab gets expanded, depending on the current column */
  2323. X        if ( c == TAB && !P(P_LS) )
  2324. X            icnt = P(P_TS) - (Cursvcol % P(P_TS));
  2325. X        else
  2326. X            icnt = chars[(unsigned)(c & 0xff)].ch_size;
  2327. X        Curscol += icnt;
  2328. X        Cursvcol += icnt;
  2329. X        if ( Curscol >= Columns ) {
  2330. X            Curscol -= Columns;
  2331. X            Cursrow++;
  2332. X            didinc = TRUE;
  2333. X        }
  2334. X        else
  2335. X            didinc = FALSE;
  2336. X    }
  2337. X    if (didinc)
  2338. X        Cursrow--;
  2339. X
  2340. X    if (c == TAB && State == NORMAL && !P(P_LS)) {
  2341. X        Curscol--;
  2342. X        Cursvcol--;
  2343. X    } else {
  2344. X        Curscol -= icnt;
  2345. X        Cursvcol -= icnt;
  2346. X    }
  2347. X    if (Curscol < 0)
  2348. X        Curscol += Columns;
  2349. X
  2350. X    if (set_want_col) {
  2351. X        Curswant = Cursvcol;
  2352. X        set_want_col = FALSE;
  2353. X    }
  2354. X}
  2355. X
  2356. X/*
  2357. X * The rest of the routines in this file perform screen manipulations.
  2358. X * The given operation is performed physically on the screen. The
  2359. X * corresponding change is also made to the internal screen image.
  2360. X * In this way, the editor anticipates the effect of editing changes
  2361. X * on the appearance of the screen. That way, when we call screenupdate
  2362. X * a complete redraw isn't usually necessary. Another advantage is that
  2363. X * we can keep adding code to anticipate screen changes, and in the
  2364. X * meantime, everything still works.
  2365. X */
  2366. X
  2367. X/*
  2368. X * s_ins(row, nlines) - insert 'nlines' lines at 'row'
  2369. X */
  2370. Xvoid
  2371. Xs_ins(row, nlines)
  2372. Xint    row;
  2373. Xint    nlines;
  2374. X{
  2375. X    register char    *s, *d;        /* src & dest for block copy */
  2376. X    register char    *e;        /* end point for copy */
  2377. X    register int    i;
  2378. X
  2379. X    if ( ! CANIL )        /* can't do it */
  2380. X        return;
  2381. X
  2382. X    /*
  2383. X     * It "looks" better if we do all the inserts at once
  2384. X     */
  2385. X    SAVCUR;            /* save position */
  2386. X    windgoto(row, 0);
  2387. X
  2388. X    CRTIL( row, nlines );
  2389. X
  2390. X    windgoto(Rows-1, 0);    /* delete any garbage that may have */
  2391. X    CLEOL;            /* been shifted to the bottom line */
  2392. X    RESCUR;            /* restore the cursor position */
  2393. X
  2394. X    /*
  2395. X     * Now do a block move to update the internal screen image
  2396. X     */
  2397. X    d = Realscreen + (Columns * (Rows - 1)) - 1;
  2398. X    s = d - (Columns * nlines);
  2399. X    e = Realscreen + (Columns * row);
  2400. X
  2401. X    while (s >= e)
  2402. X        *d-- = *s--;
  2403. X
  2404. X    /*
  2405. X     * Clear the inserted lines
  2406. X     */
  2407. X    s = Realscreen + (row * Columns);
  2408. X    e = s + (nlines * Columns);
  2409. X    while (s < e)
  2410. X        *s++ = ' ';
  2411. X}
  2412. X
  2413. X/*
  2414. X * s_del(row, nlines) - delete 'nlines' lines at 'row'
  2415. X */
  2416. Xvoid
  2417. Xs_del(row, nlines)
  2418. Xint    row;
  2419. Xint    nlines;
  2420. X{
  2421. X    register char    *s, *d, *e;
  2422. X    register int    i;
  2423. X
  2424. X#ifndef BIOS
  2425. X    if ( ! CANDL ) return;        /* can't do it */
  2426. X#endif
  2427. X
  2428. X    /* delete the lines */
  2429. X    SAVCUR;                /* save position */
  2430. X
  2431. X    windgoto (Rows-1, 0);        /* go to status line */
  2432. X    CLEOL;                /* Clear it */
  2433. X    windgoto (row, 0);        /* Go to 1st line-to-del */
  2434. X    CRTDL( row, nlines );        /* Delete the lines */
  2435. X    RESCUR;                /* Restore the cursor */
  2436. X
  2437. X    /*
  2438. X     * do a block move to update the internal image
  2439. X     */
  2440. X    d = Realscreen + (row * Columns);
  2441. X    s = d + (nlines * Columns);
  2442. X    e = Realscreen + ((Rows - 1) * Columns);
  2443. X
  2444. X    while (s < e)
  2445. X        *d++ = *s++;
  2446. X
  2447. X    while (d < e)        /* clear the lines at the bottom */
  2448. X        *d++ = ' ';
  2449. X}
  2450. !EOR!
  2451.  
  2452.  
  2453.