home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume18 / perl / part18 < prev    next >
Internet Message Format  |  1991-04-16  |  51KB

  1. From: lwall@netlabs.com (Larry Wall)
  2. Newsgroups: comp.sources.misc
  3. Subject: v18i036:  perl - The perl programming language, Part18/36
  4. Message-ID: <1991Apr16.185459.988@sparky.IMD.Sterling.COM>
  5. Date: 16 Apr 91 18:54:59 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: 309987de ade986d6 28cfbf28 cabc3a99
  8.  
  9. Submitted-by: Larry Wall <lwall@netlabs.com>
  10. Posting-number: Volume 18, Issue 36
  11. Archive-name: perl/part18
  12.  
  13. [There are 36 kits for perl version 4.0.]
  14.  
  15. #! /bin/sh
  16.  
  17. # Make a new directory for the perl sources, cd to it, and run kits 1
  18. # thru 36 through sh.  When all 36 kits have been run, read README.
  19.  
  20. echo "This is perl 4.0 kit 18 (of 36).  If kit 18 is complete, the line"
  21. echo '"'"End of kit 18 (of 36)"'" will echo at the end.'
  22. echo ""
  23. export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
  24. mkdir eg eg/scan emacs 2>/dev/null
  25. echo Extracting regcomp.c
  26. sed >regcomp.c <<'!STUFFY!FUNK!' -e 's/X//'
  27. X/* NOTE: this is derived from Henry Spencer's regexp code, and should not
  28. X * confused with the original package (see point 3 below).  Thanks, Henry!
  29. X */
  30. X
  31. X/* Additional note: this code is very heavily munged from Henry's version
  32. X * in places.  In some spots I've traded clarity for efficiency, so don't
  33. X * blame Henry for some of the lack of readability.
  34. X */
  35. X
  36. X/* $RCSfile: regcomp.c,v $$Revision: 4.0.1.1 $$Date: 91/04/12 09:04:45 $
  37. X *
  38. X * $Log:    regcomp.c,v $
  39. X * Revision 4.0.1.1  91/04/12  09:04:45  lwall
  40. X * patch1: random cleanup in cpp namespace
  41. X * 
  42. X * Revision 4.0  91/03/20  01:39:01  lwall
  43. X * 4.0 baseline.
  44. X * 
  45. X */
  46. X
  47. X/*
  48. X * regcomp and regexec -- regsub and regerror are not used in perl
  49. X *
  50. X *    Copyright (c) 1986 by University of Toronto.
  51. X *    Written by Henry Spencer.  Not derived from licensed software.
  52. X *
  53. X *    Permission is granted to anyone to use this software for any
  54. X *    purpose on any computer system, and to redistribute it freely,
  55. X *    subject to the following restrictions:
  56. X *
  57. X *    1. The author is not responsible for the consequences of use of
  58. X *        this software, no matter how awful, even if they arise
  59. X *        from defects in it.
  60. X *
  61. X *    2. The origin of this software must not be misrepresented, either
  62. X *        by explicit claim or by omission.
  63. X *
  64. X *    3. Altered versions must be plainly marked as such, and must not
  65. X *        be misrepresented as being the original software.
  66. X *
  67. X *
  68. X ****    Alterations to Henry's code are...
  69. X ****
  70. X ****    Copyright (c) 1989, Larry Wall
  71. X ****
  72. X ****    You may distribute under the terms of the GNU General Public License
  73. X ****    as specified in the README file that comes with the perl 3.0 kit.
  74. X *
  75. X * Beware that some of this code is subtly aware of the way operator
  76. X * precedence is structured in regular expressions.  Serious changes in
  77. X * regular-expression syntax might require a total rethink.
  78. X */
  79. X#include "EXTERN.h"
  80. X#include "perl.h"
  81. X#include "INTERN.h"
  82. X#include "regcomp.h"
  83. X
  84. X#ifdef MSDOS
  85. X# if defined(BUGGY_MSC6)
  86. X /* MSC 6.00A breaks on op/regexp.t test 85 unless we turn this off */
  87. X # pragma optimize("a",off)
  88. X /* But MSC 6.00A is happy with 'w', for aliases only across function calls*/
  89. X # pragma optimize("w",on )
  90. X# endif /* BUGGY_MSC6 */
  91. X#endif /* MSDOS */
  92. X
  93. X#ifndef STATIC
  94. X#define    STATIC    static
  95. X#endif
  96. X
  97. X#define    ISMULT1(c)    ((c) == '*' || (c) == '+' || (c) == '?')
  98. X#define    ISMULT2(s)    ((*s) == '*' || (*s) == '+' || (*s) == '?' || \
  99. X    ((*s) == '{' && regcurly(s)))
  100. X#define    META    "^$.[()|?+*\\"
  101. X
  102. X#ifdef SPSTART
  103. X#undef SPSTART        /* dratted cpp namespace... */
  104. X#endif
  105. X/*
  106. X * Flags to be passed up and down.
  107. X */
  108. X#define    HASWIDTH    01    /* Known never to match null string. */
  109. X#define    SIMPLE        02    /* Simple enough to be STAR/PLUS operand. */
  110. X#define    SPSTART        04    /* Starts with * or +. */
  111. X#define    WORST        0    /* Worst case. */
  112. X
  113. X/*
  114. X * Global work variables for regcomp().
  115. X */
  116. Xstatic char *regprecomp;        /* uncompiled string. */
  117. Xstatic char *regparse;        /* Input-scan pointer. */
  118. Xstatic char *regxend;        /* End of input for compile */
  119. Xstatic int regnpar;        /* () count. */
  120. Xstatic char *regcode;        /* Code-emit pointer; ®dummy = don't. */
  121. Xstatic long regsize;        /* Code size. */
  122. Xstatic int regfold;
  123. Xstatic int regsawbracket;    /* Did we do {d,d} trick? */
  124. X
  125. X/*
  126. X * Forward declarations for regcomp()'s friends.
  127. X */
  128. XSTATIC int regcurly();
  129. XSTATIC char *reg();
  130. XSTATIC char *regbranch();
  131. XSTATIC char *regpiece();
  132. XSTATIC char *regatom();
  133. XSTATIC char *regclass();
  134. XSTATIC char *regnode();
  135. XSTATIC char *reganode();
  136. XSTATIC void regc();
  137. XSTATIC void reginsert();
  138. XSTATIC void regtail();
  139. XSTATIC void regoptail();
  140. X
  141. X/*
  142. X - regcomp - compile a regular expression into internal code
  143. X *
  144. X * We can't allocate space until we know how big the compiled form will be,
  145. X * but we can't compile it (and thus know how big it is) until we've got a
  146. X * place to put the code.  So we cheat:  we compile it twice, once with code
  147. X * generation turned off and size counting turned on, and once "for real".
  148. X * This also means that we don't allocate space until we are sure that the
  149. X * thing really will compile successfully, and we never have to move the
  150. X * code and thus invalidate pointers into it.  (Note that it has to be in
  151. X * one piece because free() must be able to free it all.) [NB: not true in perl]
  152. X *
  153. X * Beware that the optimization-preparation code in here knows about some
  154. X * of the structure of the compiled regexp.  [I'll say.]
  155. X */
  156. Xregexp *
  157. Xregcomp(exp,xend,fold)
  158. Xchar *exp;
  159. Xchar *xend;
  160. Xint fold;
  161. X{
  162. X    register regexp *r;
  163. X    register char *scan;
  164. X    register STR *longish;
  165. X    STR *longest;
  166. X    register int len;
  167. X    register char *first;
  168. X    int flags;
  169. X    int backish;
  170. X    int backest;
  171. X    int curback;
  172. X    extern char *safemalloc();
  173. X    extern char *savestr();
  174. X    int sawplus = 0;
  175. X
  176. X    if (exp == NULL)
  177. X        fatal("NULL regexp argument");
  178. X
  179. X    /* First pass: determine size, legality. */
  180. X    regfold = fold;
  181. X    regparse = exp;
  182. X    regxend = xend;
  183. X    regprecomp = nsavestr(exp,xend-exp);
  184. X    regsawbracket = 0;
  185. X    regnpar = 1;
  186. X    regsize = 0L;
  187. X    regcode = ®dummy;
  188. X    regc(MAGIC);
  189. X    if (reg(0, &flags) == NULL) {
  190. X        Safefree(regprecomp);
  191. X        regprecomp = Nullch;
  192. X        return(NULL);
  193. X    }
  194. X
  195. X    /* Small enough for pointer-storage convention? */
  196. X    if (regsize >= 32767L)        /* Probably could be 65535L. */
  197. X        FAIL("regexp too big");
  198. X
  199. X    /* Allocate space. */
  200. X    Newc(1001, r, sizeof(regexp) + (unsigned)regsize, char, regexp);
  201. X    if (r == NULL)
  202. X        FAIL("regexp out of space");
  203. X
  204. X    /* Second pass: emit code. */
  205. X    if (regsawbracket)
  206. X        bcopy(regprecomp,exp,xend-exp);
  207. X    r->precomp = regprecomp;
  208. X    r->subbase = NULL;
  209. X    regparse = exp;
  210. X    regnpar = 1;
  211. X    regcode = r->program;
  212. X    regc(MAGIC);
  213. X    if (reg(0, &flags) == NULL)
  214. X        return(NULL);
  215. X
  216. X    /* Dig out information for optimizations. */
  217. X    r->regstart = Nullstr;    /* Worst-case defaults. */
  218. X    r->reganch = 0;
  219. X    r->regmust = Nullstr;
  220. X    r->regback = -1;
  221. X    r->regstclass = Nullch;
  222. X    scan = r->program+1;            /* First BRANCH. */
  223. X    if (OP(regnext(scan)) == END) {/* Only one top-level choice. */
  224. X        scan = NEXTOPER(scan);
  225. X
  226. X        first = scan;
  227. X        while (OP(first) == OPEN ||
  228. X            (OP(first) == BRANCH && OP(regnext(first)) != BRANCH) ||
  229. X            (OP(first) == PLUS) ||
  230. X            (OP(first) == CURLY && ARG1(first) > 0) ) {
  231. X            if (OP(first) == PLUS)
  232. X                sawplus = 2;
  233. X            else
  234. X                first += regarglen[OP(first)];
  235. X            first = NEXTOPER(first);
  236. X        }
  237. X
  238. X        /* Starting-point info. */
  239. X        if (OP(first) == EXACTLY) {
  240. X            r->regstart =
  241. X                str_make(OPERAND(first)+1,*OPERAND(first));
  242. X            if (r->regstart->str_cur > !(sawstudy|fold))
  243. X                fbmcompile(r->regstart,fold);
  244. X        }
  245. X        else if ((exp = index(simple,OP(first))) && exp > simple)
  246. X            r->regstclass = first;
  247. X        else if (OP(first) == BOUND || OP(first) == NBOUND)
  248. X            r->regstclass = first;
  249. X        else if (OP(first) == BOL ||
  250. X            (OP(first) == STAR && OP(NEXTOPER(first)) == ANY) )
  251. X            r->reganch = 1;        /* kinda turn .* into ^.* */
  252. X        r->reganch |= sawplus;
  253. X
  254. X#ifdef DEBUGGING
  255. X        if (debug & 512)
  256. X            fprintf(stderr,"first %d next %d offset %d\n",
  257. X              OP(first), OP(NEXTOPER(first)), first - scan);
  258. X#endif
  259. X        /*
  260. X         * If there's something expensive in the r.e., find the
  261. X         * longest literal string that must appear and make it the
  262. X         * regmust.  Resolve ties in favor of later strings, since
  263. X         * the regstart check works with the beginning of the r.e.
  264. X         * and avoiding duplication strengthens checking.  Not a
  265. X         * strong reason, but sufficient in the absence of others.
  266. X         * [Now we resolve ties in favor of the earlier string if
  267. X         * it happens that curback has been invalidated, since the
  268. X         * earlier string may buy us something the later one won't.]
  269. X         */
  270. X        longish = str_make("",0);
  271. X        longest = str_make("",0);
  272. X        len = 0;
  273. X        curback = 0;
  274. X        backish = 0;
  275. X        backest = 0;
  276. X        while (OP(scan) != END) {
  277. X            if (OP(scan) == BRANCH) {
  278. X                if (OP(regnext(scan)) == BRANCH) {
  279. X                curback = -30000;
  280. X                while (OP(scan) == BRANCH)
  281. X                    scan = regnext(scan);
  282. X                }
  283. X                else    /* single branch is ok */
  284. X                scan = NEXTOPER(scan);
  285. X            }
  286. X            if (OP(scan) == EXACTLY) {
  287. X                char *t;
  288. X
  289. X                first = scan;
  290. X                while (OP(t = regnext(scan)) == CLOSE)
  291. X                scan = t;
  292. X                if (curback - backish == len) {
  293. X                str_ncat(longish, OPERAND(first)+1,
  294. X                    *OPERAND(first));
  295. X                len += *OPERAND(first);
  296. X                curback += *OPERAND(first);
  297. X                first = regnext(scan);
  298. X                }
  299. X                else if (*OPERAND(first) >= len + (curback >= 0)) {
  300. X                len = *OPERAND(first);
  301. X                str_nset(longish, OPERAND(first)+1,len);
  302. X                backish = curback;
  303. X                curback += len;
  304. X                first = regnext(scan);
  305. X                }
  306. X                else
  307. X                curback += *OPERAND(first);
  308. X            }
  309. X            else if (index(varies,OP(scan))) {
  310. X                curback = -30000;
  311. X                len = 0;
  312. X                if (longish->str_cur > longest->str_cur) {
  313. X                str_sset(longest,longish);
  314. X                backest = backish;
  315. X                }
  316. X                str_nset(longish,"",0);
  317. X            }
  318. X            else if (index(simple,OP(scan))) {
  319. X                curback++;
  320. X                len = 0;
  321. X                if (longish->str_cur > longest->str_cur) {
  322. X                str_sset(longest,longish);
  323. X                backest = backish;
  324. X                }
  325. X                str_nset(longish,"",0);
  326. X            }
  327. X            scan = regnext(scan);
  328. X        }
  329. X
  330. X        /* Prefer earlier on tie, unless we can tail match latter */
  331. X
  332. X        if (longish->str_cur + (OP(first) == EOL) > longest->str_cur) {
  333. X            str_sset(longest,longish);
  334. X            backest = backish;
  335. X        }
  336. X        else
  337. X            str_nset(longish,"",0);
  338. X        if (longest->str_cur
  339. X            &&
  340. X            (!r->regstart
  341. X             ||
  342. X             !fbminstr(r->regstart->str_ptr,
  343. X              r->regstart->str_ptr + r->regstart->str_cur,
  344. X              longest)
  345. X            )
  346. X           )
  347. X        {
  348. X            r->regmust = longest;
  349. X            if (backest < 0)
  350. X                backest = -1;
  351. X            r->regback = backest;
  352. X            if (longest->str_cur
  353. X              > !(sawstudy || fold || OP(first) == EOL) )
  354. X                fbmcompile(r->regmust,fold);
  355. X            r->regmust->str_u.str_useful = 100;
  356. X            if (OP(first) == EOL && longish->str_cur)
  357. X                r->regmust->str_pok |= SP_TAIL;
  358. X        }
  359. X        else {
  360. X            str_free(longest);
  361. X            longest = Nullstr;
  362. X        }
  363. X        str_free(longish);
  364. X    }
  365. X
  366. X    r->do_folding = fold;
  367. X    r->nparens = regnpar - 1;
  368. X    New(1002, r->startp, regnpar, char*);
  369. X    New(1002, r->endp, regnpar, char*);
  370. X#ifdef DEBUGGING
  371. X    if (debug & 512)
  372. X        regdump(r);
  373. X#endif
  374. X    return(r);
  375. X}
  376. X
  377. X/*
  378. X - reg - regular expression, i.e. main body or parenthesized thing
  379. X *
  380. X * Caller must absorb opening parenthesis.
  381. X *
  382. X * Combining parenthesis handling with the base level of regular expression
  383. X * is a trifle forced, but the need to tie the tails of the branches to what
  384. X * follows makes it hard to avoid.
  385. X */
  386. Xstatic char *
  387. Xreg(paren, flagp)
  388. Xint paren;            /* Parenthesized? */
  389. Xint *flagp;
  390. X{
  391. X    register char *ret;
  392. X    register char *br;
  393. X    register char *ender;
  394. X    register int parno;
  395. X    int flags;
  396. X
  397. X    *flagp = HASWIDTH;    /* Tentatively. */
  398. X
  399. X    /* Make an OPEN node, if parenthesized. */
  400. X    if (paren) {
  401. X        parno = regnpar;
  402. X        regnpar++;
  403. X        ret = reganode(OPEN, parno);
  404. X    } else
  405. X        ret = NULL;
  406. X
  407. X    /* Pick up the branches, linking them together. */
  408. X    br = regbranch(&flags);
  409. X    if (br == NULL)
  410. X        return(NULL);
  411. X    if (ret != NULL)
  412. X        regtail(ret, br);    /* OPEN -> first. */
  413. X    else
  414. X        ret = br;
  415. X    if (!(flags&HASWIDTH))
  416. X        *flagp &= ~HASWIDTH;
  417. X    *flagp |= flags&SPSTART;
  418. X    while (*regparse == '|') {
  419. X        regparse++;
  420. X        br = regbranch(&flags);
  421. X        if (br == NULL)
  422. X            return(NULL);
  423. X        regtail(ret, br);    /* BRANCH -> BRANCH. */
  424. X        if (!(flags&HASWIDTH))
  425. X            *flagp &= ~HASWIDTH;
  426. X        *flagp |= flags&SPSTART;
  427. X    }
  428. X
  429. X    /* Make a closing node, and hook it on the end. */
  430. X    if (paren)
  431. X        ender = reganode(CLOSE, parno);
  432. X    else
  433. X        ender = regnode(END);
  434. X    regtail(ret, ender);
  435. X
  436. X    /* Hook the tails of the branches to the closing node. */
  437. X    for (br = ret; br != NULL; br = regnext(br))
  438. X        regoptail(br, ender);
  439. X
  440. X    /* Check for proper termination. */
  441. X    if (paren && *regparse++ != ')') {
  442. X        FAIL("unmatched () in regexp");
  443. X    } else if (!paren && regparse < regxend) {
  444. X        if (*regparse == ')') {
  445. X            FAIL("unmatched () in regexp");
  446. X        } else
  447. X            FAIL("junk on end of regexp");    /* "Can't happen". */
  448. X        /* NOTREACHED */
  449. X    }
  450. X
  451. X    return(ret);
  452. X}
  453. X
  454. X/*
  455. X - regbranch - one alternative of an | operator
  456. X *
  457. X * Implements the concatenation operator.
  458. X */
  459. Xstatic char *
  460. Xregbranch(flagp)
  461. Xint *flagp;
  462. X{
  463. X    register char *ret;
  464. X    register char *chain;
  465. X    register char *latest;
  466. X    int flags;
  467. X
  468. X    *flagp = WORST;        /* Tentatively. */
  469. X
  470. X    ret = regnode(BRANCH);
  471. X    chain = NULL;
  472. X    while (regparse < regxend && *regparse != '|' && *regparse != ')') {
  473. X        latest = regpiece(&flags);
  474. X        if (latest == NULL)
  475. X            return(NULL);
  476. X        *flagp |= flags&HASWIDTH;
  477. X        if (chain == NULL)    /* First piece. */
  478. X            *flagp |= flags&SPSTART;
  479. X        else
  480. X            regtail(chain, latest);
  481. X        chain = latest;
  482. X    }
  483. X    if (chain == NULL)    /* Loop ran zero times. */
  484. X        (void) regnode(NOTHING);
  485. X
  486. X    return(ret);
  487. X}
  488. X
  489. X/*
  490. X - regpiece - something followed by possible [*+?]
  491. X *
  492. X * Note that the branching code sequences used for ? and the general cases
  493. X * of * and + are somewhat optimized:  they use the same NOTHING node as
  494. X * both the endmarker for their branch list and the body of the last branch.
  495. X * It might seem that this node could be dispensed with entirely, but the
  496. X * endmarker role is not redundant.
  497. X */
  498. Xstatic char *
  499. Xregpiece(flagp)
  500. Xint *flagp;
  501. X{
  502. X    register char *ret;
  503. X    register char op;
  504. X    register char *next;
  505. X    int flags;
  506. X    char *origparse = regparse;
  507. X    int orignpar = regnpar;
  508. X    char *max;
  509. X    int iter;
  510. X    char ch;
  511. X
  512. X    ret = regatom(&flags);
  513. X    if (ret == NULL)
  514. X        return(NULL);
  515. X
  516. X    op = *regparse;
  517. X
  518. X    /* Here's a total kludge: if after the atom there's a {\d+,?\d*}
  519. X     * then we decrement the first number by one and reset our
  520. X     * parsing back to the beginning of the same atom.  If the first number
  521. X     * is down to 0, decrement the second number instead and fake up
  522. X     * a ? after it.  Given the way this compiler doesn't keep track
  523. X     * of offsets on the first pass, this is the only way to replicate
  524. X     * a piece of code.  Sigh.
  525. X     */
  526. X    if (op == '{' && regcurly(regparse)) {
  527. X        next = regparse + 1;
  528. X        max = Nullch;
  529. X        while (isdigit(*next) || *next == ',') {
  530. X        if (*next == ',') {
  531. X            if (max)
  532. X            break;
  533. X            else
  534. X            max = next;
  535. X        }
  536. X        next++;
  537. X        }
  538. X        if (*next == '}') {        /* got one */
  539. X        if (!max)
  540. X            max = next;
  541. X        regparse++;
  542. X        iter = atoi(regparse);
  543. X        if (flags&SIMPLE) {    /* we can do it right after all */
  544. X            int tmp;
  545. X
  546. X            reginsert(CURLY, ret);
  547. X            if (iter > 0)
  548. X            *flagp = (WORST|HASWIDTH);
  549. X            if (*max == ',')
  550. X            max++;
  551. X            else
  552. X            max = regparse;
  553. X            tmp = atoi(max);
  554. X            if (tmp && tmp < iter)
  555. X            fatal("Can't do {n,m} with n > m");
  556. X            if (regcode != ®dummy) {
  557. X#ifdef REGALIGN
  558. X            *(unsigned short *)(ret+3) = iter;
  559. X            *(unsigned short *)(ret+5) = tmp;
  560. X#else
  561. X            ret[3] = iter >> 8; ret[4] = iter & 0377;
  562. X            ret[5] = tmp  >> 8; ret[6] = tmp  & 0377;
  563. X#endif
  564. X            }
  565. X            regparse = next;
  566. X            goto nest_check;
  567. X        }
  568. X        regsawbracket++;    /* remember we clobbered exp */
  569. X        if (iter > 0) {
  570. X            ch = *max;
  571. X            sprintf(regparse,"%.*d", max-regparse, iter - 1);
  572. X            *max = ch;
  573. X            if (*max == ',' && max[1] != '}') {
  574. X            if (atoi(max+1) <= 0)
  575. X                fatal("Can't do {n,m} with n > m");
  576. X            ch = *next;
  577. X            sprintf(max+1,"%.*d", next-(max+1), atoi(max+1) - 1);
  578. X            *next = ch;
  579. X            }
  580. X            if (iter != 1 || *max == ',') {
  581. X            regparse = origparse;    /* back up input pointer */
  582. X            regnpar = orignpar;    /* don't make more parens */
  583. X            }
  584. X            else {
  585. X            regparse = next;
  586. X            goto nest_check;
  587. X            }
  588. X            *flagp = flags;
  589. X            return ret;
  590. X        }
  591. X        if (*max == ',') {
  592. X            max++;
  593. X            iter = atoi(max);
  594. X            if (max == next) {        /* any number more? */
  595. X            regparse = next;
  596. X            op = '*';        /* fake up one with a star */
  597. X            }
  598. X            else if (iter > 0) {
  599. X            op = '?';        /* fake up optional atom */
  600. X            ch = *next;
  601. X            sprintf(max,"%.*d", next-max, iter - 1);
  602. X            *next = ch;
  603. X            if (iter == 1)
  604. X                regparse = next;
  605. X            else {
  606. X                regparse = origparse - 1; /* offset ++ below */
  607. X                regnpar = orignpar;
  608. X            }
  609. X            }
  610. X            else
  611. X            fatal("Can't do {n,0}");
  612. X        }
  613. X        else
  614. X            fatal("Can't do {0}");
  615. X        }
  616. X    }
  617. X
  618. X    if (!ISMULT1(op)) {
  619. X        *flagp = flags;
  620. X        return(ret);
  621. X    }
  622. X
  623. X    if (!(flags&HASWIDTH) && op != '?')
  624. X        FAIL("regexp *+ operand could be empty");
  625. X    *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
  626. X
  627. X    if (op == '*' && (flags&SIMPLE))
  628. X        reginsert(STAR, ret);
  629. X    else if (op == '*') {
  630. X        /* Emit x* as (x&|), where & means "self". */
  631. X        reginsert(BRANCH, ret);            /* Either x */
  632. X        regoptail(ret, regnode(BACK));        /* and loop */
  633. X        regoptail(ret, ret);            /* back */
  634. X        regtail(ret, regnode(BRANCH));        /* or */
  635. X        regtail(ret, regnode(NOTHING));        /* null. */
  636. X    } else if (op == '+' && (flags&SIMPLE))
  637. X        reginsert(PLUS, ret);
  638. X    else if (op == '+') {
  639. X        /* Emit x+ as x(&|), where & means "self". */
  640. X        next = regnode(BRANCH);            /* Either */
  641. X        regtail(ret, next);
  642. X        regtail(regnode(BACK), ret);        /* loop back */
  643. X        regtail(next, regnode(BRANCH));        /* or */
  644. X        regtail(ret, regnode(NOTHING));        /* null. */
  645. X    } else if (op == '?') {
  646. X        /* Emit x? as (x|) */
  647. X        reginsert(BRANCH, ret);            /* Either x */
  648. X        regtail(ret, regnode(BRANCH));        /* or */
  649. X        next = regnode(NOTHING);        /* null. */
  650. X        regtail(ret, next);
  651. X        regoptail(ret, next);
  652. X    }
  653. X      nest_check:
  654. X    regparse++;
  655. X    if (ISMULT2(regparse))
  656. X        FAIL("nested *?+ in regexp");
  657. X
  658. X    return(ret);
  659. X}
  660. X
  661. X/*
  662. X - regatom - the lowest level
  663. X *
  664. X * Optimization:  gobbles an entire sequence of ordinary characters so that
  665. X * it can turn them into a single node, which is smaller to store and
  666. X * faster to run.  Backslashed characters are exceptions, each becoming a
  667. X * separate node; the code is simpler that way and it's not worth fixing.
  668. X *
  669. X * [Yes, it is worth fixing, some scripts can run twice the speed.]
  670. X */
  671. Xstatic char *
  672. Xregatom(flagp)
  673. Xint *flagp;
  674. X{
  675. X    register char *ret;
  676. X    int flags;
  677. X
  678. X    *flagp = WORST;        /* Tentatively. */
  679. X
  680. X    switch (*regparse++) {
  681. X    case '^':
  682. X        ret = regnode(BOL);
  683. X        break;
  684. X    case '$':
  685. X        ret = regnode(EOL);
  686. X        break;
  687. X    case '.':
  688. X        ret = regnode(ANY);
  689. X        *flagp |= HASWIDTH|SIMPLE;
  690. X        break;
  691. X    case '[':
  692. X        ret = regclass();
  693. X        *flagp |= HASWIDTH|SIMPLE;
  694. X        break;
  695. X    case '(':
  696. X        ret = reg(1, &flags);
  697. X        if (ret == NULL)
  698. X            return(NULL);
  699. X        *flagp |= flags&(HASWIDTH|SPSTART);
  700. X        break;
  701. X    case '|':
  702. X    case ')':
  703. X        FAIL("internal urp in regexp");    /* Supposed to be caught earlier. */
  704. X        break;
  705. X    case '?':
  706. X    case '+':
  707. X    case '*':
  708. X        FAIL("?+* follows nothing in regexp");
  709. X        break;
  710. X    case '\\':
  711. X        switch (*regparse) {
  712. X        case 'w':
  713. X            ret = regnode(ALNUM);
  714. X            *flagp |= HASWIDTH|SIMPLE;
  715. X            regparse++;
  716. X            break;
  717. X        case 'W':
  718. X            ret = regnode(NALNUM);
  719. X            *flagp |= HASWIDTH|SIMPLE;
  720. X            regparse++;
  721. X            break;
  722. X        case 'b':
  723. X            ret = regnode(BOUND);
  724. X            *flagp |= SIMPLE;
  725. X            regparse++;
  726. X            break;
  727. X        case 'B':
  728. X            ret = regnode(NBOUND);
  729. X            *flagp |= SIMPLE;
  730. X            regparse++;
  731. X            break;
  732. X        case 's':
  733. X            ret = regnode(SPACE);
  734. X            *flagp |= HASWIDTH|SIMPLE;
  735. X            regparse++;
  736. X            break;
  737. X        case 'S':
  738. X            ret = regnode(NSPACE);
  739. X            *flagp |= HASWIDTH|SIMPLE;
  740. X            regparse++;
  741. X            break;
  742. X        case 'd':
  743. X            ret = regnode(DIGIT);
  744. X            *flagp |= HASWIDTH|SIMPLE;
  745. X            regparse++;
  746. X            break;
  747. X        case 'D':
  748. X            ret = regnode(NDIGIT);
  749. X            *flagp |= HASWIDTH|SIMPLE;
  750. X            regparse++;
  751. X            break;
  752. X        case 'n':
  753. X        case 'r':
  754. X        case 't':
  755. X        case 'f':
  756. X        case 'e':
  757. X        case 'a':
  758. X        case 'x':
  759. X        case 'c':
  760. X        case '0':
  761. X            goto defchar;
  762. X        case '1': case '2': case '3': case '4':
  763. X        case '5': case '6': case '7': case '8': case '9':
  764. X            {
  765. X                int num = atoi(regparse);
  766. X
  767. X                if (num > 9 && num >= regnpar)
  768. X                goto defchar;
  769. X                else {
  770. X                ret = reganode(REF, num);
  771. X                while (isascii(*regparse) && isdigit(*regparse))
  772. X                    regparse++;
  773. X                *flagp |= SIMPLE;
  774. X                }
  775. X            }
  776. X            break;
  777. X        case '\0':
  778. X            if (regparse >= regxend)
  779. X                FAIL("trailing \\ in regexp");
  780. X            /* FALL THROUGH */
  781. X        default:
  782. X            goto defchar;
  783. X        }
  784. X        break;
  785. X    default: {
  786. X            register int len;
  787. X            register char ender;
  788. X            register char *p;
  789. X            char *oldp;
  790. X            int numlen;
  791. X
  792. X            defchar:
  793. X            ret = regnode(EXACTLY);
  794. X            regc(0);        /* save spot for len */
  795. X            for (len=0, p=regparse-1;
  796. X              len < 127 && p < regxend;
  797. X              len++)
  798. X            {
  799. X                oldp = p;
  800. X                switch (*p) {
  801. X                case '^':
  802. X                case '$':
  803. X                case '.':
  804. X                case '[':
  805. X                case '(':
  806. X                case ')':
  807. X                case '|':
  808. X                goto loopdone;
  809. X                case '\\':
  810. X                switch (*++p) {
  811. X                case 'w':
  812. X                case 'W':
  813. X                case 'b':
  814. X                case 'B':
  815. X                case 's':
  816. X                case 'S':
  817. X                case 'd':
  818. X                case 'D':
  819. X                    --p;
  820. X                    goto loopdone;
  821. X                case 'n':
  822. X                    ender = '\n';
  823. X                    p++;
  824. X                    break;
  825. X                case 'r':
  826. X                    ender = '\r';
  827. X                    p++;
  828. X                    break;
  829. X                case 't':
  830. X                    ender = '\t';
  831. X                    p++;
  832. X                    break;
  833. X                case 'f':
  834. X                    ender = '\f';
  835. X                    p++;
  836. X                    break;
  837. X                case 'e':
  838. X                    ender = '\033';
  839. X                    p++;
  840. X                    break;
  841. X                case 'a':
  842. X                    ender = '\007';
  843. X                    p++;
  844. X                    break;
  845. X                case 'x':
  846. X                    ender = scanhex(++p, 2, &numlen);
  847. X                    p += numlen;
  848. X                    break;
  849. X                case 'c':
  850. X                    p++;
  851. X                    ender = *p++;
  852. X                    if (islower(ender))
  853. X                    ender = toupper(ender);
  854. X                    ender ^= 64;
  855. X                    break;
  856. X                case '0': case '1': case '2': case '3':case '4':
  857. X                case '5': case '6': case '7': case '8':case '9':
  858. X                    if (*p == '0' ||
  859. X                      (isdigit(p[1]) && atoi(p) >= regnpar) ) {
  860. X                    ender = scanoct(p, 3, &numlen);
  861. X                    p += numlen;
  862. X                    }
  863. X                    else {
  864. X                    --p;
  865. X                    goto loopdone;
  866. X                    }
  867. X                    break;
  868. X                case '\0':
  869. X                    if (p >= regxend)
  870. X                    FAIL("trailing \\ in regexp");
  871. X                    /* FALL THROUGH */
  872. X                default:
  873. X                    ender = *p++;
  874. X                    break;
  875. X                }
  876. X                break;
  877. X                default:
  878. X                ender = *p++;
  879. X                break;
  880. X                }
  881. X                if (regfold && isupper(ender))
  882. X                    ender = tolower(ender);
  883. X                if (ISMULT2(p)) { /* Back off on ?+*. */
  884. X                if (len)
  885. X                    p = oldp;
  886. X                else {
  887. X                    len++;
  888. X                    regc(ender);
  889. X                }
  890. X                break;
  891. X                }
  892. X                regc(ender);
  893. X            }
  894. X            loopdone:
  895. X            regparse = p;
  896. X            if (len <= 0)
  897. X                FAIL("internal disaster in regexp");
  898. X            *flagp |= HASWIDTH;
  899. X            if (len == 1)
  900. X                *flagp |= SIMPLE;
  901. X            if (regcode != ®dummy)
  902. X                *OPERAND(ret) = len;
  903. X            regc('\0');
  904. X        }
  905. X        break;
  906. X    }
  907. X
  908. X    return(ret);
  909. X}
  910. X
  911. Xstatic void
  912. Xregset(bits,def,c)
  913. Xchar *bits;
  914. Xint def;
  915. Xregister int c;
  916. X{
  917. X    if (regcode == ®dummy)
  918. X        return;
  919. X    c &= 255;
  920. X    if (def)
  921. X        bits[c >> 3] &= ~(1 << (c & 7));
  922. X    else
  923. X        bits[c >> 3] |=  (1 << (c & 7));
  924. X}
  925. X
  926. Xstatic char *
  927. Xregclass()
  928. X{
  929. X    register char *bits;
  930. X    register int class;
  931. X    register int lastclass;
  932. X    register int range = 0;
  933. X    register char *ret;
  934. X    register int def;
  935. X    int numlen;
  936. X
  937. X    ret = regnode(ANYOF);
  938. X    if (*regparse == '^') {    /* Complement of range. */
  939. X        regparse++;
  940. X        def = 0;
  941. X    } else {
  942. X        def = 255;
  943. X    }
  944. X    bits = regcode;
  945. X    for (class = 0; class < 32; class++)
  946. X        regc(def);
  947. X    if (*regparse == ']' || *regparse == '-')
  948. X        goto skipcond;        /* allow 1st char to be ] or - */
  949. X    while (regparse < regxend && *regparse != ']') {
  950. X          skipcond:
  951. X        class = UCHARAT(regparse++);
  952. X        if (class == '\\') {
  953. X            class = UCHARAT(regparse++);
  954. X            switch (class) {
  955. X            case 'w':
  956. X                for (class = 'a'; class <= 'z'; class++)
  957. X                    regset(bits,def,class);
  958. X                for (class = 'A'; class <= 'Z'; class++)
  959. X                    regset(bits,def,class);
  960. X                for (class = '0'; class <= '9'; class++)
  961. X                    regset(bits,def,class);
  962. X                regset(bits,def,'_');
  963. X                lastclass = 1234;
  964. X                continue;
  965. X            case 's':
  966. X                regset(bits,def,' ');
  967. X                regset(bits,def,'\t');
  968. X                regset(bits,def,'\r');
  969. X                regset(bits,def,'\f');
  970. X                regset(bits,def,'\n');
  971. X                lastclass = 1234;
  972. X                continue;
  973. X            case 'd':
  974. X                for (class = '0'; class <= '9'; class++)
  975. X                    regset(bits,def,class);
  976. X                lastclass = 1234;
  977. X                continue;
  978. X            case 'n':
  979. X                class = '\n';
  980. X                break;
  981. X            case 'r':
  982. X                class = '\r';
  983. X                break;
  984. X            case 't':
  985. X                class = '\t';
  986. X                break;
  987. X            case 'f':
  988. X                class = '\f';
  989. X                break;
  990. X            case 'b':
  991. X                class = '\b';
  992. X                break;
  993. X            case 'e':
  994. X                class = '\033';
  995. X                break;
  996. X            case 'a':
  997. X                class = '\007';
  998. X                break;
  999. X            case 'x':
  1000. X                class = scanhex(regparse, 2, &numlen);
  1001. X                regparse += numlen;
  1002. X                break;
  1003. X            case 'c':
  1004. X                class = *regparse++;
  1005. X                if (islower(class))
  1006. X                    class = toupper(class);
  1007. X                class ^= 64;
  1008. X                break;
  1009. X            case '0': case '1': case '2': case '3': case '4':
  1010. X            case '5': case '6': case '7': case '8': case '9':
  1011. X                class = scanoct(--regparse, 3, &numlen);
  1012. X                regparse += numlen;
  1013. X                break;
  1014. X            }
  1015. X        }
  1016. X        if (range) {
  1017. X            if (lastclass > class)
  1018. X                FAIL("invalid [] range in regexp");
  1019. X            range = 0;
  1020. X        }
  1021. X        else {
  1022. X            lastclass = class;
  1023. X            if (*regparse == '-' && regparse+1 < regxend &&
  1024. X                regparse[1] != ']') {
  1025. X                regparse++;
  1026. X                range = 1;
  1027. X                continue;    /* do it next time */
  1028. X            }
  1029. X        }
  1030. X        for ( ; lastclass <= class; lastclass++) {
  1031. X            regset(bits,def,lastclass);
  1032. X            if (regfold && isupper(lastclass))
  1033. X                regset(bits,def,tolower(lastclass));
  1034. X        }
  1035. X        lastclass = class;
  1036. X    }
  1037. X    if (*regparse != ']')
  1038. X        FAIL("unmatched [] in regexp");
  1039. X    regparse++;
  1040. X    return ret;
  1041. X}
  1042. X
  1043. X/*
  1044. X - regnode - emit a node
  1045. X */
  1046. Xstatic char *            /* Location. */
  1047. Xregnode(op)
  1048. Xchar op;
  1049. X{
  1050. X    register char *ret;
  1051. X    register char *ptr;
  1052. X
  1053. X    ret = regcode;
  1054. X    if (ret == ®dummy) {
  1055. X#ifdef REGALIGN
  1056. X        if (!(regsize & 1))
  1057. X            regsize++;
  1058. X#endif
  1059. X        regsize += 3;
  1060. X        return(ret);
  1061. X    }
  1062. X
  1063. X#ifdef REGALIGN
  1064. X#ifndef lint
  1065. X    if (!((long)ret & 1))
  1066. X        *ret++ = 127;
  1067. X#endif
  1068. X#endif
  1069. X    ptr = ret;
  1070. X    *ptr++ = op;
  1071. X    *ptr++ = '\0';        /* Null "next" pointer. */
  1072. X    *ptr++ = '\0';
  1073. X    regcode = ptr;
  1074. X
  1075. X    return(ret);
  1076. X}
  1077. X
  1078. X/*
  1079. X - reganode - emit a node with an argument
  1080. X */
  1081. Xstatic char *            /* Location. */
  1082. Xreganode(op, arg)
  1083. Xchar op;
  1084. Xunsigned short arg;
  1085. X{
  1086. X    register char *ret;
  1087. X    register char *ptr;
  1088. X
  1089. X    ret = regcode;
  1090. X    if (ret == ®dummy) {
  1091. X#ifdef REGALIGN
  1092. X        if (!(regsize & 1))
  1093. X            regsize++;
  1094. X#endif
  1095. X        regsize += 5;
  1096. X        return(ret);
  1097. X    }
  1098. X
  1099. X#ifdef REGALIGN
  1100. X#ifndef lint
  1101. X    if (!((long)ret & 1))
  1102. X        *ret++ = 127;
  1103. X#endif
  1104. X#endif
  1105. X    ptr = ret;
  1106. X    *ptr++ = op;
  1107. X    *ptr++ = '\0';        /* Null "next" pointer. */
  1108. X    *ptr++ = '\0';
  1109. X#ifdef REGALIGN
  1110. X    *(unsigned short *)(ret+3) = arg;
  1111. X#else
  1112. X    ret[3] = arg >> 8; ret[4] = arg & 0377;
  1113. X#endif
  1114. X    ptr += 2;
  1115. X    regcode = ptr;
  1116. X
  1117. X    return(ret);
  1118. X}
  1119. X
  1120. X/*
  1121. X - regc - emit (if appropriate) a byte of code
  1122. X */
  1123. Xstatic void
  1124. Xregc(b)
  1125. Xchar b;
  1126. X{
  1127. X    if (regcode != ®dummy)
  1128. X        *regcode++ = b;
  1129. X    else
  1130. X        regsize++;
  1131. X}
  1132. X
  1133. X/*
  1134. X - reginsert - insert an operator in front of already-emitted operand
  1135. X *
  1136. X * Means relocating the operand.
  1137. X */
  1138. Xstatic void
  1139. Xreginsert(op, opnd)
  1140. Xchar op;
  1141. Xchar *opnd;
  1142. X{
  1143. X    register char *src;
  1144. X    register char *dst;
  1145. X    register char *place;
  1146. X    register offset = (op == CURLY ? 4 : 0);
  1147. X
  1148. X    if (regcode == ®dummy) {
  1149. X#ifdef REGALIGN
  1150. X        regsize += 4 + offset;
  1151. X#else
  1152. X        regsize += 3 + offset;
  1153. X#endif
  1154. X        return;
  1155. X    }
  1156. X
  1157. X    src = regcode;
  1158. X#ifdef REGALIGN
  1159. X    regcode += 4 + offset;
  1160. X#else
  1161. X    regcode += 3 + offset;
  1162. X#endif
  1163. X    dst = regcode;
  1164. X    while (src > opnd)
  1165. X        *--dst = *--src;
  1166. X
  1167. X    place = opnd;        /* Op node, where operand used to be. */
  1168. X    *place++ = op;
  1169. X    *place++ = '\0';
  1170. X    *place++ = '\0';
  1171. X    while (offset-- > 0)
  1172. X        *place++ = '\0';
  1173. X}
  1174. X
  1175. X/*
  1176. X - regtail - set the next-pointer at the end of a node chain
  1177. X */
  1178. Xstatic void
  1179. Xregtail(p, val)
  1180. Xchar *p;
  1181. Xchar *val;
  1182. X{
  1183. X    register char *scan;
  1184. X    register char *temp;
  1185. X    register int offset;
  1186. X
  1187. X    if (p == ®dummy)
  1188. X        return;
  1189. X
  1190. X    /* Find last node. */
  1191. X    scan = p;
  1192. X    for (;;) {
  1193. X        temp = regnext(scan);
  1194. X        if (temp == NULL)
  1195. X            break;
  1196. X        scan = temp;
  1197. X    }
  1198. X
  1199. X#ifdef REGALIGN
  1200. X    offset = val - scan;
  1201. X#ifndef lint
  1202. X    *(short*)(scan+1) = offset;
  1203. X#else
  1204. X    offset = offset;
  1205. X#endif
  1206. X#else
  1207. X    if (OP(scan) == BACK)
  1208. X        offset = scan - val;
  1209. X    else
  1210. X        offset = val - scan;
  1211. X    *(scan+1) = (offset>>8)&0377;
  1212. X    *(scan+2) = offset&0377;
  1213. X#endif
  1214. X}
  1215. X
  1216. X/*
  1217. X - regoptail - regtail on operand of first argument; nop if operandless
  1218. X */
  1219. Xstatic void
  1220. Xregoptail(p, val)
  1221. Xchar *p;
  1222. Xchar *val;
  1223. X{
  1224. X    /* "Operandless" and "op != BRANCH" are synonymous in practice. */
  1225. X    if (p == NULL || p == ®dummy || OP(p) != BRANCH)
  1226. X        return;
  1227. X    regtail(NEXTOPER(p), val);
  1228. X}
  1229. X
  1230. X/*
  1231. X - regcurly - a little FSA that accepts {\d+,?\d*}
  1232. X */
  1233. XSTATIC int
  1234. Xregcurly(s)
  1235. Xregister char *s;
  1236. X{
  1237. X    if (*s++ != '{')
  1238. X    return FALSE;
  1239. X    if (!isdigit(*s))
  1240. X    return FALSE;
  1241. X    while (isdigit(*s))
  1242. X    s++;
  1243. X    if (*s == ',')
  1244. X    s++;
  1245. X    while (isdigit(*s))
  1246. X    s++;
  1247. X    if (*s != '}')
  1248. X    return FALSE;
  1249. X    return TRUE;
  1250. X}
  1251. X
  1252. X#ifdef DEBUGGING
  1253. X
  1254. X/*
  1255. X - regdump - dump a regexp onto stderr in vaguely comprehensible form
  1256. X */
  1257. Xvoid
  1258. Xregdump(r)
  1259. Xregexp *r;
  1260. X{
  1261. X    register char *s;
  1262. X    register char op = EXACTLY;    /* Arbitrary non-END op. */
  1263. X    register char *next;
  1264. X
  1265. X
  1266. X    s = r->program + 1;
  1267. X    while (op != END) {    /* While that wasn't END last time... */
  1268. X#ifdef REGALIGN
  1269. X        if (!((long)s & 1))
  1270. X            s++;
  1271. X#endif
  1272. X        op = OP(s);
  1273. X        fprintf(stderr,"%2d%s", s-r->program, regprop(s));    /* Where, what. */
  1274. X        next = regnext(s);
  1275. X        s += regarglen[op];
  1276. X        if (next == NULL)        /* Next ptr. */
  1277. X            fprintf(stderr,"(0)");
  1278. X        else 
  1279. X            fprintf(stderr,"(%d)", (s-r->program)+(next-s));
  1280. X        s += 3;
  1281. X        if (op == ANYOF) {
  1282. X            s += 32;
  1283. X        }
  1284. X        if (op == EXACTLY) {
  1285. X            /* Literal string, where present. */
  1286. X            s++;
  1287. X            while (*s != '\0') {
  1288. X                (void)putchar(*s);
  1289. X                s++;
  1290. X            }
  1291. X            s++;
  1292. X        }
  1293. X        (void)putchar('\n');
  1294. X    }
  1295. X
  1296. X    /* Header fields of interest. */
  1297. X    if (r->regstart)
  1298. X        fprintf(stderr,"start `%s' ", r->regstart->str_ptr);
  1299. X    if (r->regstclass)
  1300. X        fprintf(stderr,"stclass `%s' ", regprop(r->regstclass));
  1301. X    if (r->reganch & 1)
  1302. X        fprintf(stderr,"anchored ");
  1303. X    if (r->reganch & 2)
  1304. X        fprintf(stderr,"plus ");
  1305. X    if (r->regmust != NULL)
  1306. X        fprintf(stderr,"must have \"%s\" back %d ", r->regmust->str_ptr,
  1307. X          r->regback);
  1308. X    fprintf(stderr,"\n");
  1309. X}
  1310. X
  1311. X/*
  1312. X - regprop - printable representation of opcode
  1313. X */
  1314. Xchar *
  1315. Xregprop(op)
  1316. Xchar *op;
  1317. X{
  1318. X    register char *p;
  1319. X
  1320. X    (void) strcpy(buf, ":");
  1321. X
  1322. X    switch (OP(op)) {
  1323. X    case BOL:
  1324. X        p = "BOL";
  1325. X        break;
  1326. X    case EOL:
  1327. X        p = "EOL";
  1328. X        break;
  1329. X    case ANY:
  1330. X        p = "ANY";
  1331. X        break;
  1332. X    case ANYOF:
  1333. X        p = "ANYOF";
  1334. X        break;
  1335. X    case BRANCH:
  1336. X        p = "BRANCH";
  1337. X        break;
  1338. X    case EXACTLY:
  1339. X        p = "EXACTLY";
  1340. X        break;
  1341. X    case NOTHING:
  1342. X        p = "NOTHING";
  1343. X        break;
  1344. X    case BACK:
  1345. X        p = "BACK";
  1346. X        break;
  1347. X    case END:
  1348. X        p = "END";
  1349. X        break;
  1350. X    case ALNUM:
  1351. X        p = "ALNUM";
  1352. X        break;
  1353. X    case NALNUM:
  1354. X        p = "NALNUM";
  1355. X        break;
  1356. X    case BOUND:
  1357. X        p = "BOUND";
  1358. X        break;
  1359. X    case NBOUND:
  1360. X        p = "NBOUND";
  1361. X        break;
  1362. X    case SPACE:
  1363. X        p = "SPACE";
  1364. X        break;
  1365. X    case NSPACE:
  1366. X        p = "NSPACE";
  1367. X        break;
  1368. X    case DIGIT:
  1369. X        p = "DIGIT";
  1370. X        break;
  1371. X    case NDIGIT:
  1372. X        p = "NDIGIT";
  1373. X        break;
  1374. X    case CURLY:
  1375. X        (void)sprintf(buf+strlen(buf), "CURLY {%d,%d}",
  1376. X            ARG1(op),ARG2(op));
  1377. X        p = NULL;
  1378. X        break;
  1379. X    case REF:
  1380. X        (void)sprintf(buf+strlen(buf), "REF%d", ARG1(op));
  1381. X        p = NULL;
  1382. X        break;
  1383. X    case OPEN:
  1384. X        (void)sprintf(buf+strlen(buf), "OPEN%d", ARG1(op));
  1385. X        p = NULL;
  1386. X        break;
  1387. X    case CLOSE:
  1388. X        (void)sprintf(buf+strlen(buf), "CLOSE%d", ARG1(op));
  1389. X        p = NULL;
  1390. X        break;
  1391. X    case STAR:
  1392. X        p = "STAR";
  1393. X        break;
  1394. X    case PLUS:
  1395. X        p = "PLUS";
  1396. X        break;
  1397. X    default:
  1398. X        FAIL("corrupted regexp opcode");
  1399. X    }
  1400. X    if (p != NULL)
  1401. X        (void) strcat(buf, p);
  1402. X    return(buf);
  1403. X}
  1404. X#endif /* DEBUGGING */
  1405. X
  1406. Xregfree(r)
  1407. Xstruct regexp *r;
  1408. X{
  1409. X    if (r->precomp) {
  1410. X        Safefree(r->precomp);
  1411. X        r->precomp = Nullch;
  1412. X    }
  1413. X    if (r->subbase) {
  1414. X        Safefree(r->subbase);
  1415. X        r->subbase = Nullch;
  1416. X    }
  1417. X    if (r->regmust) {
  1418. X        str_free(r->regmust);
  1419. X        r->regmust = Nullstr;
  1420. X    }
  1421. X    if (r->regstart) {
  1422. X        str_free(r->regstart);
  1423. X        r->regstart = Nullstr;
  1424. X    }
  1425. X    Safefree(r->startp);
  1426. X    Safefree(r->endp);
  1427. X    Safefree(r);
  1428. X}
  1429. !STUFFY!FUNK!
  1430. echo Extracting emacs/perldb.el
  1431. sed >emacs/perldb.el <<'!STUFFY!FUNK!' -e 's/X//'
  1432. X;; Run perl -d under Emacs
  1433. X;; Based on gdb.el, as written by W. Schelter, and modified by rms.
  1434. X;; Modified for Perl by Ray Lischner (uunet!mntgfx!lisch), Nov 1990.
  1435. X
  1436. X;; This file is part of GNU Emacs.
  1437. X;; Copyright (C) 1988,1990 Free Software Foundation, Inc.
  1438. X
  1439. X;; GNU Emacs is distributed in the hope that it will be useful, but
  1440. X;; WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  1441. X;; to anyone for the consequences of using it or for whether it serves
  1442. X;; any particular purpose or works at all, unless he says so in writing.
  1443. X;; Refer to the GNU Emacs General Public License for full details.
  1444. X
  1445. X;; Everyone is granted permission to copy, modify and redistribute GNU
  1446. X;; Emacs, but only under the conditions described in the GNU Emacs
  1447. X;; General Public License.  A copy of this license is supposed to have
  1448. X;; been given to you along with GNU Emacs so you can know your rights and
  1449. X;; responsibilities.  It should be in a file named COPYING.  Among other
  1450. X;; things, the copyright notice and this notice must be preserved on all
  1451. X;; copies.
  1452. X
  1453. X;; Description of perl -d interface:
  1454. X
  1455. X;; A facility is provided for the simultaneous display of the source code
  1456. X;; in one window, while using perldb to step through a function in the
  1457. X;; other.  A small arrow in the source window, indicates the current
  1458. X;; line.
  1459. X
  1460. X;; Starting up:
  1461. X
  1462. X;; In order to use this facility, invoke the command PERLDB to obtain a
  1463. X;; shell window with the appropriate command bindings.  You will be asked
  1464. X;; for the name of a file to run and additional command line arguments.
  1465. X;; Perldb will be invoked on this file, in a window named *perldb-foo*
  1466. X;; if the file is foo.
  1467. X
  1468. X;; M-s steps by one line, and redisplays the source file and line.
  1469. X
  1470. X;; You may easily create additional commands and bindings to interact
  1471. X;; with the display.  For example to put the perl debugger command n on \M-n
  1472. X;; (def-perldb n "\M-n")
  1473. X
  1474. X;; This causes the emacs command perldb-next to be defined, and runs
  1475. X;; perldb-display-frame after the command.
  1476. X
  1477. X;; perldb-display-frame is the basic display function.  It tries to display
  1478. X;; in the other window, the file and line corresponding to the current
  1479. X;; position in the perldb window.  For example after a perldb-step, it would
  1480. X;; display the line corresponding to the position for the last step.  Or
  1481. X;; if you have done a backtrace in the perldb buffer, and move the cursor
  1482. X;; into one of the frames, it would display the position corresponding to
  1483. X;; that frame.
  1484. X
  1485. X;; perldb-display-frame is invoked automatically when a filename-and-line-number
  1486. X;; appears in the output.
  1487. X
  1488. X
  1489. X(require 'shell)
  1490. X
  1491. X(defvar perldb-prompt-pattern "^  DB<[0-9]+> "
  1492. X  "A regexp to recognize the prompt for perldb.") 
  1493. X
  1494. X(defvar perldb-mode-map nil
  1495. X  "Keymap for perldb-mode.")
  1496. X
  1497. X(if perldb-mode-map
  1498. X   nil
  1499. X  (setq perldb-mode-map (copy-keymap shell-mode-map))
  1500. X  (define-key perldb-mode-map "\C-l" 'perldb-refresh))
  1501. X
  1502. X(define-key ctl-x-map " " 'perldb-break)
  1503. X(define-key ctl-x-map "&" 'send-perldb-command)
  1504. X
  1505. X;;Of course you may use `def-perldb' with any other perldb command, including
  1506. X;;user defined ones.   
  1507. X
  1508. X(defmacro def-perldb (name key &optional doc)
  1509. X  (let* ((fun (intern (concat "perldb-" name))))
  1510. X    (` (progn
  1511. X     (defun (, fun) (arg)
  1512. X       (, (or doc ""))
  1513. X       (interactive "p")
  1514. X       (perldb-call (if (not (= 1 arg))
  1515. X                (concat (, name) arg)
  1516. X              (, name))))
  1517. X     (define-key perldb-mode-map (, key) (quote (, fun)))))))
  1518. X
  1519. X(def-perldb "s"   "\M-s" "Step one source line with display")
  1520. X(def-perldb "n"   "\M-n" "Step one source line (skip functions)")
  1521. X(def-perldb "c"   "\M-c" "Continue with display")
  1522. X(def-perldb "r"   "\C-c\C-r" "Return from current subroutine")
  1523. X(def-perldb "A"   "\C-c\C-a" "Delete all actions")
  1524. X
  1525. X(defun perldb-mode ()
  1526. X  "Major mode for interacting with an inferior Perl debugger process.
  1527. XThe following commands are available:
  1528. X
  1529. X\\{perldb-mode-map}
  1530. X
  1531. X\\[perldb-display-frame] displays in the other window
  1532. Xthe last line referred to in the perldb buffer.
  1533. X
  1534. X\\[perldb-s],\\[perldb-n], and \\[perldb-n] in the perldb window,
  1535. Xcall perldb to step, next or continue and then update the other window
  1536. Xwith the current file and position.
  1537. X
  1538. XIf you are in a source file, you may select a point to break
  1539. Xat, by doing \\[perldb-break].
  1540. X
  1541. XCommands:
  1542. XMany commands are inherited from shell mode. 
  1543. XAdditionally we have:
  1544. X
  1545. X\\[perldb-display-frame] display frames file in other window
  1546. X\\[perldb-s] advance one line in program
  1547. X\\[perldb-n] advance one line in program (skip over calls).
  1548. X\\[send-perldb-command] used for special printing of an arg at the current point.
  1549. XC-x SPACE sets break point at current line."
  1550. X  (interactive)
  1551. X  (kill-all-local-variables)
  1552. X  (setq major-mode 'perldb-mode)
  1553. X  (setq mode-name "Inferior Perl")
  1554. X  (setq mode-line-process '(": %s"))
  1555. X  (use-local-map perldb-mode-map)
  1556. X  (make-local-variable 'last-input-start)
  1557. X  (setq last-input-start (make-marker))
  1558. X  (make-local-variable 'last-input-end)
  1559. X  (setq last-input-end (make-marker))
  1560. X  (make-local-variable 'perldb-last-frame)
  1561. X  (setq perldb-last-frame nil)
  1562. X  (make-local-variable 'perldb-last-frame-displayed-p)
  1563. X  (setq perldb-last-frame-displayed-p t)
  1564. X  (make-local-variable 'perldb-delete-prompt-marker)
  1565. X  (setq perldb-delete-prompt-marker nil)
  1566. X  (make-local-variable 'perldb-filter-accumulator)
  1567. X  (setq perldb-filter-accumulator nil)
  1568. X  (make-local-variable 'shell-prompt-pattern)
  1569. X  (setq shell-prompt-pattern perldb-prompt-pattern)
  1570. X  (run-hooks 'shell-mode-hook 'perldb-mode-hook))
  1571. X
  1572. X(defvar current-perldb-buffer nil)
  1573. X
  1574. X(defvar perldb-command-name "perl"
  1575. X  "Pathname for executing perl -d.")
  1576. X
  1577. X(defun end-of-quoted-arg (argstr start end)
  1578. X  (let* ((chr (substring argstr start (1+ start)))
  1579. X     (idx (string-match (concat "[^\\]" chr) argstr (1+ start))))
  1580. X    (and idx (1+ idx))
  1581. X    )
  1582. X)
  1583. X
  1584. X(defun parse-args-helper (arglist argstr start end)
  1585. X  (while (and (< start end) (string-match "[ \t\n\f\r\b]"
  1586. X                      (substring argstr start (1+ start))))
  1587. X    (setq start (1+ start)))
  1588. X  (cond
  1589. X    ((= start end) arglist)
  1590. X    ((string-match "[\"']" (substring argstr start (1+ start)))
  1591. X     (let ((next (end-of-quoted-arg argstr start end)))
  1592. X       (parse-args-helper (cons (substring argstr (1+ start) next) arglist)
  1593. X              argstr (1+ next) end)))
  1594. X    (t (let ((next (string-match "[ \t\n\f\b\r]" argstr start)))
  1595. X     (if next
  1596. X         (parse-args-helper (cons (substring argstr start next) arglist)
  1597. X                argstr (1+ next) end)
  1598. X       (cons (substring argstr start) arglist))))
  1599. X    )
  1600. X  )
  1601. X    
  1602. X(defun parse-args (args)
  1603. X  "Extract arguments from a string ARGS.
  1604. XWhite space separates arguments, with single or double quotes
  1605. Xused to protect spaces.  A list of strings is returned, e.g.,
  1606. X(parse-args \"foo bar 'two args'\") => (\"foo\" \"bar\" \"two args\")."
  1607. X  (nreverse (parse-args-helper '() args 0 (length args)))
  1608. X)
  1609. X
  1610. X(defun perldb (path args)
  1611. X  "Run perldb on program FILE in buffer *perldb-FILE*.
  1612. XThe default directory for the current buffer becomes the initial
  1613. Xworking directory, by analogy with  gdb .  If you wish to change this, use
  1614. Xthe Perl command `chdir(DIR)'."
  1615. X  (interactive "FRun perl -d on file: \nsCommand line arguments: ")
  1616. X  (setq path (expand-file-name path))
  1617. X  (let ((file (file-name-nondirectory path))
  1618. X    (dir default-directory))
  1619. X    (switch-to-buffer (concat "*perldb-" file "*"))
  1620. X    (setq default-directory dir)
  1621. X    (or (bolp) (newline))
  1622. X    (insert "Current directory is " default-directory "\n")
  1623. X    (apply 'make-shell
  1624. X       (concat "perldb-" file) perldb-command-name nil "-d" path "-emacs"
  1625. X       (parse-args args))
  1626. X    (perldb-mode)
  1627. X    (set-process-filter (get-buffer-process (current-buffer)) 'perldb-filter)
  1628. X    (set-process-sentinel (get-buffer-process (current-buffer)) 'perldb-sentinel)
  1629. X    (perldb-set-buffer)))
  1630. X
  1631. X(defun perldb-set-buffer ()
  1632. X  (cond ((eq major-mode 'perldb-mode)
  1633. X    (setq current-perldb-buffer (current-buffer)))))
  1634. X
  1635. X;; This function is responsible for inserting output from Perl
  1636. X;; into the buffer.
  1637. X;; Aside from inserting the text, it notices and deletes
  1638. X;; each filename-and-line-number;
  1639. X;; that Perl prints to identify the selected frame.
  1640. X;; It records the filename and line number, and maybe displays that file.
  1641. X(defun perldb-filter (proc string)
  1642. X  (let ((inhibit-quit t))
  1643. X    (if perldb-filter-accumulator
  1644. X    (perldb-filter-accumulate-marker proc
  1645. X                      (concat perldb-filter-accumulator string))
  1646. X    (perldb-filter-scan-input proc string))))
  1647. X
  1648. X(defun perldb-filter-accumulate-marker (proc string)
  1649. X  (setq perldb-filter-accumulator nil)
  1650. X  (if (> (length string) 1)
  1651. X      (if (= (aref string 1) ?\032)
  1652. X      (let ((end (string-match "\n" string)))
  1653. X        (if end
  1654. X        (progn
  1655. X          (let* ((first-colon (string-match ":" string 2))
  1656. X             (second-colon
  1657. X              (string-match ":" string (1+ first-colon))))
  1658. X            (setq perldb-last-frame
  1659. X              (cons (substring string 2 first-colon)
  1660. X                (string-to-int
  1661. X                 (substring string (1+ first-colon)
  1662. X                        second-colon)))))
  1663. X          (setq perldb-last-frame-displayed-p nil)
  1664. X          (perldb-filter-scan-input proc
  1665. X                     (substring string (1+ end))))
  1666. X          (setq perldb-filter-accumulator string)))
  1667. X    (perldb-filter-insert proc "\032")
  1668. X    (perldb-filter-scan-input proc (substring string 1)))
  1669. X    (setq perldb-filter-accumulator string)))
  1670. X
  1671. X(defun perldb-filter-scan-input (proc string)
  1672. X  (if (equal string "")
  1673. X      (setq perldb-filter-accumulator nil)
  1674. X      (let ((start (string-match "\032" string)))
  1675. X    (if start
  1676. X        (progn (perldb-filter-insert proc (substring string 0 start))
  1677. X           (perldb-filter-accumulate-marker proc
  1678. X                         (substring string start)))
  1679. X        (perldb-filter-insert proc string)))))
  1680. X
  1681. X(defun perldb-filter-insert (proc string)
  1682. X  (let ((moving (= (point) (process-mark proc)))
  1683. X    (output-after-point (< (point) (process-mark proc)))
  1684. X    (old-buffer (current-buffer))
  1685. X    start)
  1686. X    (set-buffer (process-buffer proc))
  1687. X    (unwind-protect
  1688. X    (save-excursion
  1689. X      ;; Insert the text, moving the process-marker.
  1690. X      (goto-char (process-mark proc))
  1691. X      (setq start (point))
  1692. X      (insert string)
  1693. X      (set-marker (process-mark proc) (point))
  1694. X      (perldb-maybe-delete-prompt)
  1695. X      ;; Check for a filename-and-line number.
  1696. X      (perldb-display-frame
  1697. X       ;; Don't display the specified file
  1698. X       ;; unless (1) point is at or after the position where output appears
  1699. X       ;; and (2) this buffer is on the screen.
  1700. X       (or output-after-point
  1701. X           (not (get-buffer-window (current-buffer))))
  1702. X       ;; Display a file only when a new filename-and-line-number appears.
  1703. X       t))
  1704. X      (set-buffer old-buffer))
  1705. X    (if moving (goto-char (process-mark proc)))))
  1706. X
  1707. X(defun perldb-sentinel (proc msg)
  1708. X  (cond ((null (buffer-name (process-buffer proc)))
  1709. X     ;; buffer killed
  1710. X     ;; Stop displaying an arrow in a source file.
  1711. X     (setq overlay-arrow-position nil)
  1712. X     (set-process-buffer proc nil))
  1713. X    ((memq (process-status proc) '(signal exit))
  1714. X     ;; Stop displaying an arrow in a source file.
  1715. X     (setq overlay-arrow-position nil)
  1716. X     ;; Fix the mode line.
  1717. X     (setq mode-line-process
  1718. X           (concat ": "
  1719. X               (symbol-name (process-status proc))))
  1720. X     (let* ((obuf (current-buffer)))
  1721. X       ;; save-excursion isn't the right thing if
  1722. X       ;;  process-buffer is current-buffer
  1723. X       (unwind-protect
  1724. X           (progn
  1725. X         ;; Write something in *compilation* and hack its mode line,
  1726. X         (set-buffer (process-buffer proc))
  1727. X         ;; Force mode line redisplay soon
  1728. X         (set-buffer-modified-p (buffer-modified-p))
  1729. X         (if (eobp)
  1730. X             (insert ?\n mode-name " " msg)
  1731. X           (save-excursion
  1732. X             (goto-char (point-max))
  1733. X             (insert ?\n mode-name " " msg)))
  1734. X         ;; If buffer and mode line will show that the process
  1735. X         ;; is dead, we can delete it now.  Otherwise it
  1736. X         ;; will stay around until M-x list-processes.
  1737. X         (delete-process proc))
  1738. X         ;; Restore old buffer, but don't restore old point
  1739. X         ;; if obuf is the perldb buffer.
  1740. X         (set-buffer obuf))))))
  1741. X
  1742. X
  1743. X(defun perldb-refresh ()
  1744. X  "Fix up a possibly garbled display, and redraw the arrow."
  1745. X  (interactive)
  1746. X  (redraw-display)
  1747. X  (perldb-display-frame))
  1748. X
  1749. X(defun perldb-display-frame (&optional nodisplay noauto)
  1750. X  "Find, obey and delete the last filename-and-line marker from PERLDB.
  1751. XThe marker looks like \\032\\032FILENAME:LINE:CHARPOS\\n.
  1752. XObeying it means displaying in another window the specified file and line."
  1753. X  (interactive)
  1754. X  (perldb-set-buffer)
  1755. X  (and perldb-last-frame (not nodisplay)
  1756. X       (or (not perldb-last-frame-displayed-p) (not noauto))
  1757. X       (progn (perldb-display-line (car perldb-last-frame) (cdr perldb-last-frame))
  1758. X          (setq perldb-last-frame-displayed-p t))))
  1759. X
  1760. X;; Make sure the file named TRUE-FILE is in a buffer that appears on the screen
  1761. X;; and that its line LINE is visible.
  1762. X;; Put the overlay-arrow on the line LINE in that buffer.
  1763. X
  1764. X(defun perldb-display-line (true-file line)
  1765. X  (let* ((buffer (find-file-noselect true-file))
  1766. X     (window (display-buffer buffer t))
  1767. X     (pos))
  1768. X    (save-excursion
  1769. X      (set-buffer buffer)
  1770. X      (save-restriction
  1771. X    (widen)
  1772. X    (goto-line line)
  1773. X    (setq pos (point))
  1774. X    (setq overlay-arrow-string "=>")
  1775. X    (or overlay-arrow-position
  1776. X        (setq overlay-arrow-position (make-marker)))
  1777. X    (set-marker overlay-arrow-position (point) (current-buffer)))
  1778. X      (cond ((or (< pos (point-min)) (> pos (point-max)))
  1779. X         (widen)
  1780. X         (goto-char pos))))
  1781. X    (set-window-point window overlay-arrow-position)))
  1782. X
  1783. X(defun perldb-call (command)
  1784. X  "Invoke perldb COMMAND displaying source in other window."
  1785. X  (interactive)
  1786. X  (goto-char (point-max))
  1787. X  (setq perldb-delete-prompt-marker (point-marker))
  1788. X  (perldb-set-buffer)
  1789. X  (send-string (get-buffer-process current-perldb-buffer)
  1790. X           (concat command "\n")))
  1791. X
  1792. X(defun perldb-maybe-delete-prompt ()
  1793. X  (if (and perldb-delete-prompt-marker
  1794. X       (> (point-max) (marker-position perldb-delete-prompt-marker)))
  1795. X      (let (start)
  1796. X    (goto-char perldb-delete-prompt-marker)
  1797. X    (setq start (point))
  1798. X    (beginning-of-line)
  1799. X    (delete-region (point) start)
  1800. X    (setq perldb-delete-prompt-marker nil))))
  1801. X
  1802. X(defun perldb-break ()
  1803. X  "Set PERLDB breakpoint at this source line."
  1804. X  (interactive)
  1805. X  (let ((line (save-restriction
  1806. X        (widen)
  1807. X        (1+ (count-lines 1 (point))))))
  1808. X    (send-string (get-buffer-process current-perldb-buffer)
  1809. X         (concat "b " line "\n"))))
  1810. X
  1811. X(defun perldb-read-token()
  1812. X  "Return a string containing the token found in the buffer at point.
  1813. XA token can be a number or an identifier.  If the token is a name prefaced
  1814. Xby `$', `@', or `%', the leading character is included in the token."
  1815. X  (save-excursion
  1816. X    (let (begin)
  1817. X      (or (looking-at "[$@%]")
  1818. X      (re-search-backward "[^a-zA-Z_0-9]" (point-min) 'move))
  1819. X      (setq begin (point))
  1820. X      (or (looking-at "[$@%]") (setq begin (+ begin 1)))
  1821. X      (forward-char 1)
  1822. X      (buffer-substring begin
  1823. X            (if (re-search-forward "[^a-zA-Z_0-9]"
  1824. X                           (point-max) 'move)
  1825. X                   (- (point) 1)
  1826. X              (point)))
  1827. X)))
  1828. X
  1829. X(defvar perldb-commands nil
  1830. X  "List of strings or functions used by send-perldb-command.
  1831. XIt is for customization by the user.")
  1832. X
  1833. X(defun send-perldb-command (arg)
  1834. X  "Issue a Perl debugger command selected by the prefix arg.  A numeric
  1835. Xarg selects the ARG'th member COMMAND of the list perldb-commands.
  1836. XThe token under the cursor is passed to the command.  If COMMAND is a
  1837. Xstring, (format COMMAND TOKEN) is inserted at the end of the perldb
  1838. Xbuffer, otherwise (funcall COMMAND TOKEN) is inserted.  If there is
  1839. Xno such COMMAND, then the token itself is inserted.  For example,
  1840. X\"p %s\" is a possible string to be a member of perldb-commands,
  1841. Xor \"p $ENV{%s}\"."
  1842. X  (interactive "P")
  1843. X  (let (comm token)
  1844. X    (if arg (setq comm (nth arg perldb-commands)))
  1845. X    (setq token (perldb-read-token))
  1846. X    (if (eq (current-buffer) current-perldb-buffer)
  1847. X    (set-mark (point)))
  1848. X    (cond (comm
  1849. X       (setq comm
  1850. X         (if (stringp comm) (format comm token) (funcall comm token))))
  1851. X      (t (setq comm token)))
  1852. X    (switch-to-buffer-other-window current-perldb-buffer)
  1853. X    (goto-char (dot-max))
  1854. X    (insert-string comm)))
  1855. !STUFFY!FUNK!
  1856. echo Extracting eg/scan/scan_ps
  1857. sed >eg/scan/scan_ps <<'!STUFFY!FUNK!' -e 's/X//'
  1858. X#!/usr/bin/perl -P
  1859. X
  1860. X# $Header: scan_ps,v 4.0 91/03/20 01:13:29 lwall Locked $
  1861. X
  1862. X# This looks for looping processes.
  1863. X
  1864. X#if defined(mc300) || defined(mc500) || defined(mc700)
  1865. Xopen(Ps, '/bin/ps -el|') || die "scan_ps: can't run ps";
  1866. X
  1867. Xwhile (<Ps>) {
  1868. X    next if /rwhod/;
  1869. X    print if index(' T', substr($_,62,1)) < 0;
  1870. X}
  1871. X#else
  1872. Xopen(Ps, '/bin/ps auxww|') || die "scan_ps: can't run ps";
  1873. X
  1874. Xwhile (<Ps>) {
  1875. X    next if /dataserver/;
  1876. X    next if /nfsd/;
  1877. X    next if /update/;
  1878. X    next if /ypserv/;
  1879. X    next if /rwhod/;
  1880. X    next if /routed/;
  1881. X    next if /pagedaemon/;
  1882. X#ifdef vax
  1883. X    ($user,$pid,$cpu,$mem,$sz,$rss,$tt,$stat,$start,$time) = split;
  1884. X#else
  1885. X    ($user,$pid,$cpu,$mem,$sz,$rss,$tt,$stat,$time) = split;
  1886. X#endif
  1887. X    print if length($time) > 4;
  1888. X}
  1889. X#endif
  1890. !STUFFY!FUNK!
  1891. echo " "
  1892. echo "End of kit 18 (of 36)"
  1893. cat /dev/null >kit18isdone
  1894. run=''
  1895. config=''
  1896. for iskit in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36; do
  1897.     if test -f kit${iskit}isdone; then
  1898.     run="$run $iskit"
  1899.     else
  1900.     todo="$todo $iskit"
  1901.     fi
  1902. done
  1903. case $todo in
  1904.     '')
  1905.     echo "You have run all your kits.  Please read README and then type Configure."
  1906.     for combo in *:AA; do
  1907.         if test -f "$combo"; then
  1908.         realfile=`basename $combo :AA`
  1909.         cat $realfile:[A-Z][A-Z] >$realfile
  1910.         rm -rf $realfile:[A-Z][A-Z]
  1911.         fi
  1912.     done
  1913.     rm -rf kit*isdone
  1914.     chmod 755 Configure
  1915.     ;;
  1916.     *)  echo "You have run$run."
  1917.     echo "You still need to run$todo."
  1918.     ;;
  1919. esac
  1920. : Someone might mail this, so...
  1921. exit
  1922.  
  1923. exit 0 # Just in case...
  1924. -- 
  1925. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1926. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1927. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1928. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1929.