1/*
2 * colorings of characters
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 of
18 * 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 * Note that there are some incestuous relationships between this code and NFA
32 * arc maintenance, which perhaps ought to be cleaned up sometime.
33 */
34
35#define	CISERR()	VISERR(cm->v)
36#define	CERR(e)		VERR(cm->v, (e))
37
38/*
39 - initcm - set up new colormap
40 ^ static VOID initcm(struct vars *, struct colormap *);
41 */
42static void
43initcm(
44    struct vars *v,
45    struct colormap *cm)
46{
47    int i;
48    int j;
49    union tree *t;
50    union tree *nextt;
51    struct colordesc *cd;
52
53    cm->magic = CMMAGIC;
54    cm->v = v;
55
56    cm->ncds = NINLINECDS;
57    cm->cd = cm->cdspace;
58    cm->max = 0;
59    cm->free = 0;
60
61    cd = cm->cd;		/* cm->cd[WHITE] */
62    cd->sub = NOSUB;
63    cd->arcs = NULL;
64    cd->flags = 0;
65    cd->nchrs = CHR_MAX - CHR_MIN + 1;
66
67    /*
68     * Upper levels of tree.
69     */
70
71    for (t=&cm->tree[0], j=NBYTS-1 ; j>0 ; t=nextt, j--) {
72	nextt = t + 1;
73	for (i=BYTTAB-1 ; i>=0 ; i--) {
74	    t->tptr[i] = nextt;
75	}
76    }
77
78    /*
79     * Bottom level is solid white.
80     */
81
82    t = &cm->tree[NBYTS-1];
83    for (i=BYTTAB-1 ; i>=0 ; i--) {
84	t->tcolor[i] = WHITE;
85    }
86    cd->block = t;
87}
88
89/*
90 - freecm - free dynamically-allocated things in a colormap
91 ^ static VOID freecm(struct colormap *);
92 */
93static void
94freecm(
95    struct colormap *cm)
96{
97    size_t i;
98    union tree *cb;
99
100    cm->magic = 0;
101    if (NBYTS > 1) {
102	cmtreefree(cm, cm->tree, 0);
103    }
104    for (i=1 ; i<=cm->max ; i++) {	/* skip WHITE */
105	if (!UNUSEDCOLOR(&cm->cd[i])) {
106	    cb = cm->cd[i].block;
107	    if (cb != NULL) {
108		FREE(cb);
109	    }
110	}
111    }
112    if (cm->cd != cm->cdspace) {
113	FREE(cm->cd);
114    }
115}
116
117/*
118 - cmtreefree - free a non-terminal part of a colormap tree
119 ^ static VOID cmtreefree(struct colormap *, union tree *, int);
120 */
121static void
122cmtreefree(
123    struct colormap *cm,
124    union tree *tree,
125    int level)			/* level number (top == 0) of this block */
126{
127    int i;
128    union tree *t;
129    union tree *fillt = &cm->tree[level+1];
130    union tree *cb;
131
132    assert(level < NBYTS-1);	/* this level has pointers */
133    for (i=BYTTAB-1 ; i>=0 ; i--) {
134	t = tree->tptr[i];
135	assert(t != NULL);
136	if (t != fillt) {
137	    if (level < NBYTS-2) {	/* more pointer blocks below */
138		cmtreefree(cm, t, level+1);
139		FREE(t);
140	    } else {		/* color block below */
141		cb = cm->cd[t->tcolor[0]].block;
142		if (t != cb) {	/* not a solid block */
143		    FREE(t);
144		}
145	    }
146	}
147    }
148}
149
150/*
151 - setcolor - set the color of a character in a colormap
152 ^ static color setcolor(struct colormap *, pchr, pcolor);
153 */
154static color			/* previous color */
155setcolor(
156    struct colormap *cm,
157    pchr c,
158    pcolor co)
159{
160    uchr uc = c;
161    int shift;
162    int level;
163    int b;
164    int bottom;
165    union tree *t;
166    union tree *newt;
167    union tree *fillt;
168    union tree *lastt;
169    union tree *cb;
170    color prev;
171
172    assert(cm->magic == CMMAGIC);
173    if (CISERR() || co == COLORLESS) {
174	return COLORLESS;
175    }
176
177    t = cm->tree;
178    for (level=0, shift=BYTBITS*(NBYTS-1) ; shift>0; level++, shift-=BYTBITS){
179	b = (uc >> shift) & BYTMASK;
180	lastt = t;
181	t = lastt->tptr[b];
182	assert(t != NULL);
183	fillt = &cm->tree[level+1];
184	bottom = (shift <= BYTBITS) ? 1 : 0;
185	cb = (bottom) ? cm->cd[t->tcolor[0]].block : fillt;
186	if (t == fillt || t == cb) {	/* must allocate a new block */
187	    newt = (union tree *) MALLOC((bottom) ?
188		    sizeof(struct colors) : sizeof(struct ptrs));
189	    if (newt == NULL) {
190		CERR(REG_ESPACE);
191		return COLORLESS;
192	    }
193	    if (bottom) {
194		memcpy(newt->tcolor, t->tcolor, BYTTAB*sizeof(color));
195	    } else {
196		memcpy(newt->tptr, t->tptr, BYTTAB*sizeof(union tree *));
197	    }
198	    t = newt;
199	    lastt->tptr[b] = t;
200	}
201    }
202
203    b = uc & BYTMASK;
204    prev = t->tcolor[b];
205    t->tcolor[b] = (color) co;
206    return prev;
207}
208
209/*
210 - maxcolor - report largest color number in use
211 ^ static color maxcolor(struct colormap *);
212 */
213static color
214maxcolor(
215    struct colormap *cm)
216{
217    if (CISERR()) {
218	return COLORLESS;
219    }
220
221    return (color) cm->max;
222}
223
224/*
225 - newcolor - find a new color (must be subject of setcolor at once)
226 * Beware: may relocate the colordescs.
227 ^ static color newcolor(struct colormap *);
228 */
229static color			/* COLORLESS for error */
230newcolor(
231    struct colormap *cm)
232{
233    struct colordesc *cd;
234    size_t n;
235
236    if (CISERR()) {
237	return COLORLESS;
238    }
239
240    if (cm->free != 0) {
241	assert(cm->free > 0);
242	assert((size_t) cm->free < cm->ncds);
243	cd = &cm->cd[cm->free];
244	assert(UNUSEDCOLOR(cd));
245	assert(cd->arcs == NULL);
246	cm->free = cd->sub;
247    } else if (cm->max < cm->ncds - 1) {
248	cm->max++;
249	cd = &cm->cd[cm->max];
250    } else {
251	struct colordesc *newCd;
252
253	/*
254	 * Oops, must allocate more.
255	 */
256
257	n = cm->ncds * 2;
258	if (cm->cd == cm->cdspace) {
259	    newCd = (struct colordesc *) MALLOC(n * sizeof(struct colordesc));
260	    if (newCd != NULL) {
261		memcpy(newCd, cm->cdspace,
262			cm->ncds * sizeof(struct colordesc));
263	    }
264	} else {
265	    newCd = (struct colordesc *)
266		    REALLOC(cm->cd, n * sizeof(struct colordesc));
267	}
268	if (newCd == NULL) {
269	    CERR(REG_ESPACE);
270	    return COLORLESS;
271	}
272	cm->cd = newCd;
273	cm->ncds = n;
274	assert(cm->max < cm->ncds - 1);
275	cm->max++;
276	cd = &cm->cd[cm->max];
277    }
278
279    cd->nchrs = 0;
280    cd->sub = NOSUB;
281    cd->arcs = NULL;
282    cd->flags = 0;
283    cd->block = NULL;
284
285    return (color) (cd - cm->cd);
286}
287
288/*
289 - freecolor - free a color (must have no arcs or subcolor)
290 ^ static VOID freecolor(struct colormap *, pcolor);
291 */
292static void
293freecolor(
294    struct colormap *cm,
295    pcolor co)
296{
297    struct colordesc *cd = &cm->cd[co];
298    color pco, nco;		/* for freelist scan */
299
300    assert(co >= 0);
301    if (co == WHITE) {
302	return;
303    }
304
305    assert(cd->arcs == NULL);
306    assert(cd->sub == NOSUB);
307    assert(cd->nchrs == 0);
308    cd->flags = FREECOL;
309    if (cd->block != NULL) {
310	FREE(cd->block);
311	cd->block = NULL;	/* just paranoia */
312    }
313
314    if ((size_t) co == cm->max) {
315	while (cm->max > WHITE && UNUSEDCOLOR(&cm->cd[cm->max])) {
316	    cm->max--;
317	}
318	assert(cm->free >= 0);
319	while ((size_t) cm->free > cm->max) {
320	    cm->free = cm->cd[cm->free].sub;
321	}
322	if (cm->free > 0) {
323	    assert(cm->free < cm->max);
324	    pco = cm->free;
325	    nco = cm->cd[pco].sub;
326	    while (nco > 0) {
327		if ((size_t) nco > cm->max) {
328		    /*
329		     * Take this one out of freelist.
330		     */
331
332		    nco = cm->cd[nco].sub;
333		    cm->cd[pco].sub = nco;
334		} else {
335		    assert(nco < cm->max);
336		    pco = nco;
337		    nco = cm->cd[pco].sub;
338		}
339	    }
340	}
341    } else {
342	cd->sub = cm->free;
343	cm->free = (color) (cd - cm->cd);
344    }
345}
346
347/*
348 - pseudocolor - allocate a false color, to be managed by other means
349 ^ static color pseudocolor(struct colormap *);
350 */
351static color
352pseudocolor(
353    struct colormap *cm)
354{
355    color co;
356
357    co = newcolor(cm);
358    if (CISERR()) {
359	return COLORLESS;
360    }
361    cm->cd[co].nchrs = 1;
362    cm->cd[co].flags = PSEUDO;
363    return co;
364}
365
366/*
367 - subcolor - allocate a new subcolor (if necessary) to this chr
368 ^ static color subcolor(struct colormap *, pchr c);
369 */
370static color
371subcolor(
372    struct colormap *cm,
373    pchr c)
374{
375    color co;			/* current color of c */
376    color sco;			/* new subcolor */
377
378    co = GETCOLOR(cm, c);
379    sco = newsub(cm, co);
380    if (CISERR()) {
381	return COLORLESS;
382    }
383    assert(sco != COLORLESS);
384
385    if (co == sco) {		/* already in an open subcolor */
386	return co;		/* rest is redundant */
387    }
388    cm->cd[co].nchrs--;
389    cm->cd[sco].nchrs++;
390    setcolor(cm, c, sco);
391    return sco;
392}
393
394/*
395 - newsub - allocate a new subcolor (if necessary) for a color
396 ^ static color newsub(struct colormap *, pcolor);
397 */
398static color
399newsub(
400    struct colormap *cm,
401    pcolor co)
402{
403    color sco;			/* new subcolor */
404
405    sco = cm->cd[co].sub;
406    if (sco == NOSUB) {		/* color has no open subcolor */
407	if (cm->cd[co].nchrs == 1) {	/* optimization */
408	    return co;
409	}
410	sco = newcolor(cm);	/* must create subcolor */
411	if (sco == COLORLESS) {
412	    assert(CISERR());
413	    return COLORLESS;
414	}
415	cm->cd[co].sub = sco;
416	cm->cd[sco].sub = sco;	/* open subcolor points to self */
417    }
418    assert(sco != NOSUB);
419
420    return sco;
421}
422
423/*
424 - subrange - allocate new subcolors to this range of chrs, fill in arcs
425 ^ static VOID subrange(struct vars *, pchr, pchr, struct state *,
426 ^ 	struct state *);
427 */
428static void
429subrange(
430    struct vars *v,
431    pchr from,
432    pchr to,
433    struct state *lp,
434    struct state *rp)
435{
436    uchr uf;
437    int i;
438
439    assert(from <= to);
440
441    /*
442     * First, align "from" on a tree-block boundary
443     */
444
445    uf = (uchr) from;
446    i = (int) (((uf + BYTTAB - 1) & (uchr) ~BYTMASK) - uf);
447    for (; from<=to && i>0; i--, from++) {
448	newarc(v->nfa, PLAIN, subcolor(v->cm, from), lp, rp);
449    }
450    if (from > to) {		/* didn't reach a boundary */
451	return;
452    }
453
454    /*
455     * Deal with whole blocks.
456     */
457
458    for (; to-from>=BYTTAB ; from+=BYTTAB) {
459	subblock(v, from, lp, rp);
460    }
461
462    /*
463     * Clean up any remaining partial table.
464     */
465
466    for (; from<=to ; from++) {
467	newarc(v->nfa, PLAIN, subcolor(v->cm, from), lp, rp);
468    }
469}
470
471/*
472 - subblock - allocate new subcolors for one tree block of chrs, fill in arcs
473 ^ static VOID subblock(struct vars *, pchr, struct state *, struct state *);
474 */
475static void
476subblock(
477    struct vars *v,
478    pchr start,			/* first of BYTTAB chrs */
479    struct state *lp,
480    struct state *rp)
481{
482    uchr uc = start;
483    struct colormap *cm = v->cm;
484    int shift;
485    int level;
486    int i;
487    int b;
488    union tree *t;
489    union tree *cb;
490    union tree *fillt;
491    union tree *lastt;
492    int previ;
493    int ndone;
494    color co;
495    color sco;
496
497    assert((uc % BYTTAB) == 0);
498
499    /*
500     * Find its color block, making new pointer blocks as needed.
501     */
502
503    t = cm->tree;
504    fillt = NULL;
505    for (level=0, shift=BYTBITS*(NBYTS-1); shift>0; level++, shift-=BYTBITS) {
506	b = (uc >> shift) & BYTMASK;
507	lastt = t;
508	t = lastt->tptr[b];
509	assert(t != NULL);
510	fillt = &cm->tree[level+1];
511	if (t == fillt && shift > BYTBITS) {	/* need new ptr block */
512	    t = (union tree *) MALLOC(sizeof(struct ptrs));
513	    if (t == NULL) {
514		CERR(REG_ESPACE);
515		return;
516	    }
517	    memcpy(t->tptr, fillt->tptr, BYTTAB*sizeof(union tree *));
518	    lastt->tptr[b] = t;
519	}
520    }
521
522    /*
523     * Special cases: fill block or solid block.
524     */
525    co = t->tcolor[0];
526    cb = cm->cd[co].block;
527    if (t == fillt || t == cb) {
528	/*
529	 * Either way, we want a subcolor solid block.
530	 */
531
532	sco = newsub(cm, co);
533	t = cm->cd[sco].block;
534	if (t == NULL) {	/* must set it up */
535	    t = (union tree *) MALLOC(sizeof(struct colors));
536	    if (t == NULL) {
537		CERR(REG_ESPACE);
538		return;
539	    }
540	    for (i=0 ; i<BYTTAB ; i++) {
541		t->tcolor[i] = sco;
542	    }
543	    cm->cd[sco].block = t;
544	}
545
546	/*
547	 * Find loop must have run at least once.
548	 */
549
550	lastt->tptr[b] = t;
551	newarc(v->nfa, PLAIN, sco, lp, rp);
552	cm->cd[co].nchrs -= BYTTAB;
553	cm->cd[sco].nchrs += BYTTAB;
554	return;
555    }
556
557    /*
558     * General case, a mixed block to be altered.
559     */
560
561    i = 0;
562    while (i < BYTTAB) {
563	co = t->tcolor[i];
564	sco = newsub(cm, co);
565	newarc(v->nfa, PLAIN, sco, lp, rp);
566	previ = i;
567	do {
568	    t->tcolor[i++] = sco;
569	} while (i < BYTTAB && t->tcolor[i] == co);
570	ndone = i - previ;
571	cm->cd[co].nchrs -= ndone;
572	cm->cd[sco].nchrs += ndone;
573    }
574}
575
576/*
577 - okcolors - promote subcolors to full colors
578 ^ static VOID okcolors(struct nfa *, struct colormap *);
579 */
580static void
581okcolors(
582    struct nfa *nfa,
583    struct colormap *cm)
584{
585    struct colordesc *cd;
586    struct colordesc *end = CDEND(cm);
587    struct colordesc *scd;
588    struct arc *a;
589    color co;
590    color sco;
591
592    for (cd=cm->cd, co=0 ; cd<end ; cd++, co++) {
593	sco = cd->sub;
594	if (UNUSEDCOLOR(cd) || sco == NOSUB) {
595	    /*
596	     * Has no subcolor, no further action.
597	     */
598	} else if (sco == co) {
599	    /*
600	     * Is subcolor, let parent deal with it.
601	     */
602	} else if (cd->nchrs == 0) {
603	    /*
604	     * Parent empty, its arcs change color to subcolor.
605	     */
606
607	    cd->sub = NOSUB;
608	    scd = &cm->cd[sco];
609	    assert(scd->nchrs > 0);
610	    assert(scd->sub == sco);
611	    scd->sub = NOSUB;
612	    while ((a = cd->arcs) != NULL) {
613		assert(a->co == co);
614		uncolorchain(cm, a);
615		a->co = sco;
616		colorchain(cm, a);
617	    }
618	    freecolor(cm, co);
619	} else {
620	    /*
621	     * Parent's arcs must gain parallel subcolor arcs.
622	     */
623
624	    cd->sub = NOSUB;
625	    scd = &cm->cd[sco];
626	    assert(scd->nchrs > 0);
627	    assert(scd->sub == sco);
628	    scd->sub = NOSUB;
629	    for (a=cd->arcs ; a!=NULL ; a=a->colorchain) {
630		assert(a->co == co);
631		newarc(nfa, a->type, sco, a->from, a->to);
632	    }
633	}
634    }
635}
636
637/*
638 - colorchain - add this arc to the color chain of its color
639 ^ static VOID colorchain(struct colormap *, struct arc *);
640 */
641static void
642colorchain(
643    struct colormap *cm,
644    struct arc *a)
645{
646    struct colordesc *cd = &cm->cd[a->co];
647
648    if (cd->arcs != NULL) {
649	cd->arcs->colorchainRev = a;
650    }
651    a->colorchain = cd->arcs;
652    a->colorchainRev = NULL;
653    cd->arcs = a;
654}
655
656/*
657 - uncolorchain - delete this arc from the color chain of its color
658 ^ static VOID uncolorchain(struct colormap *, struct arc *);
659 */
660static void
661uncolorchain(
662    struct colormap *cm,
663    struct arc *a)
664{
665    struct colordesc *cd = &cm->cd[a->co];
666    struct arc *aa = a->colorchainRev;
667
668    if (aa == NULL) {
669	assert(cd->arcs == a);
670	cd->arcs = a->colorchain;
671    } else {
672	assert(aa->colorchain == a);
673	aa->colorchain = a->colorchain;
674    }
675    if (a->colorchain != NULL) {
676	a->colorchain->colorchainRev = aa;
677    }
678    a->colorchain = NULL;	/* paranoia */
679    a->colorchainRev = NULL;
680}
681
682/*
683 - rainbow - add arcs of all full colors (but one) between specified states
684 ^ static VOID rainbow(struct nfa *, struct colormap *, int, pcolor,
685 ^ 	struct state *, struct state *);
686 */
687static void
688rainbow(
689    struct nfa *nfa,
690    struct colormap *cm,
691    int type,
692    pcolor but,			/* COLORLESS if no exceptions */
693    struct state *from,
694    struct state *to)
695{
696    struct colordesc *cd;
697    struct colordesc *end = CDEND(cm);
698    color co;
699
700    for (cd=cm->cd, co=0 ; cd<end && !CISERR(); cd++, co++) {
701	if (!UNUSEDCOLOR(cd) && (cd->sub != co) && (co != but)
702		&& !(cd->flags&PSEUDO)) {
703	    newarc(nfa, type, co, from, to);
704	}
705    }
706}
707
708/*
709 - colorcomplement - add arcs of complementary colors
710 * The calling sequence ought to be reconciled with cloneouts().
711 ^ static VOID colorcomplement(struct nfa *, struct colormap *, int,
712 ^ 	struct state *, struct state *, struct state *);
713 */
714static void
715colorcomplement(
716    struct nfa *nfa,
717    struct colormap *cm,
718    int type,
719    struct state *of,		/* complements of this guy's PLAIN outarcs */
720    struct state *from,
721    struct state *to)
722{
723    struct colordesc *cd;
724    struct colordesc *end = CDEND(cm);
725    color co;
726
727    assert(of != from);
728    for (cd=cm->cd, co=0 ; cd<end && !CISERR() ; cd++, co++) {
729	if (!UNUSEDCOLOR(cd) && !(cd->flags&PSEUDO)) {
730	    if (findarc(of, PLAIN, co) == NULL) {
731		newarc(nfa, type, co, from, to);
732	    }
733	}
734    }
735}
736
737#ifdef REG_DEBUG
738/*
739 ^ #ifdef REG_DEBUG
740 */
741
742/*
743 - dumpcolors - debugging output
744 ^ static VOID dumpcolors(struct colormap *, FILE *);
745 */
746static void
747dumpcolors(
748    struct colormap *cm,
749    FILE *f)
750{
751    struct colordesc *cd;
752    struct colordesc *end;
753    color co;
754    chr c;
755    char *has;
756
757    fprintf(f, "max %ld\n", (long) cm->max);
758    if (NBYTS > 1) {
759	fillcheck(cm, cm->tree, 0, f);
760    }
761    end = CDEND(cm);
762    for (cd=cm->cd+1, co=1 ; cd<end ; cd++, co++) {	/* skip 0 */
763	if (!UNUSEDCOLOR(cd)) {
764	    assert(cd->nchrs > 0);
765	    has = (cd->block != NULL) ? "#" : "";
766	    if (cd->flags&PSEUDO) {
767		fprintf(f, "#%2ld%s(ps): ", (long) co, has);
768	    } else {
769		fprintf(f, "#%2ld%s(%2d): ", (long) co, has, cd->nchrs);
770	    }
771
772	    /*
773	     * It's hard to do this more efficiently.
774	     */
775
776	    for (c=CHR_MIN ; c<CHR_MAX ; c++) {
777		if (GETCOLOR(cm, c) == co) {
778		    dumpchr(c, f);
779		}
780	    }
781	    assert(c == CHR_MAX);
782	    if (GETCOLOR(cm, c) == co) {
783		dumpchr(c, f);
784	    }
785	    fprintf(f, "\n");
786	}
787    }
788}
789
790/*
791 - fillcheck - check proper filling of a tree
792 ^ static VOID fillcheck(struct colormap *, union tree *, int, FILE *);
793 */
794static void
795fillcheck(
796    struct colormap *cm,
797    union tree *tree,
798    int level,			/* level number (top == 0) of this block */
799    FILE *f)
800{
801    int i;
802    union tree *t;
803    union tree *fillt = &cm->tree[level+1];
804
805    assert(level < NBYTS-1);	/* this level has pointers */
806    for (i=BYTTAB-1 ; i>=0 ; i--) {
807	t = tree->tptr[i];
808	if (t == NULL) {
809	    fprintf(f, "NULL found in filled tree!\n");
810	} else if (t == fillt) {
811	    /* empty body */
812	} else if (level < NBYTS-2) {	/* more pointer blocks below */
813	    fillcheck(cm, t, level+1, f);
814	}
815    }
816}
817
818/*
819 - dumpchr - print a chr
820 * Kind of char-centric but works well enough for debug use.
821 ^ static VOID dumpchr(pchr, FILE *);
822 */
823static void
824dumpchr(
825    pchr c,
826    FILE *f)
827{
828    if (c == '\\') {
829	fprintf(f, "\\\\");
830    } else if (c > ' ' && c <= '~') {
831	putc((char) c, f);
832    } else {
833	fprintf(f, "\\u%04lx", (long) c);
834    }
835}
836
837/*
838 ^ #endif
839 */
840#endif				/* ifdef REG_DEBUG */
841
842/*
843 * Local Variables:
844 * mode: c
845 * c-basic-offset: 4
846 * fill-column: 78
847 * End:
848 */
849