Home | History | Annotate | Line # | Download | only in mklocale
      1 /*	$NetBSD: yacc.y,v 1.36 2024/01/05 02:38:06 rin Exp $	*/
      2 
      3 %{
      4 /*-
      5  * Copyright (c) 1993
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * This code is derived from software contributed to Berkeley by
      9  * Paul Borman at Krystal Technologies.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #if HAVE_NBTOOL_CONFIG_H
     37 #include "nbtool_config.h"
     38 #endif
     39 
     40 #include <sys/cdefs.h>
     41 #ifndef lint
     42 #if 0
     43 static char sccsid[] = "@(#)yacc.y	8.1 (Berkeley) 6/6/93";
     44 static char rcsid[] = "$FreeBSD$";
     45 #else
     46 __RCSID("$NetBSD: yacc.y,v 1.36 2024/01/05 02:38:06 rin Exp $");
     47 #endif
     48 #endif /* not lint */
     49 
     50 #include <sys/types.h>
     51 #include <netinet/in.h>	/* Needed by <arpa/inet.h> on NetBSD 1.5. */
     52 #include <arpa/inet.h>	/* Needed for htonl on POSIX systems. */
     53 
     54 #include <err.h>
     55 #include <locale.h>
     56 #include <stddef.h>
     57 #include <stdio.h>
     58 #include <stdlib.h>
     59 #include <string.h>
     60 #include <unistd.h>
     61 #include <ctype.h>
     62 
     63 #include "runetype_file.h"
     64 
     65 #include "ldef.h"
     66 
     67 const char	*locale_file = "<stdout>";
     68 
     69 rune_map	maplower = { { 0, }, };
     70 rune_map	mapupper = { { 0, }, };
     71 rune_map	types = { { 0, }, };
     72 
     73 _FileRuneLocale new_locale = { { 0, }, };
     74 
     75 size_t rl_variable_len = (size_t)0;
     76 void *rl_variable = NULL;
     77 
     78 __nbrune_t	charsetbits = (__nbrune_t)0x00000000;
     79 #if 0
     80 __nbrune_t	charsetmask = (__nbrune_t)0x0000007f;
     81 #endif
     82 __nbrune_t	charsetmask = (__nbrune_t)0xffffffff;
     83 
     84 void set_map(rune_map *, rune_list *, u_int32_t);
     85 void set_digitmap(rune_map *, rune_list *);
     86 void add_map(rune_map *, rune_list *, u_int32_t);
     87 
     88 __dead void	usage(void);
     89 int		yyerror(const char *s);
     90 void		*xmalloc(unsigned int sz);
     91 u_int32_t	*xlalloc(unsigned int sz);
     92 u_int32_t	*xrelalloc(u_int32_t *old, unsigned int sz);
     93 void		dump_tables(void);
     94 int		yyparse(void);
     95 extern int	yylex(void);
     96 
     97 /* mklocaledb.c */
     98 extern void mklocaledb(const char *, FILE *, FILE *);
     99 
    100 %}
    101 
    102 %union	{
    103     __nbrune_t	rune;
    104     int		i;
    105     char	*str;
    106 
    107     rune_list	*list;
    108 }
    109 
    110 %token	<rune>	RUNE
    111 %token		LBRK
    112 %token		RBRK
    113 %token		THRU
    114 %token		MAPLOWER
    115 %token		MAPUPPER
    116 %token		DIGITMAP
    117 %token	<i>	LIST
    118 %token	<str>	VARIABLE
    119 %token		CHARSET
    120 %token		ENCODING
    121 %token		INVALID
    122 %token	<str>	STRING
    123 
    124 %type	<list>	list
    125 %type	<list>	map
    126 
    127 
    128 %%
    129 
    130 locale	:	/* empty */
    131 	|	table
    132 	    	{ dump_tables(); }
    133 	;
    134 
    135 table	:	entry
    136 	|	table entry
    137 	;
    138 
    139 entry	:	ENCODING STRING
    140 		{ strlcpy(new_locale.frl_encoding, $2, sizeof(new_locale.frl_encoding)); }
    141 	|	VARIABLE
    142 		{ rl_variable_len = strlen($1) + 1;
    143 		  rl_variable = strdup($1);
    144 		  new_locale.frl_variable_len = htonl((u_int32_t)rl_variable_len);
    145 		}
    146 	|	CHARSET RUNE
    147 		{ charsetbits = $2; charsetmask = 0x0000007f; }
    148 	|	CHARSET RUNE RUNE
    149 		{ charsetbits = $2; charsetmask = $3; }
    150 	|	CHARSET STRING
    151 		{ int final = $2[strlen($2) - 1] & 0x7f;
    152 		  charsetbits = final << 24;
    153 		  if ($2[0] == '$') {
    154 			charsetmask = 0x00007f7f;
    155 			if (strchr(",-./", $2[1]))
    156 				charsetbits |= 0x80;
    157 			if (0xd0 <= final && final <= 0xdf)
    158 				charsetmask |= 0x007f0000;
    159 		  } else {
    160 			charsetmask = 0x0000007f;
    161 			if (strchr(",-./", $2[0]))
    162 				charsetbits |= 0x80;
    163 			if (strlen($2) == 2 && $2[0] == '!')
    164 				charsetbits |= ((0x80 | $2[0]) << 16);
    165 		  }
    166 
    167 		  /*
    168 		   * special rules
    169 		   */
    170 		  if (charsetbits == ('B' << 24)
    171 		   && charsetmask == 0x0000007f) {
    172 			/*ASCII: 94B*/
    173 			charsetbits = 0;
    174 			charsetmask = 0x0000007f;
    175 		  } else if (charsetbits == (('A' << 24) | 0x80)
    176 		  	  && charsetmask == 0x0000007f) {
    177 		  	/*Latin1: 96A*/
    178 			charsetbits = 0x80;
    179 			charsetmask = 0x0000007f;
    180 		  }
    181 		}
    182 	|	INVALID RUNE
    183 		{ new_locale.frl_invalid_rune = htonl((u_int32_t)$2); }
    184 	|	LIST list
    185 		{ set_map(&types, $2, $1); }
    186 	|	MAPLOWER map
    187 		{ set_map(&maplower, $2, 0); }
    188 	|	MAPUPPER map
    189 		{ set_map(&mapupper, $2, 0); }
    190 	|	DIGITMAP map
    191 		{ set_digitmap(&types, $2); }
    192 	;
    193 
    194 list	:	RUNE
    195 		{
    196 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    197 		    $$->min = ($1 & charsetmask) | charsetbits;
    198 		    $$->max = ($1 & charsetmask) | charsetbits;
    199 		    $$->next = 0;
    200 		}
    201 	|	RUNE THRU RUNE
    202 		{
    203 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    204 		    $$->min = ($1 & charsetmask) | charsetbits;
    205 		    $$->max = ($3 & charsetmask) | charsetbits;
    206 		    $$->next = 0;
    207 		}
    208 	|	list RUNE
    209 		{
    210 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    211 		    $$->min = ($2 & charsetmask) | charsetbits;
    212 		    $$->max = ($2 & charsetmask) | charsetbits;
    213 		    $$->next = $1;
    214 		}
    215 	|	list RUNE THRU RUNE
    216 		{
    217 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    218 		    $$->min = ($2 & charsetmask) | charsetbits;
    219 		    $$->max = ($4 & charsetmask) | charsetbits;
    220 		    $$->next = $1;
    221 		}
    222 	;
    223 
    224 map	:	LBRK RUNE RUNE RBRK
    225 		{
    226 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    227 		    $$->min = ($2 & charsetmask) | charsetbits;
    228 		    $$->max = ($2 & charsetmask) | charsetbits;
    229 		    $$->map = $3;
    230 		    $$->next = 0;
    231 		}
    232 	|	map LBRK RUNE RUNE RBRK
    233 		{
    234 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    235 		    $$->min = ($3 & charsetmask) | charsetbits;
    236 		    $$->max = ($3 & charsetmask) | charsetbits;
    237 		    $$->map = $4;
    238 		    $$->next = $1;
    239 		}
    240 	|	LBRK RUNE THRU RUNE ':' RUNE RBRK
    241 		{
    242 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    243 		    $$->min = ($2 & charsetmask) | charsetbits;
    244 		    $$->max = ($4 & charsetmask) | charsetbits;
    245 		    $$->map = $6;
    246 		    $$->next = 0;
    247 		}
    248 	|	map LBRK RUNE THRU RUNE ':' RUNE RBRK
    249 		{
    250 		    $$ = (rune_list *)malloc(sizeof(rune_list));
    251 		    $$->min = ($3 & charsetmask) | charsetbits;
    252 		    $$->max = ($5 & charsetmask) | charsetbits;
    253 		    $$->map = $7;
    254 		    $$->next = $1;
    255 		}
    256 	;
    257 %%
    258 
    259 int debug = 0;
    260 FILE *ofile;
    261 
    262 int
    263 main(int ac, char *av[])
    264 {
    265     int x;
    266     const char *locale_type;
    267 
    268     extern char *optarg;
    269     extern int optind;
    270 
    271     locale_type = NULL;
    272     while ((x = getopt(ac, av, "do:t:")) != EOF) {
    273 	switch(x) {
    274 	case 'd':
    275 	    debug = 1;
    276 	    break;
    277 	case 'o':
    278 	    locale_file = optarg;
    279 	    if ((ofile = fopen(locale_file, "w")) == 0)
    280 		err(1, "unable to open output file %s", locale_file);
    281 	    break;
    282         case 't':
    283 	    locale_type = optarg;
    284             break;
    285 	default:
    286 	    usage();
    287 	}
    288     }
    289 
    290     switch (ac - optind) {
    291     case 0:
    292 	break;
    293     case 1:
    294 	if (freopen(av[optind], "r", stdin) == 0)
    295 	    err(1, "unable to open input file %s", av[optind]);
    296 	break;
    297     default:
    298 	usage();
    299     }
    300 
    301     if (ofile == NULL)
    302 	ofile = stdout;
    303     if (locale_type != NULL && strcasecmp(locale_type, "CTYPE")) {
    304 	mklocaledb(locale_type, stdin, ofile);
    305 	return 0;
    306     }
    307 
    308     for (x = 0; x < _CTYPE_CACHE_SIZE; ++x) {
    309 	mapupper.map[x] = x;
    310 	maplower.map[x] = x;
    311     }
    312 
    313     new_locale.frl_invalid_rune = htonl((u_int32_t)_DEFAULT_INVALID_RUNE);
    314     memcpy(new_locale.frl_magic, _RUNECT10_MAGIC, sizeof(new_locale.frl_magic));
    315 
    316     yyparse();
    317 
    318     return 0;
    319 
    320 }
    321 
    322 void
    323 usage(void)
    324 {
    325     fprintf(stderr,
    326 	"usage: mklocale [-d] [-o output] [-t type] [source]\n");
    327 
    328     exit(1);
    329 }
    330 
    331 int
    332 yyerror(const char *s)
    333 {
    334     fprintf(stderr, "%s\n", s);
    335 
    336     return 0;
    337 }
    338 
    339 void *
    340 xmalloc(unsigned int sz)
    341 {
    342     void *r = malloc(sz);
    343     if (!r) {
    344 	perror("xmalloc");
    345 	abort();
    346     }
    347     return(r);
    348 }
    349 
    350 u_int32_t *
    351 xlalloc(unsigned int sz)
    352 {
    353     u_int32_t *r = (u_int32_t *)malloc(sz * sizeof(u_int32_t));
    354     if (!r) {
    355 	perror("xlalloc");
    356 	abort();
    357     }
    358     return(r);
    359 }
    360 
    361 u_int32_t *
    362 xrelalloc(u_int32_t *old, unsigned int sz)
    363 {
    364     u_int32_t *r = (u_int32_t *)realloc((char *)old,
    365 						sz * sizeof(u_int32_t));
    366     if (!r) {
    367 	perror("xrelalloc");
    368 	abort();
    369     }
    370     return(r);
    371 }
    372 
    373 void
    374 set_map(rune_map *map, rune_list *list, u_int32_t flag)
    375 {
    376     list->map &= charsetmask;
    377     list->map |= charsetbits;
    378     while (list) {
    379 	rune_list *nlist = list->next;
    380 	add_map(map, list, flag);
    381 	list = nlist;
    382     }
    383 }
    384 
    385 void
    386 set_digitmap(rune_map *map, rune_list *list)
    387 {
    388     __nbrune_t i;
    389 
    390     while (list) {
    391 	rune_list *nlist = list->next;
    392 	for (i = list->min; i <= list->max; ++i) {
    393 	    /*
    394 	     * XXX PR lib/57798
    395 	     * Currently, we support mapping up to 255. Attempts to map
    396 	     * 256 (== _RUNETYPE_A) and above are silently ignored.
    397 	     */
    398 	    _RuneType digit = list->map + (i - list->min);
    399 	    if (digit > 0 && digit <= 0xff) {
    400 		rune_list *tmp = (rune_list *)xmalloc(sizeof(rune_list));
    401 		memset(tmp, 0, sizeof(*tmp));
    402 		tmp->min = i;
    403 		tmp->max = i;
    404 		add_map(map, tmp, digit);
    405 	    }
    406 	}
    407 	free(list);
    408 	list = nlist;
    409     }
    410 }
    411 
    412 void
    413 add_map(rune_map *map, rune_list *list, u_int32_t flag)
    414 {
    415     __nbrune_t i;
    416     rune_list *lr = 0;
    417     rune_list *r;
    418     __nbrune_t run;
    419 
    420     while (list->min < _CTYPE_CACHE_SIZE && list->min <= list->max) {
    421 	if (flag)
    422 	    map->map[list->min++] |= flag;
    423 	else
    424 	    map->map[list->min++] = list->map++;
    425     }
    426 
    427     if (list->min > list->max) {
    428 	free(list);
    429 	return;
    430     }
    431 
    432     run = list->max - list->min + 1;
    433 
    434     if (!(r = map->root) || (list->max < r->min - 1)
    435 			 || (!flag && list->max == r->min - 1)) {
    436 	if (flag) {
    437 	    list->types = xlalloc(run);
    438 	    for (i = 0; i < run; ++i)
    439 		list->types[i] = flag;
    440 	}
    441 	list->next = map->root;
    442 	map->root = list;
    443 	return;
    444     }
    445 
    446     for (r = map->root; r && r->max + 1 < list->min; r = r->next)
    447 	lr = r;
    448 
    449     if (!r) {
    450 	/*
    451 	 * We are off the end.
    452 	 */
    453 	if (flag) {
    454 	    list->types = xlalloc(run);
    455 	    for (i = 0; i < run; ++i)
    456 		list->types[i] = flag;
    457 	}
    458 	list->next = 0;
    459 	lr->next = list;
    460 	return;
    461     }
    462 
    463     if (list->max < r->min - 1) {
    464 	/*
    465 	 * We come before this range and we do not intersect it.
    466 	 * We are not before the root node, it was checked before the loop
    467 	 */
    468 	if (flag) {
    469 	    list->types = xlalloc(run);
    470 	    for (i = 0; i < run; ++i)
    471 		list->types[i] = flag;
    472 	}
    473 	list->next = lr->next;
    474 	lr->next = list;
    475 	return;
    476     }
    477 
    478     /*
    479      * At this point we have found that we at least intersect with
    480      * the range pointed to by `r', we might intersect with one or
    481      * more ranges beyond `r' as well.
    482      */
    483 
    484     if (!flag && list->map - list->min != r->map - r->min) {
    485 	/*
    486 	 * There are only two cases when we are doing case maps and
    487 	 * our maps needn't have the same offset.  When we are adjoining
    488 	 * but not intersecting.
    489 	 */
    490 	if (list->max + 1 == r->min) {
    491 	    lr->next = list;
    492 	    list->next = r;
    493 	    return;
    494 	}
    495 	if (list->min - 1 == r->max) {
    496 	    list->next = r->next;
    497 	    r->next = list;
    498 	    return;
    499 	}
    500 	fprintf(stderr, "Error: conflicting map entries\n");
    501 	exit(1);
    502     }
    503 
    504     if (list->min >= r->min && list->max <= r->max) {
    505 	/*
    506 	 * Subset case.
    507 	 */
    508 
    509 	if (flag) {
    510 	    for (i = list->min; i <= list->max; ++i)
    511 		r->types[i - r->min] |= flag;
    512 	}
    513 	free(list);
    514 	return;
    515     }
    516     if (list->min <= r->min && list->max >= r->max) {
    517 	/*
    518 	 * Superset case.  Make him big enough to hold us.
    519 	 * We might need to merge with the guy after him.
    520 	 */
    521 	if (flag) {
    522 	    list->types = xlalloc(list->max - list->min + 1);
    523 
    524 	    for (i = list->min; i <= list->max; ++i)
    525 		list->types[i - list->min] = flag;
    526 
    527 	    for (i = r->min; i <= r->max; ++i)
    528 		list->types[i - list->min] |= r->types[i - r->min];
    529 
    530 	    free(r->types);
    531 	    r->types = list->types;
    532 	} else {
    533 	    r->map = list->map;
    534 	}
    535 	r->min = list->min;
    536 	r->max = list->max;
    537 	free(list);
    538     } else if (list->min < r->min) {
    539 	/*
    540 	 * Our tail intersects his head.
    541 	 */
    542 	if (flag) {
    543 	    list->types = xlalloc(r->max - list->min + 1);
    544 
    545 	    for (i = r->min; i <= r->max; ++i)
    546 		list->types[i - list->min] = r->types[i - r->min];
    547 
    548 	    for (i = list->min; i < r->min; ++i)
    549 		list->types[i - list->min] = flag;
    550 
    551 	    for (i = r->min; i <= list->max; ++i)
    552 		list->types[i - list->min] |= flag;
    553 
    554 	    free(r->types);
    555 	    r->types = list->types;
    556 	} else {
    557 	    r->map = list->map;
    558 	}
    559 	r->min = list->min;
    560 	free(list);
    561 	return;
    562     } else {
    563 	/*
    564 	 * Our head intersects his tail.
    565 	 * We might need to merge with the guy after him.
    566 	 */
    567 	if (flag) {
    568 	    r->types = xrelalloc(r->types, list->max - r->min + 1);
    569 
    570 	    for (i = list->min; i <= r->max; ++i)
    571 		r->types[i - r->min] |= flag;
    572 
    573 	    for (i = r->max+1; i <= list->max; ++i)
    574 		r->types[i - r->min] = flag;
    575 	}
    576 	r->max = list->max;
    577 	free(list);
    578     }
    579 
    580     /*
    581      * Okay, check to see if we grew into the next guy(s)
    582      */
    583     while ((lr = r->next) && r->max >= lr->min) {
    584 	if (flag) {
    585 	    if (r->max >= lr->max) {
    586 		/*
    587 		 * Good, we consumed all of him.
    588 		 */
    589 		for (i = lr->min; i <= lr->max; ++i)
    590 		    r->types[i - r->min] |= lr->types[i - lr->min];
    591 	    } else {
    592 		/*
    593 		 * "append" him on to the end of us.
    594 		 */
    595 		r->types = xrelalloc(r->types, lr->max - r->min + 1);
    596 
    597 		for (i = lr->min; i <= r->max; ++i)
    598 		    r->types[i - r->min] |= lr->types[i - lr->min];
    599 
    600 		for (i = r->max+1; i <= lr->max; ++i)
    601 		    r->types[i - r->min] = lr->types[i - lr->min];
    602 
    603 		r->max = lr->max;
    604 	    }
    605 	} else {
    606 	    if (lr->max > r->max)
    607 		r->max = lr->max;
    608 	}
    609 
    610 	r->next = lr->next;
    611 
    612 	if (flag)
    613 	    free(lr->types);
    614 	free(lr);
    615     }
    616 }
    617 
    618 void
    619 dump_tables(void)
    620 {
    621     int x, n;
    622     rune_list *list;
    623     FILE *fp = ofile;
    624     u_int32_t nranges;
    625 
    626     /*
    627      * See if we can compress some of the istype arrays
    628      */
    629     for(list = types.root; list; list = list->next) {
    630 	list->map = list->types[0];
    631 	for (x = 1; x < list->max - list->min + 1; ++x) {
    632 	    if (list->types[x] != list->map) {
    633 		list->map = 0;
    634 		break;
    635 	    }
    636 	}
    637     }
    638 
    639     /*
    640      * Fill in our tables.  Do this in network order so that
    641      * diverse machines have a chance of sharing data.
    642      * (Machines like Crays cannot share with little machines due to
    643      *  word size.  Sigh.  We tried.)
    644      */
    645     for (x = 0; x < _CTYPE_CACHE_SIZE; ++x) {
    646 	new_locale.frl_runetype[x] = htonl(types.map[x]);
    647 	new_locale.frl_maplower[x] = htonl(maplower.map[x]);
    648 	new_locale.frl_mapupper[x] = htonl(mapupper.map[x]);
    649     }
    650 
    651     /*
    652      * Count up how many ranges we will need for each of the extents.
    653      */
    654     list = types.root;
    655 
    656     nranges = (u_int32_t)0;
    657     while (list) {
    658 	++nranges;
    659 	list = list->next;
    660     }
    661     new_locale.frl_runetype_ext.frr_nranges =
    662 	htonl(nranges);
    663 
    664     list = maplower.root;
    665 
    666     nranges = (u_int32_t)0;
    667     while (list) {
    668 	++nranges;
    669 	list = list->next;
    670     }
    671     new_locale.frl_maplower_ext.frr_nranges =
    672 	htonl(nranges);
    673 
    674     list = mapupper.root;
    675 
    676     nranges = (u_int32_t)0;
    677     while (list) {
    678 	++nranges;
    679 	list = list->next;
    680     }
    681     new_locale.frl_mapupper_ext.frr_nranges =
    682 	htonl(nranges);
    683 
    684     /*
    685      * Okay, we are now ready to write the new locale file.
    686      */
    687 
    688     /*
    689      * PART 1: The _RuneLocale structure
    690      */
    691     if (fwrite((char *)&new_locale, sizeof(new_locale), 1, fp) != 1)
    692 	err(1, "writing _FileRuneLocale to %s", locale_file);
    693     /*
    694      * PART 2: The runetype_ext structures (not the actual tables)
    695      */
    696     for (list = types.root, n = 0; list != NULL; list = list->next, n++) {
    697 	_FileRuneEntry re;
    698 
    699 	memset(&re, 0, sizeof(re));
    700 	re.fre_min = htonl(list->min);
    701 	re.fre_max = htonl(list->max);
    702 	re.fre_map = htonl(list->map);
    703 
    704 	if (fwrite((char *)&re, sizeof(re), 1, fp) != 1)
    705 	    err(1, "writing runetype_ext #%d to %s", n, locale_file);
    706     }
    707     /*
    708      * PART 3: The maplower_ext structures
    709      */
    710     for (list = maplower.root, n = 0; list != NULL; list = list->next, n++) {
    711 	_FileRuneEntry re;
    712 
    713 	memset(&re, 0, sizeof(re));
    714 	re.fre_min = htonl(list->min);
    715 	re.fre_max = htonl(list->max);
    716 	re.fre_map = htonl(list->map);
    717 
    718 	if (fwrite((char *)&re, sizeof(re), 1, fp) != 1)
    719 	    err(1, "writing maplower_ext #%d to %s", n, locale_file);
    720     }
    721     /*
    722      * PART 4: The mapupper_ext structures
    723      */
    724     for (list = mapupper.root, n = 0; list != NULL; list = list->next, n++) {
    725 	_FileRuneEntry re;
    726 
    727 	memset(&re, 0, sizeof(re));
    728 	re.fre_min = htonl(list->min);
    729 	re.fre_max = htonl(list->max);
    730 	re.fre_map = htonl(list->map);
    731 
    732 	if (fwrite((char *)&re, sizeof(re), 1, fp) != 1)
    733 	    err(1, "writing mapupper_ext #%d to %s", n, locale_file);
    734     }
    735     /*
    736      * PART 5: The runetype_ext tables
    737      */
    738     for (list = types.root, n = 0; list != NULL; list = list->next, n++) {
    739 	for (x = 0; x < list->max - list->min + 1; ++x)
    740 	    list->types[x] = htonl(list->types[x]);
    741 
    742 	if (!list->map) {
    743 	    if (fwrite((char *)list->types,
    744 		       (list->max - list->min + 1) * sizeof(u_int32_t),
    745 		       1, fp) != 1)
    746 		err(1, "writing runetype_ext table #%d to %s", n, locale_file);
    747 	}
    748     }
    749     /*
    750      * PART 5: And finally the variable data
    751      */
    752     if (rl_variable_len != 0 &&
    753 	fwrite((char *)rl_variable, rl_variable_len, 1, fp) != 1)
    754 	err(1, "writing variable data to %s", locale_file);
    755     fclose(fp);
    756 
    757     if (!debug)
    758 	return;
    759 
    760     if (new_locale.frl_encoding[0])
    761 	fprintf(stderr, "ENCODING	%.*s\n",
    762 	    (int)sizeof(new_locale.frl_encoding), new_locale.frl_encoding);
    763     if (rl_variable)
    764 	fprintf(stderr, "VARIABLE	%.*s\n",
    765 	    (int)rl_variable_len, (char *)rl_variable);
    766 
    767     fprintf(stderr, "\nMAPLOWER:\n\n");
    768 
    769     for (x = 0; x < _CTYPE_CACHE_SIZE; ++x) {
    770 	if (isprint(maplower.map[x]))
    771 	    fprintf(stderr, " '%c'", (int)maplower.map[x]);
    772 	else if (maplower.map[x])
    773 	    fprintf(stderr, "%04x", maplower.map[x]);
    774 	else
    775 	    fprintf(stderr, "%4x", 0);
    776 	if ((x & 0xf) == 0xf)
    777 	    fprintf(stderr, "\n");
    778 	else
    779 	    fprintf(stderr, " ");
    780     }
    781     fprintf(stderr, "\n");
    782 
    783     for (list = maplower.root; list; list = list->next)
    784 	fprintf(stderr, "\t%04x - %04x : %04x\n", list->min, list->max, list->map);
    785 
    786     fprintf(stderr, "\nMAPUPPER:\n\n");
    787 
    788     for (x = 0; x < _CTYPE_CACHE_SIZE; ++x) {
    789 	if (isprint(mapupper.map[x]))
    790 	    fprintf(stderr, " '%c'", (int)mapupper.map[x]);
    791 	else if (mapupper.map[x])
    792 	    fprintf(stderr, "%04x", mapupper.map[x]);
    793 	else
    794 	    fprintf(stderr, "%4x", 0);
    795 	if ((x & 0xf) == 0xf)
    796 	    fprintf(stderr, "\n");
    797 	else
    798 	    fprintf(stderr, " ");
    799     }
    800     fprintf(stderr, "\n");
    801 
    802     for (list = mapupper.root; list; list = list->next)
    803 	fprintf(stderr, "\t%04x - %04x : %04x\n", list->min, list->max, list->map);
    804 
    805 
    806     fprintf(stderr, "\nTYPES:\n\n");
    807 
    808     for (x = 0; x < _CTYPE_CACHE_SIZE; ++x) {
    809 	u_int32_t r = types.map[x];
    810 
    811 	if (r) {
    812 	    if (isprint(x))
    813 		fprintf(stderr, " '%c':%2d", x, (int)(r & 0xff));
    814 	    else
    815 		fprintf(stderr, "%04x:%2d", x, (int)(r & 0xff));
    816 
    817 	    fprintf(stderr, " %4s", (r & _RUNETYPE_A) ? "alph" : "");
    818 	    fprintf(stderr, " %4s", (r & _RUNETYPE_C) ? "ctrl" : "");
    819 	    fprintf(stderr, " %4s", (r & _RUNETYPE_D) ? "dig" : "");
    820 	    fprintf(stderr, " %4s", (r & _RUNETYPE_G) ? "graf" : "");
    821 	    fprintf(stderr, " %4s", (r & _RUNETYPE_L) ? "low" : "");
    822 	    fprintf(stderr, " %4s", (r & _RUNETYPE_P) ? "punc" : "");
    823 	    fprintf(stderr, " %4s", (r & _RUNETYPE_S) ? "spac" : "");
    824 	    fprintf(stderr, " %4s", (r & _RUNETYPE_U) ? "upp" : "");
    825 	    fprintf(stderr, " %4s", (r & _RUNETYPE_X) ? "xdig" : "");
    826 	    fprintf(stderr, " %4s", (r & _RUNETYPE_B) ? "blnk" : "");
    827 	    fprintf(stderr, " %4s", (r & _RUNETYPE_R) ? "prnt" : "");
    828 	    fprintf(stderr, " %4s", (r & _RUNETYPE_I) ? "ideo" : "");
    829 	    fprintf(stderr, " %4s", (r & _RUNETYPE_T) ? "spec" : "");
    830 	    fprintf(stderr, " %4s", (r & _RUNETYPE_Q) ? "phon" : "");
    831 	    fprintf(stderr, "\n");
    832 	}
    833     }
    834 
    835     for (list = types.root; list; list = list->next) {
    836 	if (list->map && list->min + 3 < list->max) {
    837 	    u_int32_t r = list->map;
    838 
    839 	    fprintf(stderr, "%04x:%2d", list->min, r & 0xff);
    840 
    841 	    fprintf(stderr, " %4s", (r & _RUNETYPE_A) ? "alph" : "");
    842 	    fprintf(stderr, " %4s", (r & _RUNETYPE_C) ? "ctrl" : "");
    843 	    fprintf(stderr, " %4s", (r & _RUNETYPE_D) ? "dig" : "");
    844 	    fprintf(stderr, " %4s", (r & _RUNETYPE_G) ? "graf" : "");
    845 	    fprintf(stderr, " %4s", (r & _RUNETYPE_L) ? "low" : "");
    846 	    fprintf(stderr, " %4s", (r & _RUNETYPE_P) ? "punc" : "");
    847 	    fprintf(stderr, " %4s", (r & _RUNETYPE_S) ? "spac" : "");
    848 	    fprintf(stderr, " %4s", (r & _RUNETYPE_U) ? "upp" : "");
    849 	    fprintf(stderr, " %4s", (r & _RUNETYPE_X) ? "xdig" : "");
    850 	    fprintf(stderr, " %4s", (r & _RUNETYPE_B) ? "blnk" : "");
    851 	    fprintf(stderr, " %4s", (r & _RUNETYPE_R) ? "prnt" : "");
    852 	    fprintf(stderr, " %4s", (r & _RUNETYPE_I) ? "ideo" : "");
    853 	    fprintf(stderr, " %4s", (r & _RUNETYPE_T) ? "spec" : "");
    854 	    fprintf(stderr, " %4s", (r & _RUNETYPE_Q) ? "phon" : "");
    855 	    fprintf(stderr, "\n...\n");
    856 
    857 	    fprintf(stderr, "%04x:%2d", list->max, r & 0xff);
    858 
    859 	    fprintf(stderr, " %4s", (r & _RUNETYPE_A) ? "alph" : "");
    860 	    fprintf(stderr, " %4s", (r & _RUNETYPE_C) ? "ctrl" : "");
    861 	    fprintf(stderr, " %4s", (r & _RUNETYPE_D) ? "dig" : "");
    862 	    fprintf(stderr, " %4s", (r & _RUNETYPE_G) ? "graf" : "");
    863 	    fprintf(stderr, " %4s", (r & _RUNETYPE_L) ? "low" : "");
    864 	    fprintf(stderr, " %4s", (r & _RUNETYPE_P) ? "punc" : "");
    865 	    fprintf(stderr, " %4s", (r & _RUNETYPE_S) ? "spac" : "");
    866 	    fprintf(stderr, " %4s", (r & _RUNETYPE_U) ? "upp" : "");
    867 	    fprintf(stderr, " %4s", (r & _RUNETYPE_X) ? "xdig" : "");
    868 	    fprintf(stderr, " %4s", (r & _RUNETYPE_B) ? "blnk" : "");
    869 	    fprintf(stderr, " %4s", (r & _RUNETYPE_R) ? "prnt" : "");
    870 	    fprintf(stderr, " %4s", (r & _RUNETYPE_I) ? "ideo" : "");
    871 	    fprintf(stderr, " %4s", (r & _RUNETYPE_T) ? "spec" : "");
    872 	    fprintf(stderr, " %4s", (r & _RUNETYPE_Q) ? "phon" : "");
    873             fprintf(stderr, " %1u", (unsigned)((r & _RUNETYPE_SWM)>>_RUNETYPE_SWS));
    874 	    fprintf(stderr, "\n");
    875 	} else
    876 	for (x = list->min; x <= list->max; ++x) {
    877 	    u_int32_t r = ntohl(list->types[x - list->min]);
    878 
    879 	    if (r) {
    880 		fprintf(stderr, "%04x:%2d", x, (int)(r & 0xff));
    881 
    882 		fprintf(stderr, " %4s", (r & _RUNETYPE_A) ? "alph" : "");
    883 		fprintf(stderr, " %4s", (r & _RUNETYPE_C) ? "ctrl" : "");
    884 		fprintf(stderr, " %4s", (r & _RUNETYPE_D) ? "dig" : "");
    885 		fprintf(stderr, " %4s", (r & _RUNETYPE_G) ? "graf" : "");
    886 		fprintf(stderr, " %4s", (r & _RUNETYPE_L) ? "low" : "");
    887 		fprintf(stderr, " %4s", (r & _RUNETYPE_P) ? "punc" : "");
    888 		fprintf(stderr, " %4s", (r & _RUNETYPE_S) ? "spac" : "");
    889 		fprintf(stderr, " %4s", (r & _RUNETYPE_U) ? "upp" : "");
    890 		fprintf(stderr, " %4s", (r & _RUNETYPE_X) ? "xdig" : "");
    891 		fprintf(stderr, " %4s", (r & _RUNETYPE_B) ? "blnk" : "");
    892 		fprintf(stderr, " %4s", (r & _RUNETYPE_R) ? "prnt" : "");
    893 		fprintf(stderr, " %4s", (r & _RUNETYPE_I) ? "ideo" : "");
    894 		fprintf(stderr, " %4s", (r & _RUNETYPE_T) ? "spec" : "");
    895 		fprintf(stderr, " %4s", (r & _RUNETYPE_Q) ? "phon" : "");
    896                 fprintf(stderr, " %1u", (unsigned)((r & _RUNETYPE_SWM)>>_RUNETYPE_SWS));
    897 		fprintf(stderr, "\n");
    898 	    }
    899 	}
    900     }
    901 }
    902