1 /*
2  * NFA utilities.
3  * This file is #included by regcomp.c.
4  *
5  * Copyright (c) 1998, 1999 Henry Spencer.  All rights reserved.
6  *
7  * Development of this software was funded, in part, by Cray Research Inc.,
8  * UUNET Communications Services Inc., Sun Microsystems Inc., and Scriptics
9  * Corporation, none of whom are responsible for the results.  The author
10  * thanks all of them.
11  *
12  * Redistribution and use in source and binary forms -- with or without
13  * modification -- are permitted for any purpose, provided that
14  * redistributions in source form retain this entire copyright notice and
15  * indicate the origin and nature of any modifications.
16  *
17  * I'd appreciate being given credit for this package in the documentation
18  * of software which uses it, but that is not a requirement.
19  *
20  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
23  * HENRY SPENCER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  *
32  *
33  * One or two things that technically ought to be in here
34  * are actually in color.c, thanks to some incestuous relationships in
35  * the color chains.
36  */
37 
38 #define	NISERR()	VISERR(nfa->v)
39 #define	NERR(e)		VERR(nfa->v, (e))
40 
41 
42 /*
43  - newnfa - set up an NFA
44  ^ static struct nfa *newnfa(struct vars *, struct colormap *, struct nfa *);
45  */
46 static struct nfa *		/* the NFA, or NULL */
newnfa(v,cm,parent)47 newnfa(v, cm, parent)
48 struct vars *v;
49 struct colormap *cm;
50 struct nfa *parent;		/* NULL if primary NFA */
51 {
52 	struct nfa *nfa;
53 
54 	nfa = (struct nfa *)MALLOC(sizeof(struct nfa));
55 	if (nfa == NULL)
56 		return NULL;
57 
58 	nfa->states = NULL;
59 	nfa->slast = NULL;
60 	nfa->free = NULL;
61 	nfa->nstates = 0;
62 	nfa->cm = cm;
63 	nfa->v = v;
64 	nfa->bos[0] = nfa->bos[1] = COLORLESS;
65 	nfa->eos[0] = nfa->eos[1] = COLORLESS;
66 	nfa->post = newfstate(nfa, '@');	/* number 0 */
67 	nfa->pre = newfstate(nfa, '>');		/* number 1 */
68 	nfa->parent = parent;
69 
70 	nfa->init = newstate(nfa);		/* may become invalid later */
71 	nfa->final = newstate(nfa);
72 	if (ISERR()) {
73 		freenfa(nfa);
74 		return NULL;
75 	}
76 	rainbow(nfa, nfa->cm, PLAIN, COLORLESS, nfa->pre, nfa->init);
77 	newarc(nfa, '^', 1, nfa->pre, nfa->init);
78 	newarc(nfa, '^', 0, nfa->pre, nfa->init);
79 	rainbow(nfa, nfa->cm, PLAIN, COLORLESS, nfa->final, nfa->post);
80 	newarc(nfa, '$', 1, nfa->final, nfa->post);
81 	newarc(nfa, '$', 0, nfa->final, nfa->post);
82 
83 	if (ISERR()) {
84 		freenfa(nfa);
85 		return NULL;
86 	}
87 	return nfa;
88 }
89 
90 /*
91  - freenfa - free an entire NFA
92  ^ static VOID freenfa(struct nfa *);
93  */
94 static VOID
freenfa(nfa)95 freenfa(nfa)
96 struct nfa *nfa;
97 {
98 	struct state *s;
99 
100 	while ((s = nfa->states) != NULL) {
101 		s->nins = s->nouts = 0;		/* don't worry about arcs */
102 		freestate(nfa, s);
103 	}
104 	while ((s = nfa->free) != NULL) {
105 		nfa->free = s->next;
106 		destroystate(nfa, s);
107 	}
108 
109 	nfa->slast = NULL;
110 	nfa->nstates = -1;
111 	nfa->pre = NULL;
112 	nfa->post = NULL;
113 	FREE(nfa);
114 }
115 
116 /*
117  - newstate - allocate an NFA state, with zero flag value
118  ^ static struct state *newstate(struct nfa *);
119  */
120 static struct state *		/* NULL on error */
newstate(nfa)121 newstate(nfa)
122 struct nfa *nfa;
123 {
124 	struct state *s;
125 
126 	if (nfa->free != NULL) {
127 		s = nfa->free;
128 		nfa->free = s->next;
129 	} else {
130 		s = (struct state *)MALLOC(sizeof(struct state));
131 		if (s == NULL) {
132 			NERR(REG_ESPACE);
133 			return NULL;
134 		}
135 		s->oas.next = NULL;
136 		s->free = NULL;
137 		s->noas = 0;
138 	}
139 
140 	assert(nfa->nstates >= 0);
141 	s->no = nfa->nstates++;
142 	s->flag = 0;
143 	if (nfa->states == NULL)
144 		nfa->states = s;
145 	s->nins = 0;
146 	s->ins = NULL;
147 	s->nouts = 0;
148 	s->outs = NULL;
149 	s->tmp = NULL;
150 	s->next = NULL;
151 	if (nfa->slast != NULL) {
152 		assert(nfa->slast->next == NULL);
153 		nfa->slast->next = s;
154 	}
155 	s->prev = nfa->slast;
156 	nfa->slast = s;
157 	return s;
158 }
159 
160 /*
161  - newfstate - allocate an NFA state with a specified flag value
162  ^ static struct state *newfstate(struct nfa *, int flag);
163  */
164 static struct state *		/* NULL on error */
newfstate(nfa,flag)165 newfstate(nfa, flag)
166 struct nfa *nfa;
167 int flag;
168 {
169 	struct state *s;
170 
171 	s = newstate(nfa);
172 	if (s != NULL)
173 		s->flag = (char)flag;
174 	return s;
175 }
176 
177 /*
178  - dropstate - delete a state's inarcs and outarcs and free it
179  ^ static VOID dropstate(struct nfa *, struct state *);
180  */
181 static VOID
dropstate(nfa,s)182 dropstate(nfa, s)
183 struct nfa *nfa;
184 struct state *s;
185 {
186 	struct arc *a;
187 
188 	while ((a = s->ins) != NULL)
189 		freearc(nfa, a);
190 	while ((a = s->outs) != NULL)
191 		freearc(nfa, a);
192 	freestate(nfa, s);
193 }
194 
195 /*
196  - freestate - free a state, which has no in-arcs or out-arcs
197  ^ static VOID freestate(struct nfa *, struct state *);
198  */
199 static VOID
freestate(nfa,s)200 freestate(nfa, s)
201 struct nfa *nfa;
202 struct state *s;
203 {
204 	assert(s != NULL);
205 	assert(s->nins == 0 && s->nouts == 0);
206 
207 	s->no = FREESTATE;
208 	s->flag = 0;
209 	if (s->next != NULL)
210 		s->next->prev = s->prev;
211 	else {
212 		assert(s == nfa->slast);
213 		nfa->slast = s->prev;
214 	}
215 	if (s->prev != NULL)
216 		s->prev->next = s->next;
217 	else {
218 		assert(s == nfa->states);
219 		nfa->states = s->next;
220 	}
221 	s->prev = NULL;
222 	s->next = nfa->free;	/* don't delete it, put it on the free list */
223 	nfa->free = s;
224 }
225 
226 /*
227  - destroystate - really get rid of an already-freed state
228  ^ static VOID destroystate(struct nfa *, struct state *);
229  */
230 static VOID
destroystate(nfa,s)231 destroystate(nfa, s)
232 struct nfa *nfa;
233 struct state *s;
234 {
235 	struct arcbatch *ab;
236 	struct arcbatch *abnext;
237 
238 	assert(s->no == FREESTATE);
239 	for (ab = s->oas.next; ab != NULL; ab = abnext) {
240 		abnext = ab->next;
241 		FREE(ab);
242 	}
243 	s->ins = NULL;
244 	s->outs = NULL;
245 	s->next = NULL;
246 	FREE(s);
247 }
248 
249 /*
250  - newarc - set up a new arc within an NFA
251  ^ static VOID newarc(struct nfa *, int, pcolor, struct state *,
252  ^	struct state *);
253  */
254 static VOID
newarc(nfa,t,co,from,to)255 newarc(nfa, t, co, from, to)
256 struct nfa *nfa;
257 int t;
258 pcolor co;
259 struct state *from;
260 struct state *to;
261 {
262 	struct arc *a;
263 
264 	assert(from != NULL && to != NULL);
265 
266 	/* check for duplicates */
267 	for (a = from->outs; a != NULL; a = a->outchain)
268 		if (a->to == to && a->co == co && a->type == t)
269 			return;
270 
271 	a = allocarc(nfa, from);
272 	if (NISERR())
273 		return;
274 	assert(a != NULL);
275 
276 	a->type = t;
277 	a->co = (color)co;
278 	a->to = to;
279 	a->from = from;
280 
281 	/*
282 	 * Put the new arc on the beginning, not the end, of the chains.
283 	 * Not only is this easier, it has the very useful side effect that
284 	 * deleting the most-recently-added arc is the cheapest case rather
285 	 * than the most expensive one.
286 	 */
287 	a->inchain = to->ins;
288 	to->ins = a;
289 	a->outchain = from->outs;
290 	from->outs = a;
291 
292 	from->nouts++;
293 	to->nins++;
294 
295 	if (COLORED(a) && nfa->parent == NULL)
296 		colorchain(nfa->cm, a);
297 
298 	return;
299 }
300 
301 /*
302  - allocarc - allocate a new out-arc within a state
303  ^ static struct arc *allocarc(struct nfa *, struct state *);
304  */
305 static struct arc *		/* NULL for failure */
allocarc(nfa,s)306 allocarc(nfa, s)
307 struct nfa *nfa;
308 struct state *s;
309 {
310 	struct arc *a;
311 	struct arcbatch *new;
312 	int i;
313 
314 	/* shortcut */
315 	if (s->free == NULL && s->noas < ABSIZE) {
316 		a = &s->oas.a[s->noas];
317 		s->noas++;
318 		return a;
319 	}
320 
321 	/* if none at hand, get more */
322 	if (s->free == NULL) {
323 		new = (struct arcbatch *)MALLOC(sizeof(struct arcbatch));
324 		if (new == NULL) {
325 			NERR(REG_ESPACE);
326 			return NULL;
327 		}
328 		new->next = s->oas.next;
329 		s->oas.next = new;
330 
331 		for (i = 0; i < ABSIZE; i++) {
332 			new->a[i].type = 0;
333 			new->a[i].freechain = &new->a[i+1];
334 		}
335 		new->a[ABSIZE-1].freechain = NULL;
336 		s->free = &new->a[0];
337 	}
338 	assert(s->free != NULL);
339 
340 	a = s->free;
341 	s->free = a->freechain;
342 	return a;
343 }
344 
345 /*
346  - freearc - free an arc
347  ^ static VOID freearc(struct nfa *, struct arc *);
348  */
349 static VOID
freearc(nfa,victim)350 freearc(nfa, victim)
351 struct nfa *nfa;
352 struct arc *victim;
353 {
354 	struct state *from = victim->from;
355 	struct state *to = victim->to;
356 	struct arc *a;
357 
358 	assert(victim->type != 0);
359 
360 	/* take it off color chain if necessary */
361 	if (COLORED(victim) && nfa->parent == NULL)
362 		uncolorchain(nfa->cm, victim);
363 
364 	/* take it off source's out-chain */
365 	assert(from != NULL);
366 	assert(from->outs != NULL);
367 	a = from->outs;
368 	if (a == victim)		/* simple case:  first in chain */
369 		from->outs = victim->outchain;
370 	else {
371 		for (; a != NULL && a->outchain != victim; a = a->outchain)
372 			continue;
373 		assert(a != NULL);
374 		a->outchain = victim->outchain;
375 	}
376 	from->nouts--;
377 
378 	/* take it off target's in-chain */
379 	assert(to != NULL);
380 	assert(to->ins != NULL);
381 	a = to->ins;
382 	if (a == victim)		/* simple case:  first in chain */
383 		to->ins = victim->inchain;
384 	else {
385 		for (; a != NULL && a->inchain != victim; a = a->inchain)
386 			continue;
387 		assert(a != NULL);
388 		a->inchain = victim->inchain;
389 	}
390 	to->nins--;
391 
392 	/* clean up and place on free list */
393 	victim->type = 0;
394 	victim->from = NULL;		/* precautions... */
395 	victim->to = NULL;
396 	victim->inchain = NULL;
397 	victim->outchain = NULL;
398 	victim->freechain = from->free;
399 	from->free = victim;
400 }
401 
402 /*
403  - findarc - find arc, if any, from given source with given type and color
404  * If there is more than one such arc, the result is random.
405  ^ static struct arc *findarc(struct state *, int, pcolor);
406  */
407 static struct arc *
findarc(s,type,co)408 findarc(s, type, co)
409 struct state *s;
410 int type;
411 pcolor co;
412 {
413 	struct arc *a;
414 
415 	for (a = s->outs; a != NULL; a = a->outchain)
416 		if (a->type == type && a->co == co)
417 			return a;
418 	return NULL;
419 }
420 
421 /*
422  - cparc - allocate a new arc within an NFA, copying details from old one
423  ^ static VOID cparc(struct nfa *, struct arc *, struct state *,
424  ^ 	struct state *);
425  */
426 static VOID
cparc(nfa,oa,from,to)427 cparc(nfa, oa, from, to)
428 struct nfa *nfa;
429 struct arc *oa;
430 struct state *from;
431 struct state *to;
432 {
433 	newarc(nfa, oa->type, oa->co, from, to);
434 }
435 
436 /*
437  - moveins - move all in arcs of a state to another state
438  * You might think this could be done better by just updating the
439  * existing arcs, and you would be right if it weren't for the desire
440  * for duplicate suppression, which makes it easier to just make new
441  * ones to exploit the suppression built into newarc.
442  ^ static VOID moveins(struct nfa *, struct state *, struct state *);
443  */
444 static VOID
moveins(nfa,old,new)445 moveins(nfa, old, new)
446 struct nfa *nfa;
447 struct state *old;
448 struct state *new;
449 {
450 	struct arc *a;
451 
452 	assert(old != new);
453 
454 	while ((a = old->ins) != NULL) {
455 		cparc(nfa, a, a->from, new);
456 		freearc(nfa, a);
457 	}
458 	assert(old->nins == 0);
459 	assert(old->ins == NULL);
460 }
461 
462 /*
463  - copyins - copy all in arcs of a state to another state
464  ^ static VOID copyins(struct nfa *, struct state *, struct state *);
465  */
466 static VOID
copyins(nfa,old,new)467 copyins(nfa, old, new)
468 struct nfa *nfa;
469 struct state *old;
470 struct state *new;
471 {
472 	struct arc *a;
473 
474 	assert(old != new);
475 
476 	for (a = old->ins; a != NULL; a = a->inchain)
477 		cparc(nfa, a, a->from, new);
478 }
479 
480 /*
481  - moveouts - move all out arcs of a state to another state
482  ^ static VOID moveouts(struct nfa *, struct state *, struct state *);
483  */
484 static VOID
moveouts(nfa,old,new)485 moveouts(nfa, old, new)
486 struct nfa *nfa;
487 struct state *old;
488 struct state *new;
489 {
490 	struct arc *a;
491 
492 	assert(old != new);
493 
494 	while ((a = old->outs) != NULL) {
495 		cparc(nfa, a, new, a->to);
496 		freearc(nfa, a);
497 	}
498 }
499 
500 /*
501  - copyouts - copy all out arcs of a state to another state
502  ^ static VOID copyouts(struct nfa *, struct state *, struct state *);
503  */
504 static VOID
copyouts(nfa,old,new)505 copyouts(nfa, old, new)
506 struct nfa *nfa;
507 struct state *old;
508 struct state *new;
509 {
510 	struct arc *a;
511 
512 	assert(old != new);
513 
514 	for (a = old->outs; a != NULL; a = a->outchain)
515 		cparc(nfa, a, new, a->to);
516 }
517 
518 /*
519  - cloneouts - copy out arcs of a state to another state pair, modifying type
520  ^ static VOID cloneouts(struct nfa *, struct state *, struct state *,
521  ^ 	struct state *, int);
522  */
523 static VOID
cloneouts(nfa,old,from,to,type)524 cloneouts(nfa, old, from, to, type)
525 struct nfa *nfa;
526 struct state *old;
527 struct state *from;
528 struct state *to;
529 int type;
530 {
531 	struct arc *a;
532 
533 	assert(old != from);
534 
535 	for (a = old->outs; a != NULL; a = a->outchain)
536 		newarc(nfa, type, a->co, from, to);
537 }
538 
539 /*
540  - delsub - delete a sub-NFA, updating subre pointers if necessary
541  * This uses a recursive traversal of the sub-NFA, marking already-seen
542  * states using their tmp pointer.
543  ^ static VOID delsub(struct nfa *, struct state *, struct state *);
544  */
545 static VOID
delsub(nfa,lp,rp)546 delsub(nfa, lp, rp)
547 struct nfa *nfa;
548 struct state *lp;	/* the sub-NFA goes from here... */
549 struct state *rp;	/* ...to here, *not* inclusive */
550 {
551 	assert(lp != rp);
552 
553 	rp->tmp = rp;			/* mark end */
554 
555 	deltraverse(nfa, lp, lp);
556 	assert(lp->nouts == 0 && rp->nins == 0);	/* did the job */
557 	assert(lp->no != FREESTATE && rp->no != FREESTATE);	/* no more */
558 
559 	rp->tmp = NULL;			/* unmark end */
560 	lp->tmp = NULL;			/* and begin, marked by deltraverse */
561 }
562 
563 /*
564  - deltraverse - the recursive heart of delsub
565  * This routine's basic job is to destroy all out-arcs of the state.
566  ^ static VOID deltraverse(struct nfa *, struct state *, struct state *);
567  */
568 static VOID
deltraverse(nfa,leftend,s)569 deltraverse(nfa, leftend, s)
570 struct nfa *nfa;
571 struct state *leftend;
572 struct state *s;
573 {
574 	struct arc *a;
575 	struct state *to;
576 
577 	if (s->nouts == 0)
578 		return;			/* nothing to do */
579 	if (s->tmp != NULL)
580 		return;			/* already in progress */
581 
582 	s->tmp = s;			/* mark as in progress */
583 
584 	while ((a = s->outs) != NULL) {
585 		to = a->to;
586 		deltraverse(nfa, leftend, to);
587 		assert(to->nouts == 0 || to->tmp != NULL);
588 		freearc(nfa, a);
589 		if (to->nins == 0 && to->tmp == NULL) {
590 			assert(to->nouts == 0);
591 			freestate(nfa, to);
592 		}
593 	}
594 
595 	assert(s->no != FREESTATE);	/* we're still here */
596 	assert(s == leftend || s->nins != 0);	/* and still reachable */
597 	assert(s->nouts == 0);		/* but have no outarcs */
598 
599 	s->tmp = NULL;			/* we're done here */
600 }
601 
602 /*
603  - dupnfa - duplicate sub-NFA
604  * Another recursive traversal, this time using tmp to point to duplicates
605  * as well as mark already-seen states.  (You knew there was a reason why
606  * it's a state pointer, didn't you? :-))
607  ^ static VOID dupnfa(struct nfa *, struct state *, struct state *,
608  ^ 	struct state *, struct state *);
609  */
610 static VOID
dupnfa(nfa,start,stop,from,to)611 dupnfa(nfa, start, stop, from, to)
612 struct nfa *nfa;
613 struct state *start;		/* duplicate of subNFA starting here */
614 struct state *stop;		/* and stopping here */
615 struct state *from;		/* stringing duplicate from here */
616 struct state *to;		/* to here */
617 {
618 	if (start == stop) {
619 		newarc(nfa, EMPTY, 0, from, to);
620 		return;
621 	}
622 
623 	stop->tmp = to;
624 	duptraverse(nfa, start, from);
625 	/* done, except for clearing out the tmp pointers */
626 
627 	stop->tmp = NULL;
628 	cleartraverse(nfa, start);
629 }
630 
631 /*
632  - duptraverse - recursive heart of dupnfa
633  ^ static VOID duptraverse(struct nfa *, struct state *, struct state *);
634  */
635 static VOID
duptraverse(nfa,s,stmp)636 duptraverse(nfa, s, stmp)
637 struct nfa *nfa;
638 struct state *s;
639 struct state *stmp;		/* s's duplicate, or NULL */
640 {
641 	struct arc *a;
642 
643 	if (s->tmp != NULL)
644 		return;		/* already done */
645 
646 	s->tmp = (stmp == NULL) ? newstate(nfa) : stmp;
647 	if (s->tmp == NULL) {
648 		assert(NISERR());
649 		return;
650 	}
651 
652 	for (a = s->outs; a != NULL && !NISERR(); a = a->outchain) {
653 		duptraverse(nfa, a->to, (struct state *)NULL);
654 		assert(a->to->tmp != NULL);
655 		cparc(nfa, a, s->tmp, a->to->tmp);
656 	}
657 }
658 
659 /*
660  - cleartraverse - recursive cleanup for algorithms that leave tmp ptrs set
661  ^ static VOID cleartraverse(struct nfa *, struct state *);
662  */
663 static VOID
cleartraverse(nfa,s)664 cleartraverse(nfa, s)
665 struct nfa *nfa;
666 struct state *s;
667 {
668 	struct arc *a;
669 
670 	if (s->tmp == NULL)
671 		return;
672 	s->tmp = NULL;
673 
674 	for (a = s->outs; a != NULL; a = a->outchain)
675 		cleartraverse(nfa, a->to);
676 }
677 
678 /*
679  - specialcolors - fill in special colors for an NFA
680  ^ static VOID specialcolors(struct nfa *);
681  */
682 static VOID
specialcolors(nfa)683 specialcolors(nfa)
684 struct nfa *nfa;
685 {
686 	/* false colors for BOS, BOL, EOS, EOL */
687 	if (nfa->parent == NULL) {
688 		nfa->bos[0] = pseudocolor(nfa->cm);
689 		nfa->bos[1] = pseudocolor(nfa->cm);
690 		nfa->eos[0] = pseudocolor(nfa->cm);
691 		nfa->eos[1] = pseudocolor(nfa->cm);
692 	} else {
693 		assert(nfa->parent->bos[0] != COLORLESS);
694 		nfa->bos[0] = nfa->parent->bos[0];
695 		assert(nfa->parent->bos[1] != COLORLESS);
696 		nfa->bos[1] = nfa->parent->bos[1];
697 		assert(nfa->parent->eos[0] != COLORLESS);
698 		nfa->eos[0] = nfa->parent->eos[0];
699 		assert(nfa->parent->eos[1] != COLORLESS);
700 		nfa->eos[1] = nfa->parent->eos[1];
701 	}
702 }
703 
704 /*
705  - optimize - optimize an NFA
706  ^ static long optimize(struct nfa *, FILE *);
707  */
708 static long			/* re_info bits */
optimize(nfa,f)709 optimize(nfa, f)
710 struct nfa *nfa;
711 FILE *f;			/* for debug output; NULL none */
712 {
713 	int verbose = (f != NULL) ? 1 : 0;
714 
715 	if (verbose)
716 		fprintf(f, "\ninitial cleanup:\n");
717 	cleanup(nfa);		/* may simplify situation */
718 	if (verbose)
719 		dumpnfa(nfa, f);
720 	if (verbose)
721 		fprintf(f, "\nempties:\n");
722 	fixempties(nfa, f);	/* get rid of EMPTY arcs */
723 	if (verbose)
724 		fprintf(f, "\nconstraints:\n");
725 	pullback(nfa, f);	/* pull back constraints backward */
726 	pushfwd(nfa, f);	/* push fwd constraints forward */
727 	if (verbose)
728 		fprintf(f, "\nfinal cleanup:\n");
729 	cleanup(nfa);		/* final tidying */
730 	return analyze(nfa);	/* and analysis */
731 }
732 
733 /*
734  - pullback - pull back constraints backward to (with luck) eliminate them
735  ^ static VOID pullback(struct nfa *, FILE *);
736  */
737 static VOID
pullback(nfa,f)738 pullback(nfa, f)
739 struct nfa *nfa;
740 FILE *f;			/* for debug output; NULL none */
741 {
742 	struct state *s;
743 	struct state *nexts;
744 	struct arc *a;
745 	struct arc *nexta;
746 	int progress;
747 
748 	/* find and pull until there are no more */
749 	do {
750 		progress = 0;
751 		for (s = nfa->states; s != NULL && !NISERR(); s = nexts) {
752 			nexts = s->next;
753 			for (a = s->outs; a != NULL && !NISERR(); a = nexta) {
754 				nexta = a->outchain;
755 				if (a->type == '^' || a->type == BEHIND)
756 					if (pull(nfa, a))
757 						progress = 1;
758 				assert(nexta == NULL || s->no != FREESTATE);
759 			}
760 		}
761 		if (progress && f != NULL)
762 			dumpnfa(nfa, f);
763 	} while (progress && !NISERR());
764 	if (NISERR())
765 		return;
766 
767 	for (a = nfa->pre->outs; a != NULL; a = nexta) {
768 		nexta = a->outchain;
769 		if (a->type == '^') {
770 			assert(a->co == 0 || a->co == 1);
771 			newarc(nfa, PLAIN, nfa->bos[a->co], a->from, a->to);
772 			freearc(nfa, a);
773 		}
774 	}
775 }
776 
777 /*
778  - pull - pull a back constraint backward past its source state
779  * A significant property of this function is that it deletes at most
780  * one state -- the constraint's from state -- and only if the constraint
781  * was that state's last outarc.
782  ^ static int pull(struct nfa *, struct arc *);
783  */
784 static int			/* 0 couldn't, 1 could */
pull(nfa,con)785 pull(nfa, con)
786 struct nfa *nfa;
787 struct arc *con;
788 {
789 	struct state *from = con->from;
790 	struct state *to = con->to;
791 	struct arc *a;
792 	struct arc *nexta;
793 	struct state *s;
794 
795 	if (from == to) {	/* circular constraint is pointless */
796 		freearc(nfa, con);
797 		return 1;
798 	}
799 	if (from->flag)		/* can't pull back beyond start */
800 		return 0;
801 	if (from->nins == 0) {	/* unreachable */
802 		freearc(nfa, con);
803 		return 1;
804 	}
805 
806 	/* first, clone from state if necessary to avoid other outarcs */
807 	if (from->nouts > 1) {
808 		s = newstate(nfa);
809 		if (NISERR())
810 			return 0;
811 		assert(to != from);		/* con is not an inarc */
812 		copyins(nfa, from, s);		/* duplicate inarcs */
813 		cparc(nfa, con, s, to);		/* move constraint arc */
814 		freearc(nfa, con);
815 		from = s;
816 		con = from->outs;
817 	}
818 	assert(from->nouts == 1);
819 
820 	/* propagate the constraint into the from state's inarcs */
821 	for (a = from->ins; a != NULL; a = nexta) {
822 		nexta = a->inchain;
823 		switch (combine(con, a)) {
824 		case INCOMPATIBLE:	/* destroy the arc */
825 			freearc(nfa, a);
826 			break;
827 		case SATISFIED:		/* no action needed */
828 			break;
829 		case COMPATIBLE:	/* swap the two arcs, more or less */
830 			s = newstate(nfa);
831 			if (NISERR())
832 				return 0;
833 			cparc(nfa, a, s, to);		/* anticipate move */
834 			cparc(nfa, con, a->from, s);
835 			if (NISERR())
836 				return 0;
837 			freearc(nfa, a);
838 			break;
839 		default:
840 			assert(NOTREACHED);
841 			break;
842 		}
843 	}
844 
845 	/* remaining inarcs, if any, incorporate the constraint */
846 	moveins(nfa, from, to);
847 	dropstate(nfa, from);		/* will free the constraint */
848 	return 1;
849 }
850 
851 /*
852  - pushfwd - push forward constraints forward to (with luck) eliminate them
853  ^ static VOID pushfwd(struct nfa *, FILE *);
854  */
855 static VOID
pushfwd(nfa,f)856 pushfwd(nfa, f)
857 struct nfa *nfa;
858 FILE *f;			/* for debug output; NULL none */
859 {
860 	struct state *s;
861 	struct state *nexts;
862 	struct arc *a;
863 	struct arc *nexta;
864 	int progress;
865 
866 	/* find and push until there are no more */
867 	do {
868 		progress = 0;
869 		for (s = nfa->states; s != NULL && !NISERR(); s = nexts) {
870 			nexts = s->next;
871 			for (a = s->ins; a != NULL && !NISERR(); a = nexta) {
872 				nexta = a->inchain;
873 				if (a->type == '$' || a->type == AHEAD)
874 					if (push(nfa, a))
875 						progress = 1;
876 				assert(nexta == NULL || s->no != FREESTATE);
877 			}
878 		}
879 		if (progress && f != NULL)
880 			dumpnfa(nfa, f);
881 	} while (progress && !NISERR());
882 	if (NISERR())
883 		return;
884 
885 	for (a = nfa->post->ins; a != NULL; a = nexta) {
886 		nexta = a->inchain;
887 		if (a->type == '$') {
888 			assert(a->co == 0 || a->co == 1);
889 			newarc(nfa, PLAIN, nfa->eos[a->co], a->from, a->to);
890 			freearc(nfa, a);
891 		}
892 	}
893 }
894 
895 /*
896  - push - push a forward constraint forward past its destination state
897  * A significant property of this function is that it deletes at most
898  * one state -- the constraint's to state -- and only if the constraint
899  * was that state's last inarc.
900  ^ static int push(struct nfa *, struct arc *);
901  */
902 static int			/* 0 couldn't, 1 could */
push(nfa,con)903 push(nfa, con)
904 struct nfa *nfa;
905 struct arc *con;
906 {
907 	struct state *from = con->from;
908 	struct state *to = con->to;
909 	struct arc *a;
910 	struct arc *nexta;
911 	struct state *s;
912 
913 	if (to == from) {	/* circular constraint is pointless */
914 		freearc(nfa, con);
915 		return 1;
916 	}
917 	if (to->flag)		/* can't push forward beyond end */
918 		return 0;
919 	if (to->nouts == 0) {	/* dead end */
920 		freearc(nfa, con);
921 		return 1;
922 	}
923 
924 	/* first, clone to state if necessary to avoid other inarcs */
925 	if (to->nins > 1) {
926 		s = newstate(nfa);
927 		if (NISERR())
928 			return 0;
929 		copyouts(nfa, to, s);		/* duplicate outarcs */
930 		cparc(nfa, con, from, s);	/* move constraint */
931 		freearc(nfa, con);
932 		to = s;
933 		con = to->ins;
934 	}
935 	assert(to->nins == 1);
936 
937 	/* propagate the constraint into the to state's outarcs */
938 	for (a = to->outs; a != NULL; a = nexta) {
939 		nexta = a->outchain;
940 		switch (combine(con, a)) {
941 		case INCOMPATIBLE:	/* destroy the arc */
942 			freearc(nfa, a);
943 			break;
944 		case SATISFIED:		/* no action needed */
945 			break;
946 		case COMPATIBLE:	/* swap the two arcs, more or less */
947 			s = newstate(nfa);
948 			if (NISERR())
949 				return 0;
950 			cparc(nfa, con, s, a->to);	/* anticipate move */
951 			cparc(nfa, a, from, s);
952 			if (NISERR())
953 				return 0;
954 			freearc(nfa, a);
955 			break;
956 		default:
957 			assert(NOTREACHED);
958 			break;
959 		}
960 	}
961 
962 	/* remaining outarcs, if any, incorporate the constraint */
963 	moveouts(nfa, to, from);
964 	dropstate(nfa, to);		/* will free the constraint */
965 	return 1;
966 }
967 
968 /*
969  - combine - constraint lands on an arc, what happens?
970  ^ #def	INCOMPATIBLE	1	// destroys arc
971  ^ #def	SATISFIED	2	// constraint satisfied
972  ^ #def	COMPATIBLE	3	// compatible but not satisfied yet
973  ^ static int combine(struct arc *, struct arc *);
974  */
975 static int
combine(con,a)976 combine(con, a)
977 struct arc *con;
978 struct arc *a;
979 {
980 #	define	CA(ct,at)	(((ct)<<CHAR_BIT) | (at))
981 
982 	switch (CA(con->type, a->type)) {
983 	case CA('^', PLAIN):		/* newlines are handled separately */
984 	case CA('$', PLAIN):
985 		return INCOMPATIBLE;
986 		break;
987 	case CA(AHEAD, PLAIN):		/* color constraints meet colors */
988 	case CA(BEHIND, PLAIN):
989 		if (con->co == a->co)
990 			return SATISFIED;
991 		return INCOMPATIBLE;
992 		break;
993 	case CA('^', '^'):		/* collision, similar constraints */
994 	case CA('$', '$'):
995 	case CA(AHEAD, AHEAD):
996 	case CA(BEHIND, BEHIND):
997 		if (con->co == a->co)		/* true duplication */
998 			return SATISFIED;
999 		return INCOMPATIBLE;
1000 		break;
1001 	case CA('^', BEHIND):		/* collision, dissimilar constraints */
1002 	case CA(BEHIND, '^'):
1003 	case CA('$', AHEAD):
1004 	case CA(AHEAD, '$'):
1005 		return INCOMPATIBLE;
1006 		break;
1007 	case CA('^', '$'):		/* constraints passing each other */
1008 	case CA('^', AHEAD):
1009 	case CA(BEHIND, '$'):
1010 	case CA(BEHIND, AHEAD):
1011 	case CA('$', '^'):
1012 	case CA('$', BEHIND):
1013 	case CA(AHEAD, '^'):
1014 	case CA(AHEAD, BEHIND):
1015 	case CA('^', LACON):
1016 	case CA(BEHIND, LACON):
1017 	case CA('$', LACON):
1018 	case CA(AHEAD, LACON):
1019 		return COMPATIBLE;
1020 		break;
1021 	}
1022 	assert(NOTREACHED);
1023 	return INCOMPATIBLE;		/* for benefit of blind compilers */
1024 }
1025 
1026 /*
1027  - fixempties - get rid of EMPTY arcs
1028  ^ static VOID fixempties(struct nfa *, FILE *);
1029  */
1030 static VOID
fixempties(nfa,f)1031 fixempties(nfa, f)
1032 struct nfa *nfa;
1033 FILE *f;			/* for debug output; NULL none */
1034 {
1035 	struct state *s;
1036 	struct state *nexts;
1037 	struct arc *a;
1038 	struct arc *nexta;
1039 	int progress;
1040 
1041 	/* find and eliminate empties until there are no more */
1042 	do {
1043 		progress = 0;
1044 		for (s = nfa->states; s != NULL && !NISERR(); s = nexts) {
1045 			nexts = s->next;
1046 			for (a = s->outs; a != NULL && !NISERR(); a = nexta) {
1047 				nexta = a->outchain;
1048 				if (a->type == EMPTY && unempty(nfa, a))
1049 					progress = 1;
1050 				assert(nexta == NULL || s->no != FREESTATE);
1051 			}
1052 		}
1053 		if (progress && f != NULL)
1054 			dumpnfa(nfa, f);
1055 	} while (progress && !NISERR());
1056 }
1057 
1058 /*
1059  - unempty - optimize out an EMPTY arc, if possible
1060  * Actually, as it stands this function always succeeds, but the return
1061  * value is kept with an eye on possible future changes.
1062  ^ static int unempty(struct nfa *, struct arc *);
1063  */
1064 static int			/* 0 couldn't, 1 could */
unempty(nfa,a)1065 unempty(nfa, a)
1066 struct nfa *nfa;
1067 struct arc *a;
1068 {
1069 	struct state *from = a->from;
1070 	struct state *to = a->to;
1071 	int usefrom;		/* work on from, as opposed to to? */
1072 
1073 	assert(a->type == EMPTY);
1074 	assert(from != nfa->pre && to != nfa->post);
1075 
1076 	if (from == to) {		/* vacuous loop */
1077 		freearc(nfa, a);
1078 		return 1;
1079 	}
1080 
1081 	/* decide which end to work on */
1082 	usefrom = 1;			/* default:  attack from */
1083 	if (from->nouts > to->nins)
1084 		usefrom = 0;
1085 	else if (from->nouts == to->nins) {
1086 		/* decide on secondary issue:  move/copy fewest arcs */
1087 		if (from->nins > to->nouts)
1088 			usefrom = 0;
1089 	}
1090 
1091 	freearc(nfa, a);
1092 	if (usefrom) {
1093 		if (from->nouts == 0) {
1094 			/* was the state's only outarc */
1095 			moveins(nfa, from, to);
1096 			freestate(nfa, from);
1097 		} else
1098 			copyins(nfa, from, to);
1099 	} else {
1100 		if (to->nins == 0) {
1101 			/* was the state's only inarc */
1102 			moveouts(nfa, to, from);
1103 			freestate(nfa, to);
1104 		} else
1105 			copyouts(nfa, to, from);
1106 	}
1107 
1108 	return 1;
1109 }
1110 
1111 /*
1112  - cleanup - clean up NFA after optimizations
1113  ^ static VOID cleanup(struct nfa *);
1114  */
1115 static VOID
cleanup(nfa)1116 cleanup(nfa)
1117 struct nfa *nfa;
1118 {
1119 	struct state *s;
1120 	struct state *nexts;
1121 	int n;
1122 
1123 	/* clear out unreachable or dead-end states */
1124 	/* use pre to mark reachable, then post to mark can-reach-post */
1125 	markreachable(nfa, nfa->pre, (struct state *)NULL, nfa->pre);
1126 	markcanreach(nfa, nfa->post, nfa->pre, nfa->post);
1127 	for (s = nfa->states; s != NULL; s = nexts) {
1128 		nexts = s->next;
1129 		if (s->tmp != nfa->post && !s->flag)
1130 			dropstate(nfa, s);
1131 	}
1132 	assert(nfa->post->nins == 0 || nfa->post->tmp == nfa->post);
1133 	cleartraverse(nfa, nfa->pre);
1134 	assert(nfa->post->nins == 0 || nfa->post->tmp == NULL);
1135 	/* the nins==0 (final unreachable) case will be caught later */
1136 
1137 	/* renumber surviving states */
1138 	n = 0;
1139 	for (s = nfa->states; s != NULL; s = s->next)
1140 		s->no = n++;
1141 	nfa->nstates = n;
1142 }
1143 
1144 /*
1145  - markreachable - recursive marking of reachable states
1146  ^ static VOID markreachable(struct nfa *, struct state *, struct state *,
1147  ^ 	struct state *);
1148  */
1149 static VOID
markreachable(nfa,s,okay,mark)1150 markreachable(nfa, s, okay, mark)
1151 struct nfa *nfa;
1152 struct state *s;
1153 struct state *okay;		/* consider only states with this mark */
1154 struct state *mark;		/* the value to mark with */
1155 {
1156 	struct arc *a;
1157 
1158 	if (s->tmp != okay)
1159 		return;
1160 	s->tmp = mark;
1161 
1162 	for (a = s->outs; a != NULL; a = a->outchain)
1163 		markreachable(nfa, a->to, okay, mark);
1164 }
1165 
1166 /*
1167  - markcanreach - recursive marking of states which can reach here
1168  ^ static VOID markcanreach(struct nfa *, struct state *, struct state *,
1169  ^ 	struct state *);
1170  */
1171 static VOID
markcanreach(nfa,s,okay,mark)1172 markcanreach(nfa, s, okay, mark)
1173 struct nfa *nfa;
1174 struct state *s;
1175 struct state *okay;		/* consider only states with this mark */
1176 struct state *mark;		/* the value to mark with */
1177 {
1178 	struct arc *a;
1179 
1180 	if (s->tmp != okay)
1181 		return;
1182 	s->tmp = mark;
1183 
1184 	for (a = s->ins; a != NULL; a = a->inchain)
1185 		markcanreach(nfa, a->from, okay, mark);
1186 }
1187 
1188 /*
1189  - analyze - ascertain potentially-useful facts about an optimized NFA
1190  ^ static long analyze(struct nfa *);
1191  */
1192 static long			/* re_info bits to be ORed in */
analyze(nfa)1193 analyze(nfa)
1194 struct nfa *nfa;
1195 {
1196 	struct arc *a;
1197 	struct arc *aa;
1198 
1199 	if (nfa->pre->outs == NULL)
1200 		return REG_UIMPOSSIBLE;
1201 	for (a = nfa->pre->outs; a != NULL; a = a->outchain)
1202 		for (aa = a->to->outs; aa != NULL; aa = aa->outchain)
1203 			if (aa->to == nfa->post)
1204 				return REG_UEMPTYMATCH;
1205 	return 0;
1206 }
1207 
1208 /*
1209  - compact - compact an NFA
1210  ^ static VOID compact(struct nfa *, struct cnfa *);
1211  */
1212 static VOID
compact(nfa,cnfa)1213 compact(nfa, cnfa)
1214 struct nfa *nfa;
1215 struct cnfa *cnfa;
1216 {
1217 	struct state *s;
1218 	struct arc *a;
1219 	size_t nstates;
1220 	size_t narcs;
1221 	struct carc *ca;
1222 	struct carc *first;
1223 
1224 	assert (!NISERR());
1225 
1226 	nstates = 0;
1227 	narcs = 0;
1228 	for (s = nfa->states; s != NULL; s = s->next) {
1229 		nstates++;
1230 		narcs += 1 + s->nouts + 1;
1231 		/* 1 as a fake for flags, nouts for arcs, 1 as endmarker */
1232 	}
1233 
1234 	cnfa->states = (struct carc **)MALLOC(nstates * sizeof(struct carc *));
1235 	cnfa->arcs = (struct carc *)MALLOC(narcs * sizeof(struct carc));
1236 	if (cnfa->states == NULL || cnfa->arcs == NULL) {
1237 		if (cnfa->states != NULL)
1238 			FREE(cnfa->states);
1239 		if (cnfa->arcs != NULL)
1240 			FREE(cnfa->arcs);
1241 		NERR(REG_ESPACE);
1242 		return;
1243 	}
1244 	cnfa->nstates = nstates;
1245 	cnfa->pre = nfa->pre->no;
1246 	cnfa->post = nfa->post->no;
1247 	cnfa->bos[0] = nfa->bos[0];
1248 	cnfa->bos[1] = nfa->bos[1];
1249 	cnfa->eos[0] = nfa->eos[0];
1250 	cnfa->eos[1] = nfa->eos[1];
1251 	cnfa->ncolors = maxcolor(nfa->cm) + 1;
1252 	cnfa->flags = 0;
1253 
1254 	ca = cnfa->arcs;
1255 	for (s = nfa->states; s != NULL; s = s->next) {
1256 		assert((size_t)s->no < nstates);
1257 		cnfa->states[s->no] = ca;
1258 		ca->co = 0;		/* clear and skip flags "arc" */
1259 		ca++;
1260 		first = ca;
1261 		for (a = s->outs; a != NULL; a = a->outchain)
1262 			switch (a->type) {
1263 			case PLAIN:
1264 				ca->co = a->co;
1265 				ca->to = a->to->no;
1266 				ca++;
1267 				break;
1268 			case LACON:
1269 				assert(s->no != cnfa->pre);
1270 				ca->co = (color)(cnfa->ncolors + a->co);
1271 				ca->to = a->to->no;
1272 				ca++;
1273 				cnfa->flags |= HASLACONS;
1274 				break;
1275 			default:
1276 				assert(NOTREACHED);
1277 				break;
1278 			}
1279 		carcsort(first, ca-1);
1280 		ca->co = COLORLESS;
1281 		ca->to = 0;
1282 		ca++;
1283 	}
1284 	assert(ca == &cnfa->arcs[narcs]);
1285 	assert(cnfa->nstates != 0);
1286 
1287 	/* mark no-progress states */
1288 	for (a = nfa->pre->outs; a != NULL; a = a->outchain)
1289 		cnfa->states[a->to->no]->co = 1;
1290 	cnfa->states[nfa->pre->no]->co = 1;
1291 }
1292 
1293 /*
1294  - carcsort - sort compacted-NFA arcs by color
1295  * Really dumb algorithm, but if the list is long enough for that to matter,
1296  * you're in real trouble anyway.
1297  ^ static VOID carcsort(struct carc *, struct carc *);
1298  */
1299 static VOID
carcsort(first,last)1300 carcsort(first, last)
1301 struct carc *first;
1302 struct carc *last;
1303 {
1304 	struct carc *p;
1305 	struct carc *q;
1306 	struct carc tmp;
1307 
1308 	if (last - first <= 1)
1309 		return;
1310 
1311 	for (p = first; p <= last; p++)
1312 		for (q = p; q <= last; q++)
1313 			if (p->co > q->co ||
1314 					(p->co == q->co && p->to > q->to)) {
1315 				assert(p != q);
1316 				tmp = *p;
1317 				*p = *q;
1318 				*q = tmp;
1319 			}
1320 }
1321 
1322 /*
1323  - freecnfa - free a compacted NFA
1324  ^ static VOID freecnfa(struct cnfa *);
1325  */
1326 static VOID
freecnfa(cnfa)1327 freecnfa(cnfa)
1328 struct cnfa *cnfa;
1329 {
1330 	assert(cnfa->nstates != 0);	/* not empty already */
1331 	cnfa->nstates = 0;
1332 	FREE(cnfa->states);
1333 	FREE(cnfa->arcs);
1334 }
1335 
1336 /*
1337  - dumpnfa - dump an NFA in human-readable form
1338  ^ static VOID dumpnfa(struct nfa *, FILE *);
1339  */
1340 static VOID
dumpnfa(nfa,f)1341 dumpnfa(nfa, f)
1342 struct nfa *nfa;
1343 FILE *f;
1344 {
1345 #ifdef REG_DEBUG
1346 	struct state *s;
1347 
1348 	fprintf(f, "pre %d, post %d", nfa->pre->no, nfa->post->no);
1349 	if (nfa->bos[0] != COLORLESS)
1350 		fprintf(f, ", bos [%ld]", (long)nfa->bos[0]);
1351 	if (nfa->bos[1] != COLORLESS)
1352 		fprintf(f, ", bol [%ld]", (long)nfa->bos[1]);
1353 	if (nfa->eos[0] != COLORLESS)
1354 		fprintf(f, ", eos [%ld]", (long)nfa->eos[0]);
1355 	if (nfa->eos[1] != COLORLESS)
1356 		fprintf(f, ", eol [%ld]", (long)nfa->eos[1]);
1357 	fprintf(f, "\n");
1358 	for (s = nfa->states; s != NULL; s = s->next)
1359 		dumpstate(s, f);
1360 	if (nfa->parent == NULL)
1361 		dumpcolors(nfa->cm, f);
1362 	fflush(f);
1363 #endif
1364 }
1365 
1366 #ifdef REG_DEBUG		/* subordinates of dumpnfa */
1367 /*
1368  ^ #ifdef REG_DEBUG
1369  */
1370 
1371 /*
1372  - dumpstate - dump an NFA state in human-readable form
1373  ^ static VOID dumpstate(struct state *, FILE *);
1374  */
1375 static VOID
dumpstate(s,f)1376 dumpstate(s, f)
1377 struct state *s;
1378 FILE *f;
1379 {
1380 	struct arc *a;
1381 
1382 	fprintf(f, "%d%s%c", s->no, (s->tmp != NULL) ? "T" : "",
1383 					(s->flag) ? s->flag : '.');
1384 	if (s->prev != NULL && s->prev->next != s)
1385 		fprintf(f, "\tstate chain bad\n");
1386 	if (s->nouts == 0)
1387 		fprintf(f, "\tno out arcs\n");
1388 	else
1389 		dumparcs(s, f);
1390 	fflush(f);
1391 	for (a = s->ins; a != NULL; a = a->inchain) {
1392 		if (a->to != s)
1393 			fprintf(f, "\tlink from %d to %d on %d's in-chain\n",
1394 					a->from->no, a->to->no, s->no);
1395 	}
1396 }
1397 
1398 /*
1399  - dumparcs - dump out-arcs in human-readable form
1400  ^ static VOID dumparcs(struct state *, FILE *);
1401  */
1402 static VOID
dumparcs(s,f)1403 dumparcs(s, f)
1404 struct state *s;
1405 FILE *f;
1406 {
1407 	int pos;
1408 
1409 	assert(s->nouts > 0);
1410 	/* printing arcs in reverse order is usually clearer */
1411 	pos = dumprarcs(s->outs, s, f, 1);
1412 	if (pos != 1)
1413 		fprintf(f, "\n");
1414 }
1415 
1416 /*
1417  - dumprarcs - dump remaining outarcs, recursively, in reverse order
1418  ^ static int dumprarcs(struct arc *, struct state *, FILE *, int);
1419  */
1420 static int			/* resulting print position */
dumprarcs(a,s,f,pos)1421 dumprarcs(a, s, f, pos)
1422 struct arc *a;
1423 struct state *s;
1424 FILE *f;
1425 int pos;			/* initial print position */
1426 {
1427 	if (a->outchain != NULL)
1428 		pos = dumprarcs(a->outchain, s, f, pos);
1429 	dumparc(a, s, f);
1430 	if (pos == 5) {
1431 		fprintf(f, "\n");
1432 		pos = 1;
1433 	} else
1434 		pos++;
1435 	return pos;
1436 }
1437 
1438 /*
1439  - dumparc - dump one outarc in readable form, including prefixing tab
1440  ^ static VOID dumparc(struct arc *, struct state *, FILE *);
1441  */
1442 static VOID
dumparc(a,s,f)1443 dumparc(a, s, f)
1444 struct arc *a;
1445 struct state *s;
1446 FILE *f;
1447 {
1448 	struct arc *aa;
1449 	struct arcbatch *ab;
1450 
1451 	fprintf(f, "\t");
1452 	switch (a->type) {
1453 	case PLAIN:
1454 		fprintf(f, "[%ld]", (long)a->co);
1455 		break;
1456 	case AHEAD:
1457 		fprintf(f, ">%ld>", (long)a->co);
1458 		break;
1459 	case BEHIND:
1460 		fprintf(f, "<%ld<", (long)a->co);
1461 		break;
1462 	case LACON:
1463 		fprintf(f, ":%ld:", (long)a->co);
1464 		break;
1465 	case '^':
1466 	case '$':
1467 		fprintf(f, "%c%d", a->type, (int)a->co);
1468 		break;
1469 	case EMPTY:
1470 		break;
1471 	default:
1472 		fprintf(f, "0x%x/0%lo", a->type, (long)a->co);
1473 		break;
1474 	}
1475 	if (a->from != s)
1476 		fprintf(f, "?%d?", a->from->no);
1477 	for (ab = &a->from->oas; ab != NULL; ab = ab->next) {
1478 		for (aa = &ab->a[0]; aa < &ab->a[ABSIZE]; aa++)
1479 			if (aa == a)
1480 				break;		/* NOTE BREAK OUT */
1481 		if (aa < &ab->a[ABSIZE])	/* propagate break */
1482 				break;		/* NOTE BREAK OUT */
1483 	}
1484 	if (ab == NULL)
1485 		fprintf(f, "?!?");	/* not in allocated space */
1486 	fprintf(f, "->");
1487 	if (a->to == NULL) {
1488 		fprintf(f, "NULL");
1489 		return;
1490 	}
1491 	fprintf(f, "%d", a->to->no);
1492 	for (aa = a->to->ins; aa != NULL; aa = aa->inchain)
1493 		if (aa == a)
1494 			break;		/* NOTE BREAK OUT */
1495 	if (aa == NULL)
1496 		fprintf(f, "?!?");	/* missing from in-chain */
1497 }
1498 
1499 /*
1500  ^ #endif
1501  */
1502 #endif				/* ifdef REG_DEBUG */
1503 
1504 #ifdef REG_DEBUG
1505 /*
1506  - dumpcnfa - dump a compacted NFA in human-readable form
1507  ^ static VOID dumpcnfa(struct cnfa *, FILE *);
1508  */
1509 static VOID
dumpcnfa(cnfa,f)1510 dumpcnfa(cnfa, f)
1511 struct cnfa *cnfa;
1512 FILE *f;
1513 {
1514 	int st;
1515 
1516 	fprintf(f, "pre %d, post %d", cnfa->pre, cnfa->post);
1517 	if (cnfa->bos[0] != COLORLESS)
1518 		fprintf(f, ", bos [%ld]", (long)cnfa->bos[0]);
1519 	if (cnfa->bos[1] != COLORLESS)
1520 		fprintf(f, ", bol [%ld]", (long)cnfa->bos[1]);
1521 	if (cnfa->eos[0] != COLORLESS)
1522 		fprintf(f, ", eos [%ld]", (long)cnfa->eos[0]);
1523 	if (cnfa->eos[1] != COLORLESS)
1524 		fprintf(f, ", eol [%ld]", (long)cnfa->eos[1]);
1525 	if (cnfa->flags&HASLACONS)
1526 		fprintf(f, ", haslacons");
1527 	fprintf(f, "\n");
1528 	for (st = 0; st < cnfa->nstates; st++)
1529 		dumpcstate(st, cnfa->states[st], cnfa, f);
1530 	fflush(f);
1531 }
1532 
1533 /*
1534  ^ #ifdef REG_DEBUG
1535  */
1536 
1537 /*
1538  - dumpcstate - dump a compacted-NFA state in human-readable form
1539  ^ static VOID dumpcstate(int, struct carc *, struct cnfa *, FILE *);
1540  */
1541 static VOID
dumpcstate(st,ca,cnfa,f)1542 dumpcstate(st, ca, cnfa, f)
1543 int st;
1544 struct carc *ca;
1545 struct cnfa *cnfa;
1546 FILE *f;
1547 {
1548 	int i;
1549 	int pos;
1550 
1551 	fprintf(f, "%d%s", st, (ca[0].co) ? ":" : ".");
1552 	pos = 1;
1553 	for (i = 1; ca[i].co != COLORLESS; i++) {
1554 		if (ca[i].co < cnfa->ncolors)
1555 			fprintf(f, "\t[%ld]->%d", (long)ca[i].co, ca[i].to);
1556 		else
1557 			fprintf(f, "\t:%ld:->%d", (long)ca[i].co-cnfa->ncolors,
1558 								ca[i].to);
1559 		if (pos == 5) {
1560 			fprintf(f, "\n");
1561 			pos = 1;
1562 		} else
1563 			pos++;
1564 	}
1565 	if (i == 1 || pos != 1)
1566 		fprintf(f, "\n");
1567 	fflush(f);
1568 }
1569 
1570 /*
1571  ^ #endif
1572  */
1573 #endif				/* ifdef REG_DEBUG */
1574