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