Home | History | Annotate | Line # | Download | only in locale
locale.c revision 1.5.28.2
      1  1.5.28.2       snj /*	$NetBSD: locale.c,v 1.5.28.2 2009/01/26 00:46:33 snj Exp $	*/
      2       1.1  tshiozak 
      3       1.1  tshiozak /*-
      4       1.1  tshiozak  * Copyright (c) 2002, 2003 Alexey Zelkin <phantom (at) FreeBSD.org>
      5       1.1  tshiozak  * All rights reserved.
      6       1.1  tshiozak  *
      7       1.1  tshiozak  * Redistribution and use in source and binary forms, with or without
      8       1.1  tshiozak  * modification, are permitted provided that the following conditions
      9       1.1  tshiozak  * are met:
     10       1.1  tshiozak  * 1. Redistributions of source code must retain the above copyright
     11       1.1  tshiozak  *    notice, this list of conditions and the following disclaimer.
     12       1.1  tshiozak  * 2. Redistributions in binary form must reproduce the above copyright
     13       1.1  tshiozak  *    notice, this list of conditions and the following disclaimer in the
     14       1.1  tshiozak  *    documentation and/or other materials provided with the distribution.
     15       1.1  tshiozak  *
     16       1.1  tshiozak  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17       1.1  tshiozak  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18       1.1  tshiozak  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19       1.1  tshiozak  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20       1.1  tshiozak  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21       1.1  tshiozak  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22       1.1  tshiozak  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23       1.1  tshiozak  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24       1.1  tshiozak  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25       1.1  tshiozak  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26       1.1  tshiozak  * SUCH DAMAGE.
     27       1.1  tshiozak  *
     28       1.1  tshiozak  * FreeBSD: src/usr.bin/locale/locale.c,v 1.10 2003/06/26 11:05:56 phantom Exp
     29       1.1  tshiozak  */
     30       1.1  tshiozak 
     31       1.1  tshiozak #include <sys/cdefs.h>
     32       1.1  tshiozak #if defined(LIBC_SCCS) && !defined(lint)
     33  1.5.28.2       snj __RCSID("$NetBSD: locale.c,v 1.5.28.2 2009/01/26 00:46:33 snj Exp $");
     34       1.1  tshiozak #endif /* LIBC_SCCS and not lint */
     35       1.1  tshiozak 
     36       1.1  tshiozak /*
     37       1.1  tshiozak  * XXX: implement missing era_* (LC_TIME) keywords (require libc &
     38       1.1  tshiozak  *	nl_langinfo(3) extensions)
     39       1.1  tshiozak  *
     40       1.1  tshiozak  * XXX: correctly handle reserved 'charmap' keyword and '-m' option (require
     41       1.1  tshiozak  *      localedef(1) implementation).  Currently it's handled via
     42       1.1  tshiozak  *	nl_langinfo(CODESET).
     43       1.1  tshiozak  */
     44       1.1  tshiozak 
     45       1.1  tshiozak #include <sys/types.h>
     46       1.5   tnozaki #include <assert.h>
     47       1.1  tshiozak #include <dirent.h>
     48       1.1  tshiozak #include <err.h>
     49       1.1  tshiozak #include <locale.h>
     50       1.1  tshiozak #include <langinfo.h>
     51       1.5   tnozaki #include <limits.h>
     52       1.1  tshiozak #include <paths.h>
     53       1.1  tshiozak #include <stdio.h>
     54       1.1  tshiozak #include <stdlib.h>
     55       1.1  tshiozak #include <string.h>
     56       1.1  tshiozak #include <stringlist.h>
     57       1.1  tshiozak #include <unistd.h>
     58       1.1  tshiozak 
     59       1.5   tnozaki #ifdef CITRUS
     60       1.5   tnozaki #include "citrus_namespace.h"
     61       1.5   tnozaki #include "citrus_region.h"
     62       1.5   tnozaki #include "citrus_lookup.h"
     63       1.5   tnozaki #endif
     64  1.5.28.1       snj #include "setlocale_local.h"
     65       1.5   tnozaki 
     66       1.1  tshiozak /* Local prototypes */
     67       1.1  tshiozak void	init_locales_list(void);
     68       1.5   tnozaki void	init_locales_list_alias(void);
     69       1.1  tshiozak void	list_charmaps(void);
     70       1.1  tshiozak void	list_locales(void);
     71       1.1  tshiozak const char *lookup_localecat(int);
     72       1.1  tshiozak char	*kwval_lconv(int);
     73       1.1  tshiozak int	kwval_lookup(char *, char **, int *, int *);
     74       1.1  tshiozak void	showdetails(char *);
     75       1.1  tshiozak void	showkeywordslist(void);
     76       1.1  tshiozak void	showlocale(void);
     77       1.1  tshiozak void	usage(void);
     78       1.1  tshiozak 
     79       1.1  tshiozak /* Global variables */
     80       1.1  tshiozak static StringList *locales = NULL;
     81       1.1  tshiozak 
     82       1.1  tshiozak int	all_locales = 0;
     83       1.1  tshiozak int	all_charmaps = 0;
     84       1.1  tshiozak int	prt_categories = 0;
     85       1.1  tshiozak int	prt_keywords = 0;
     86       1.1  tshiozak int	more_params = 0;
     87       1.1  tshiozak 
     88       1.1  tshiozak struct _lcinfo {
     89       1.1  tshiozak 	const char	*name;
     90       1.1  tshiozak 	int		id;
     91       1.1  tshiozak } lcinfo [] = {
     92       1.1  tshiozak 	{ "LC_CTYPE",		LC_CTYPE },
     93       1.1  tshiozak 	{ "LC_COLLATE",		LC_COLLATE },
     94       1.1  tshiozak 	{ "LC_TIME",		LC_TIME },
     95       1.1  tshiozak 	{ "LC_NUMERIC",		LC_NUMERIC },
     96       1.1  tshiozak 	{ "LC_MONETARY",	LC_MONETARY },
     97       1.1  tshiozak 	{ "LC_MESSAGES",	LC_MESSAGES }
     98       1.1  tshiozak };
     99       1.1  tshiozak #define NLCINFO (sizeof(lcinfo)/sizeof(lcinfo[0]))
    100       1.1  tshiozak 
    101       1.1  tshiozak /* ids for values not referenced by nl_langinfo() */
    102       1.1  tshiozak #define	KW_ZERO			10000
    103       1.1  tshiozak #define	KW_GROUPING		(KW_ZERO+1)
    104       1.1  tshiozak #define KW_INT_CURR_SYMBOL 	(KW_ZERO+2)
    105       1.1  tshiozak #define KW_CURRENCY_SYMBOL 	(KW_ZERO+3)
    106       1.1  tshiozak #define KW_MON_DECIMAL_POINT 	(KW_ZERO+4)
    107       1.1  tshiozak #define KW_MON_THOUSANDS_SEP 	(KW_ZERO+5)
    108       1.1  tshiozak #define KW_MON_GROUPING 	(KW_ZERO+6)
    109       1.1  tshiozak #define KW_POSITIVE_SIGN 	(KW_ZERO+7)
    110       1.1  tshiozak #define KW_NEGATIVE_SIGN 	(KW_ZERO+8)
    111       1.1  tshiozak #define KW_INT_FRAC_DIGITS 	(KW_ZERO+9)
    112       1.1  tshiozak #define KW_FRAC_DIGITS 		(KW_ZERO+10)
    113       1.1  tshiozak #define KW_P_CS_PRECEDES 	(KW_ZERO+11)
    114       1.1  tshiozak #define KW_P_SEP_BY_SPACE 	(KW_ZERO+12)
    115       1.1  tshiozak #define KW_N_CS_PRECEDES 	(KW_ZERO+13)
    116       1.1  tshiozak #define KW_N_SEP_BY_SPACE 	(KW_ZERO+14)
    117       1.1  tshiozak #define KW_P_SIGN_POSN 		(KW_ZERO+15)
    118       1.1  tshiozak #define KW_N_SIGN_POSN 		(KW_ZERO+16)
    119       1.1  tshiozak #define KW_INT_P_CS_PRECEDES 	(KW_ZERO+17)
    120       1.1  tshiozak #define KW_INT_P_SEP_BY_SPACE 	(KW_ZERO+18)
    121       1.1  tshiozak #define KW_INT_N_CS_PRECEDES 	(KW_ZERO+19)
    122       1.1  tshiozak #define KW_INT_N_SEP_BY_SPACE 	(KW_ZERO+20)
    123       1.1  tshiozak #define KW_INT_P_SIGN_POSN 	(KW_ZERO+21)
    124       1.1  tshiozak #define KW_INT_N_SIGN_POSN 	(KW_ZERO+22)
    125       1.1  tshiozak 
    126       1.1  tshiozak struct _kwinfo {
    127       1.1  tshiozak 	const char	*name;
    128       1.1  tshiozak 	int		isstr;		/* true - string, false - number */
    129       1.1  tshiozak 	int		catid;		/* LC_* */
    130       1.1  tshiozak 	int		value_ref;
    131       1.1  tshiozak 	const char	*comment;
    132       1.1  tshiozak } kwinfo [] = {
    133       1.1  tshiozak 	{ "charmap",		1, LC_CTYPE,	CODESET, "" },	/* hack */
    134       1.1  tshiozak 
    135       1.1  tshiozak 	{ "decimal_point",	1, LC_NUMERIC,	RADIXCHAR, "" },
    136       1.1  tshiozak 	{ "thousands_sep",	1, LC_NUMERIC,	THOUSEP, "" },
    137       1.1  tshiozak 	{ "grouping",		1, LC_NUMERIC,	KW_GROUPING, "" },
    138       1.1  tshiozak 	{ "radixchar",		1, LC_NUMERIC,	RADIXCHAR,
    139       1.1  tshiozak 	  "Same as decimal_point (BSD only)" },			/* compat */
    140       1.1  tshiozak 	{ "thousep",		1, LC_NUMERIC,	THOUSEP,
    141       1.1  tshiozak 	  "Same as thousands_sep (BSD only)" },			/* compat */
    142       1.1  tshiozak 
    143       1.1  tshiozak 	{ "int_curr_symbol",	1, LC_MONETARY,	KW_INT_CURR_SYMBOL, "" },
    144       1.1  tshiozak 	{ "currency_symbol",	1, LC_MONETARY,	KW_CURRENCY_SYMBOL, "" },
    145       1.1  tshiozak 	{ "mon_decimal_point",	1, LC_MONETARY,	KW_MON_DECIMAL_POINT, "" },
    146       1.1  tshiozak 	{ "mon_thousands_sep",	1, LC_MONETARY,	KW_MON_THOUSANDS_SEP, "" },
    147       1.1  tshiozak 	{ "mon_grouping",	1, LC_MONETARY,	KW_MON_GROUPING, "" },
    148       1.1  tshiozak 	{ "positive_sign",	1, LC_MONETARY,	KW_POSITIVE_SIGN, "" },
    149       1.1  tshiozak 	{ "negative_sign",	1, LC_MONETARY,	KW_NEGATIVE_SIGN, "" },
    150       1.1  tshiozak 
    151       1.1  tshiozak 	{ "int_frac_digits",	0, LC_MONETARY,	KW_INT_FRAC_DIGITS, "" },
    152       1.1  tshiozak 	{ "frac_digits",	0, LC_MONETARY,	KW_FRAC_DIGITS, "" },
    153       1.1  tshiozak 	{ "p_cs_precedes",	0, LC_MONETARY,	KW_P_CS_PRECEDES, "" },
    154       1.1  tshiozak 	{ "p_sep_by_space",	0, LC_MONETARY,	KW_P_SEP_BY_SPACE, "" },
    155       1.1  tshiozak 	{ "n_cs_precedes",	0, LC_MONETARY,	KW_N_CS_PRECEDES, "" },
    156       1.1  tshiozak 	{ "n_sep_by_space",	0, LC_MONETARY,	KW_N_SEP_BY_SPACE, "" },
    157       1.1  tshiozak 	{ "p_sign_posn",	0, LC_MONETARY,	KW_P_SIGN_POSN, "" },
    158       1.1  tshiozak 	{ "n_sign_posn",	0, LC_MONETARY,	KW_N_SIGN_POSN, "" },
    159       1.1  tshiozak 	{ "int_p_cs_precedes",	0, LC_MONETARY,	KW_INT_P_CS_PRECEDES, "" },
    160       1.1  tshiozak 	{ "int_p_sep_by_space",	0, LC_MONETARY,	KW_INT_P_SEP_BY_SPACE, "" },
    161       1.1  tshiozak 	{ "int_n_cs_precedes",	0, LC_MONETARY,	KW_INT_N_CS_PRECEDES, "" },
    162       1.1  tshiozak 	{ "int_n_sep_by_space",	0, LC_MONETARY,	KW_INT_N_SEP_BY_SPACE, "" },
    163       1.1  tshiozak 	{ "int_p_sign_posn",	0, LC_MONETARY,	KW_INT_P_SIGN_POSN, "" },
    164       1.1  tshiozak 	{ "int_n_sign_posn",	0, LC_MONETARY,	KW_INT_N_SIGN_POSN, "" },
    165       1.1  tshiozak 
    166       1.1  tshiozak 	{ "d_t_fmt",		1, LC_TIME,	D_T_FMT, "" },
    167       1.1  tshiozak 	{ "d_fmt",		1, LC_TIME,	D_FMT, "" },
    168       1.1  tshiozak 	{ "t_fmt",		1, LC_TIME,	T_FMT, "" },
    169       1.1  tshiozak 	{ "am_str",		1, LC_TIME,	AM_STR, "" },
    170       1.1  tshiozak 	{ "pm_str",		1, LC_TIME,	PM_STR, "" },
    171       1.1  tshiozak 	{ "t_fmt_ampm",		1, LC_TIME,	T_FMT_AMPM, "" },
    172       1.1  tshiozak 	{ "day_1",		1, LC_TIME,	DAY_1, "" },
    173       1.1  tshiozak 	{ "day_2",		1, LC_TIME,	DAY_2, "" },
    174       1.1  tshiozak 	{ "day_3",		1, LC_TIME,	DAY_3, "" },
    175       1.1  tshiozak 	{ "day_4",		1, LC_TIME,	DAY_4, "" },
    176       1.1  tshiozak 	{ "day_5",		1, LC_TIME,	DAY_5, "" },
    177       1.1  tshiozak 	{ "day_6",		1, LC_TIME,	DAY_6, "" },
    178       1.1  tshiozak 	{ "day_7",		1, LC_TIME,	DAY_7, "" },
    179       1.1  tshiozak 	{ "abday_1",		1, LC_TIME,	ABDAY_1, "" },
    180       1.1  tshiozak 	{ "abday_2",		1, LC_TIME,	ABDAY_2, "" },
    181       1.1  tshiozak 	{ "abday_3",		1, LC_TIME,	ABDAY_3, "" },
    182       1.1  tshiozak 	{ "abday_4",		1, LC_TIME,	ABDAY_4, "" },
    183       1.1  tshiozak 	{ "abday_5",		1, LC_TIME,	ABDAY_5, "" },
    184       1.1  tshiozak 	{ "abday_6",		1, LC_TIME,	ABDAY_6, "" },
    185       1.1  tshiozak 	{ "abday_7",		1, LC_TIME,	ABDAY_7, "" },
    186       1.1  tshiozak 	{ "mon_1",		1, LC_TIME,	MON_1, "" },
    187       1.1  tshiozak 	{ "mon_2",		1, LC_TIME,	MON_2, "" },
    188       1.1  tshiozak 	{ "mon_3",		1, LC_TIME,	MON_3, "" },
    189       1.1  tshiozak 	{ "mon_4",		1, LC_TIME,	MON_4, "" },
    190       1.1  tshiozak 	{ "mon_5",		1, LC_TIME,	MON_5, "" },
    191       1.1  tshiozak 	{ "mon_6",		1, LC_TIME,	MON_6, "" },
    192       1.1  tshiozak 	{ "mon_7",		1, LC_TIME,	MON_7, "" },
    193       1.1  tshiozak 	{ "mon_8",		1, LC_TIME,	MON_8, "" },
    194       1.1  tshiozak 	{ "mon_9",		1, LC_TIME,	MON_9, "" },
    195       1.1  tshiozak 	{ "mon_10",		1, LC_TIME,	MON_10, "" },
    196       1.1  tshiozak 	{ "mon_11",		1, LC_TIME,	MON_11, "" },
    197       1.1  tshiozak 	{ "mon_12",		1, LC_TIME,	MON_12, "" },
    198       1.1  tshiozak 	{ "abmon_1",		1, LC_TIME,	ABMON_1, "" },
    199       1.1  tshiozak 	{ "abmon_2",		1, LC_TIME,	ABMON_2, "" },
    200       1.1  tshiozak 	{ "abmon_3",		1, LC_TIME,	ABMON_3, "" },
    201       1.1  tshiozak 	{ "abmon_4",		1, LC_TIME,	ABMON_4, "" },
    202       1.1  tshiozak 	{ "abmon_5",		1, LC_TIME,	ABMON_5, "" },
    203       1.1  tshiozak 	{ "abmon_6",		1, LC_TIME,	ABMON_6, "" },
    204       1.1  tshiozak 	{ "abmon_7",		1, LC_TIME,	ABMON_7, "" },
    205       1.1  tshiozak 	{ "abmon_8",		1, LC_TIME,	ABMON_8, "" },
    206       1.1  tshiozak 	{ "abmon_9",		1, LC_TIME,	ABMON_9, "" },
    207       1.1  tshiozak 	{ "abmon_10",		1, LC_TIME,	ABMON_10, "" },
    208       1.1  tshiozak 	{ "abmon_11",		1, LC_TIME,	ABMON_11, "" },
    209       1.1  tshiozak 	{ "abmon_12",		1, LC_TIME,	ABMON_12, "" },
    210       1.1  tshiozak 	{ "era",		1, LC_TIME,	ERA, "(unavailable)" },
    211       1.1  tshiozak 	{ "era_d_fmt",		1, LC_TIME,	ERA_D_FMT, "(unavailable)" },
    212       1.1  tshiozak 	{ "era_d_t_fmt",	1, LC_TIME,	ERA_D_T_FMT, "(unavailable)" },
    213       1.1  tshiozak 	{ "era_t_fmt",		1, LC_TIME,	ERA_T_FMT, "(unavailable)" },
    214       1.1  tshiozak 	{ "alt_digits",		1, LC_TIME,	ALT_DIGITS, "" },
    215       1.1  tshiozak 
    216       1.1  tshiozak 	{ "yesexpr",		1, LC_MESSAGES, YESEXPR, "" },
    217       1.1  tshiozak 	{ "noexpr",		1, LC_MESSAGES, NOEXPR, "" },
    218       1.1  tshiozak 	{ "yesstr",		1, LC_MESSAGES, YESSTR,
    219       1.1  tshiozak 	  "(POSIX legacy)" },					/* compat */
    220       1.1  tshiozak 	{ "nostr",		1, LC_MESSAGES, NOSTR,
    221       1.1  tshiozak 	  "(POSIX legacy)" }					/* compat */
    222       1.1  tshiozak 
    223       1.1  tshiozak };
    224       1.1  tshiozak #define NKWINFO (sizeof(kwinfo)/sizeof(kwinfo[0]))
    225       1.1  tshiozak 
    226       1.1  tshiozak int
    227       1.1  tshiozak main(int argc, char *argv[])
    228       1.1  tshiozak {
    229       1.2       agc 	int	ch;
    230       1.1  tshiozak 	int	tmp;
    231       1.1  tshiozak 
    232       1.1  tshiozak 	while ((ch = getopt(argc, argv, "ackm")) != -1) {
    233       1.1  tshiozak 		switch (ch) {
    234       1.1  tshiozak 		case 'a':
    235       1.1  tshiozak 			all_locales = 1;
    236       1.1  tshiozak 			break;
    237       1.1  tshiozak 		case 'c':
    238       1.1  tshiozak 			prt_categories = 1;
    239       1.1  tshiozak 			break;
    240       1.1  tshiozak 		case 'k':
    241       1.1  tshiozak 			prt_keywords = 1;
    242       1.1  tshiozak 			break;
    243       1.1  tshiozak 		case 'm':
    244       1.1  tshiozak 			all_charmaps = 1;
    245       1.1  tshiozak 			break;
    246       1.1  tshiozak 		default:
    247       1.1  tshiozak 			usage();
    248       1.1  tshiozak 		}
    249       1.1  tshiozak 	}
    250       1.1  tshiozak 	argc -= optind;
    251       1.1  tshiozak 	argv += optind;
    252       1.1  tshiozak 
    253       1.1  tshiozak 	/* validate arguments */
    254       1.1  tshiozak 	if (all_locales && all_charmaps)
    255       1.1  tshiozak 		usage();
    256       1.1  tshiozak 	if ((all_locales || all_charmaps) && argc > 0)
    257       1.1  tshiozak 		usage();
    258       1.1  tshiozak 	if ((all_locales || all_charmaps) && (prt_categories || prt_keywords))
    259       1.1  tshiozak 		usage();
    260       1.1  tshiozak 	if ((prt_categories || prt_keywords) && argc <= 0)
    261       1.1  tshiozak 		usage();
    262       1.1  tshiozak 
    263       1.1  tshiozak 	/* process '-a' */
    264       1.1  tshiozak 	if (all_locales) {
    265       1.1  tshiozak 		list_locales();
    266       1.1  tshiozak 		exit(0);
    267       1.1  tshiozak 	}
    268       1.1  tshiozak 
    269       1.1  tshiozak 	/* process '-m' */
    270       1.1  tshiozak 	if (all_charmaps) {
    271       1.1  tshiozak 		list_charmaps();
    272       1.1  tshiozak 		exit(0);
    273       1.1  tshiozak 	}
    274       1.1  tshiozak 
    275       1.1  tshiozak 	/* check for special case '-k list' */
    276       1.1  tshiozak 	tmp = 0;
    277       1.1  tshiozak 	if (prt_keywords && argc > 0)
    278       1.1  tshiozak 		while (tmp < argc)
    279       1.1  tshiozak 			if (strcasecmp(argv[tmp++], "list") == 0) {
    280       1.1  tshiozak 				showkeywordslist();
    281       1.1  tshiozak 				exit(0);
    282       1.1  tshiozak 			}
    283       1.1  tshiozak 
    284       1.1  tshiozak 	/* process '-c' and/or '-k' */
    285       1.1  tshiozak 	if (prt_categories || prt_keywords || argc > 0) {
    286       1.1  tshiozak 		setlocale(LC_ALL, "");
    287       1.1  tshiozak 		while (argc > 0) {
    288       1.1  tshiozak 			showdetails(*argv);
    289       1.1  tshiozak 			argv++;
    290       1.1  tshiozak 			argc--;
    291       1.1  tshiozak 		}
    292       1.1  tshiozak 		exit(0);
    293       1.1  tshiozak 	}
    294       1.1  tshiozak 
    295       1.1  tshiozak 	/* no arguments, show current locale state */
    296       1.1  tshiozak 	showlocale();
    297       1.1  tshiozak 
    298       1.1  tshiozak 	return (0);
    299       1.1  tshiozak }
    300       1.1  tshiozak 
    301       1.1  tshiozak void
    302       1.1  tshiozak usage(void)
    303       1.1  tshiozak {
    304       1.3      jmmv 	printf("usage: locale [ -a | -m ]\n"
    305       1.1  tshiozak                "       locale [ -ck ] name ...\n");
    306       1.1  tshiozak 	exit(1);
    307       1.1  tshiozak }
    308       1.1  tshiozak 
    309       1.1  tshiozak /*
    310       1.1  tshiozak  * Output information about all available locales
    311       1.1  tshiozak  *
    312       1.1  tshiozak  * XXX actually output of this function does not guarantee that locale
    313       1.1  tshiozak  *     is really available to application, since it can be broken or
    314       1.1  tshiozak  *     inconsistent thus setlocale() will fail.  Maybe add '-V' function to
    315       1.1  tshiozak  *     also validate these locales?
    316       1.1  tshiozak  */
    317       1.1  tshiozak void
    318       1.1  tshiozak list_locales(void)
    319       1.1  tshiozak {
    320       1.1  tshiozak 	size_t i;
    321       1.1  tshiozak 
    322       1.1  tshiozak 	init_locales_list();
    323       1.1  tshiozak 	for (i = 0; i < locales->sl_cur; i++) {
    324       1.1  tshiozak 		printf("%s\n", locales->sl_str[i]);
    325       1.1  tshiozak 	}
    326       1.1  tshiozak }
    327       1.1  tshiozak 
    328       1.1  tshiozak /*
    329       1.1  tshiozak  * qsort() helper function
    330       1.1  tshiozak  */
    331       1.1  tshiozak static int
    332       1.1  tshiozak scmp(const void *s1, const void *s2)
    333       1.1  tshiozak {
    334       1.1  tshiozak 	return strcmp(*(const char **)s1, *(const char **)s2);
    335       1.1  tshiozak }
    336       1.1  tshiozak 
    337       1.1  tshiozak /*
    338       1.1  tshiozak  * Output information about all available charmaps
    339       1.1  tshiozak  *
    340       1.1  tshiozak  * XXX this function is doing a task in hackish way, i.e. by scaning
    341       1.1  tshiozak  *     list of locales, spliting their codeset part and building list of
    342       1.1  tshiozak  *     them.
    343       1.1  tshiozak  */
    344       1.1  tshiozak void
    345       1.1  tshiozak list_charmaps(void)
    346       1.1  tshiozak {
    347       1.1  tshiozak 	size_t i;
    348       1.1  tshiozak 	char *s, *cs;
    349       1.1  tshiozak 	StringList *charmaps;
    350       1.1  tshiozak 
    351       1.1  tshiozak 	/* initialize StringList */
    352       1.1  tshiozak 	charmaps = sl_init();
    353       1.1  tshiozak 	if (charmaps == NULL)
    354       1.1  tshiozak 		err(1, "could not allocate memory");
    355       1.1  tshiozak 
    356       1.1  tshiozak 	/* fetch locales list */
    357       1.1  tshiozak 	init_locales_list();
    358       1.1  tshiozak 
    359       1.1  tshiozak 	/* split codesets and build their list */
    360       1.1  tshiozak 	for (i = 0; i < locales->sl_cur; i++) {
    361       1.1  tshiozak 		s = locales->sl_str[i];
    362       1.1  tshiozak 		if ((cs = strchr(s, '.')) != NULL) {
    363       1.1  tshiozak 			cs++;
    364       1.1  tshiozak 			if (sl_find(charmaps, cs) == NULL)
    365       1.1  tshiozak 				sl_add(charmaps, cs);
    366       1.1  tshiozak 		}
    367       1.1  tshiozak 	}
    368       1.1  tshiozak 
    369       1.1  tshiozak 	/* add US-ASCII, if not yet added */
    370       1.1  tshiozak 	if (sl_find(charmaps, "US-ASCII") == NULL)
    371       1.1  tshiozak 		sl_add(charmaps, "US-ASCII");
    372       1.1  tshiozak 
    373       1.1  tshiozak 	/* sort the list */
    374       1.1  tshiozak 	qsort(charmaps->sl_str, charmaps->sl_cur, sizeof(char *), scmp);
    375       1.1  tshiozak 
    376       1.1  tshiozak 	/* print results */
    377       1.1  tshiozak 	for (i = 0; i < charmaps->sl_cur; i++) {
    378       1.1  tshiozak 		printf("%s\n", charmaps->sl_str[i]);
    379       1.1  tshiozak 	}
    380       1.1  tshiozak }
    381       1.1  tshiozak 
    382       1.1  tshiozak /*
    383       1.1  tshiozak  * Retrieve sorted list of system locales (or user locales, if PATH_LOCALE
    384       1.1  tshiozak  * environment variable is set)
    385       1.1  tshiozak  */
    386       1.1  tshiozak void
    387       1.1  tshiozak init_locales_list(void)
    388       1.1  tshiozak {
    389       1.1  tshiozak 	DIR *dirp;
    390       1.1  tshiozak 	struct dirent *dp;
    391       1.5   tnozaki 	char *s;
    392       1.1  tshiozak 
    393       1.1  tshiozak 	/* why call this function twice ? */
    394       1.1  tshiozak 	if (locales != NULL)
    395       1.1  tshiozak 		return;
    396       1.1  tshiozak 
    397       1.1  tshiozak 	/* initialize StringList */
    398       1.1  tshiozak 	locales = sl_init();
    399       1.1  tshiozak 	if (locales == NULL)
    400       1.1  tshiozak 		err(1, "could not allocate memory");
    401       1.1  tshiozak 
    402       1.1  tshiozak 	/* get actual locales directory name */
    403       1.1  tshiozak 	setlocale(LC_CTYPE, "C");
    404       1.1  tshiozak 	if (_PathLocale == NULL)
    405       1.1  tshiozak 		errx(1, "unable to find locales storage");
    406       1.1  tshiozak 
    407       1.1  tshiozak 	/* open locales directory */
    408       1.1  tshiozak 	dirp = opendir(_PathLocale);
    409       1.1  tshiozak 	if (dirp == NULL)
    410       1.1  tshiozak 		err(1, "could not open directory '%s'", _PathLocale);
    411       1.1  tshiozak 
    412       1.1  tshiozak 	/* scan directory and store its contents except "." and ".." */
    413       1.1  tshiozak 	while ((dp = readdir(dirp)) != NULL) {
    414       1.5   tnozaki 		/* exclude "." and "..", _LOCALE_ALIAS_NAME */
    415       1.5   tnozaki 		if ((dp->d_name[0] != '.' || (dp->d_name[1] != '\0' &&
    416       1.5   tnozaki 		    (dp->d_name[1] != '.' ||  dp->d_name[2] != '\0'))) &&
    417       1.5   tnozaki 		    strcmp(_LOCALE_ALIAS_NAME, dp->d_name) != 0) {
    418       1.5   tnozaki 			s = strdup(dp->d_name);
    419       1.5   tnozaki 			if (s == NULL)
    420       1.5   tnozaki 				err(1, "could not allocate memory");
    421       1.5   tnozaki 			sl_add(locales, s);
    422       1.5   tnozaki 		}
    423       1.1  tshiozak 	}
    424       1.1  tshiozak 	closedir(dirp);
    425       1.1  tshiozak 
    426       1.1  tshiozak         /* make sure that 'POSIX' and 'C' locales are present in the list.
    427       1.1  tshiozak 	 * POSIX 1003.1-2001 requires presence of 'POSIX' name only here, but
    428       1.1  tshiozak          * we also list 'C' for constistency
    429       1.1  tshiozak          */
    430       1.1  tshiozak 	if (sl_find(locales, "POSIX") == NULL)
    431       1.1  tshiozak 		sl_add(locales, "POSIX");
    432       1.1  tshiozak 
    433       1.1  tshiozak 	if (sl_find(locales, "C") == NULL)
    434       1.1  tshiozak 		sl_add(locales, "C");
    435       1.1  tshiozak 
    436       1.5   tnozaki 	init_locales_list_alias();
    437       1.5   tnozaki 
    438       1.1  tshiozak 	/* make output nicer, sort the list */
    439       1.1  tshiozak 	qsort(locales->sl_str, locales->sl_cur, sizeof(char *), scmp);
    440       1.1  tshiozak }
    441       1.1  tshiozak 
    442       1.5   tnozaki void
    443       1.5   tnozaki init_locales_list_alias(void)
    444       1.5   tnozaki {
    445       1.5   tnozaki 	char aliaspath[PATH_MAX];
    446       1.5   tnozaki #ifdef CITRUS
    447       1.5   tnozaki 	struct _lookup *hlookup;
    448       1.5   tnozaki 	struct _region key, dat;
    449       1.5   tnozaki #else
    450       1.5   tnozaki 	FILE *fp;
    451       1.5   tnozaki #endif
    452       1.5   tnozaki 	size_t n;
    453       1.5   tnozaki 	char *s, *t;
    454       1.5   tnozaki 
    455       1.5   tnozaki 	_DIAGASSERT(locales != NULL);
    456       1.5   tnozaki 	_DIAGASSERT(_PathLocale != NULL);
    457       1.5   tnozaki 
    458       1.5   tnozaki 	(void)snprintf(aliaspath, sizeof(aliaspath),
    459       1.5   tnozaki 		"%s/" _LOCALE_ALIAS_NAME, _PathLocale);
    460       1.5   tnozaki 
    461       1.5   tnozaki #ifdef CITRUS
    462       1.5   tnozaki 	if (_lookup_seq_open(&hlookup, aliaspath,
    463       1.5   tnozaki 	    _LOOKUP_CASE_SENSITIVE) == 0) {
    464       1.5   tnozaki 		while (_lookup_seq_next(hlookup, &key, &dat) == 0) {
    465       1.5   tnozaki 			n = _region_size((const struct _region *)&key);
    466       1.5   tnozaki 			s = _region_head((const struct _region *)&key);
    467       1.5   tnozaki 			for (t = s; n > 0 && *s!= '/'; --n, ++s);
    468       1.5   tnozaki #else
    469       1.5   tnozaki 	fp = fopen(aliaspath, "r");
    470       1.5   tnozaki 	if (fp != NULL) {
    471       1.5   tnozaki 		while ((s = fgetln(fp, &n)) != NULL) {
    472       1.5   tnozaki 			_DIAGASSERT(n > 0);
    473       1.5   tnozaki 			if (*s == '#' || *s == '\n')
    474       1.5   tnozaki 				continue;
    475       1.5   tnozaki 			for (t = s; n > 0 && strchr("/ \t\n", *s) == NULL;
    476       1.5   tnozaki 			    --n, ++s);
    477       1.5   tnozaki #endif
    478       1.5   tnozaki 			n = (size_t)(s - t);
    479  1.5.28.2       snj 			s = malloc(n + 1);
    480       1.5   tnozaki 			if (s == NULL)
    481       1.5   tnozaki 				err(1, "could not allocate memory");
    482       1.5   tnozaki 			memcpy(s, t, n);
    483       1.5   tnozaki 			s[n] = '\0';
    484       1.5   tnozaki 			if (sl_find(locales, s) == NULL)
    485       1.5   tnozaki 				sl_add(locales, s);
    486       1.5   tnozaki 			else
    487       1.5   tnozaki 				free(s);
    488       1.5   tnozaki 		}
    489       1.5   tnozaki #ifdef CITRUS
    490       1.5   tnozaki 		_lookup_seq_close(hlookup);
    491       1.5   tnozaki #else
    492       1.5   tnozaki 		fclose(fp);
    493       1.5   tnozaki #endif
    494       1.5   tnozaki 	}
    495       1.5   tnozaki }
    496       1.5   tnozaki 
    497       1.1  tshiozak /*
    498       1.1  tshiozak  * Show current locale status, depending on environment variables
    499       1.1  tshiozak  */
    500       1.1  tshiozak void
    501       1.1  tshiozak showlocale(void)
    502       1.1  tshiozak {
    503       1.1  tshiozak 	size_t	i;
    504       1.1  tshiozak 	const char *lang, *vval, *eval;
    505       1.1  tshiozak 
    506       1.1  tshiozak 	setlocale(LC_ALL, "");
    507       1.1  tshiozak 
    508       1.1  tshiozak 	lang = getenv("LANG");
    509       1.1  tshiozak 	if (lang == NULL) {
    510       1.1  tshiozak 		lang = "";
    511       1.1  tshiozak 	}
    512       1.4  jdolecek 	printf("LANG=\"%s\"\n", lang);
    513       1.1  tshiozak 	/* XXX: if LANG is null, then set it to "C" to get implied values? */
    514       1.1  tshiozak 
    515       1.1  tshiozak 	for (i = 0; i < NLCINFO; i++) {
    516       1.1  tshiozak 		vval = setlocale(lcinfo[i].id, NULL);
    517       1.1  tshiozak 		eval = getenv(lcinfo[i].name);
    518       1.1  tshiozak 		if (eval != NULL && !strcmp(eval, vval)
    519       1.1  tshiozak 				&& strcmp(lang, vval)) {
    520       1.1  tshiozak 			/*
    521       1.1  tshiozak 			 * Appropriate environment variable set, its value
    522       1.1  tshiozak 			 * is valid and not overriden by LC_ALL
    523       1.1  tshiozak 			 *
    524       1.1  tshiozak 			 * XXX: possible side effect: if both LANG and
    525       1.1  tshiozak 			 * overriden environment variable are set into same
    526       1.1  tshiozak 			 * value, then it'll be assumed as 'implied'
    527       1.1  tshiozak 			 */
    528       1.4  jdolecek 			printf("%s=\"%s\"\n", lcinfo[i].name, vval);
    529       1.1  tshiozak 		} else {
    530       1.1  tshiozak 			printf("%s=\"%s\"\n", lcinfo[i].name, vval);
    531       1.1  tshiozak 		}
    532       1.1  tshiozak 	}
    533       1.1  tshiozak 
    534       1.1  tshiozak 	vval = getenv("LC_ALL");
    535       1.1  tshiozak 	if (vval == NULL) {
    536       1.1  tshiozak 		vval = "";
    537       1.1  tshiozak 	}
    538       1.4  jdolecek 	printf("LC_ALL=\"%s\"\n", vval);
    539       1.1  tshiozak }
    540       1.1  tshiozak 
    541       1.1  tshiozak /*
    542       1.1  tshiozak  * keyword value lookup helper (via localeconv())
    543       1.1  tshiozak  */
    544       1.1  tshiozak char *
    545       1.1  tshiozak kwval_lconv(int id)
    546       1.1  tshiozak {
    547       1.1  tshiozak 	struct lconv *lc;
    548       1.1  tshiozak 	char *rval;
    549       1.1  tshiozak 
    550       1.1  tshiozak 	rval = NULL;
    551       1.1  tshiozak 	lc = localeconv();
    552       1.1  tshiozak 	switch (id) {
    553       1.1  tshiozak 		case KW_GROUPING:
    554       1.1  tshiozak 			rval = lc->grouping;
    555       1.1  tshiozak 			break;
    556       1.1  tshiozak 		case KW_INT_CURR_SYMBOL:
    557       1.1  tshiozak 			rval = lc->int_curr_symbol;
    558       1.1  tshiozak 			break;
    559       1.1  tshiozak 		case KW_CURRENCY_SYMBOL:
    560       1.1  tshiozak 			rval = lc->currency_symbol;
    561       1.1  tshiozak 			break;
    562       1.1  tshiozak 		case KW_MON_DECIMAL_POINT:
    563       1.1  tshiozak 			rval = lc->mon_decimal_point;
    564       1.1  tshiozak 			break;
    565       1.1  tshiozak 		case KW_MON_THOUSANDS_SEP:
    566       1.1  tshiozak 			rval = lc->mon_thousands_sep;
    567       1.1  tshiozak 			break;
    568       1.1  tshiozak 		case KW_MON_GROUPING:
    569       1.1  tshiozak 			rval = lc->mon_grouping;
    570       1.1  tshiozak 			break;
    571       1.1  tshiozak 		case KW_POSITIVE_SIGN:
    572       1.1  tshiozak 			rval = lc->positive_sign;
    573       1.1  tshiozak 			break;
    574       1.1  tshiozak 		case KW_NEGATIVE_SIGN:
    575       1.1  tshiozak 			rval = lc->negative_sign;
    576       1.1  tshiozak 			break;
    577       1.1  tshiozak 		case KW_INT_FRAC_DIGITS:
    578       1.1  tshiozak 			rval = &(lc->int_frac_digits);
    579       1.1  tshiozak 			break;
    580       1.1  tshiozak 		case KW_FRAC_DIGITS:
    581       1.1  tshiozak 			rval = &(lc->frac_digits);
    582       1.1  tshiozak 			break;
    583       1.1  tshiozak 		case KW_P_CS_PRECEDES:
    584       1.1  tshiozak 			rval = &(lc->p_cs_precedes);
    585       1.1  tshiozak 			break;
    586       1.1  tshiozak 		case KW_P_SEP_BY_SPACE:
    587       1.1  tshiozak 			rval = &(lc->p_sep_by_space);
    588       1.1  tshiozak 			break;
    589       1.1  tshiozak 		case KW_N_CS_PRECEDES:
    590       1.1  tshiozak 			rval = &(lc->n_cs_precedes);
    591       1.1  tshiozak 			break;
    592       1.1  tshiozak 		case KW_N_SEP_BY_SPACE:
    593       1.1  tshiozak 			rval = &(lc->n_sep_by_space);
    594       1.1  tshiozak 			break;
    595       1.1  tshiozak 		case KW_P_SIGN_POSN:
    596       1.1  tshiozak 			rval = &(lc->p_sign_posn);
    597       1.1  tshiozak 			break;
    598       1.1  tshiozak 		case KW_N_SIGN_POSN:
    599       1.1  tshiozak 			rval = &(lc->n_sign_posn);
    600       1.1  tshiozak 			break;
    601       1.1  tshiozak 		case KW_INT_P_CS_PRECEDES:
    602       1.1  tshiozak 			rval = &(lc->int_p_cs_precedes);
    603       1.1  tshiozak 			break;
    604       1.1  tshiozak 		case KW_INT_P_SEP_BY_SPACE:
    605       1.1  tshiozak 			rval = &(lc->int_p_sep_by_space);
    606       1.1  tshiozak 			break;
    607       1.1  tshiozak 		case KW_INT_N_CS_PRECEDES:
    608       1.1  tshiozak 			rval = &(lc->int_n_cs_precedes);
    609       1.1  tshiozak 			break;
    610       1.1  tshiozak 		case KW_INT_N_SEP_BY_SPACE:
    611       1.1  tshiozak 			rval = &(lc->int_n_sep_by_space);
    612       1.1  tshiozak 			break;
    613       1.1  tshiozak 		case KW_INT_P_SIGN_POSN:
    614       1.1  tshiozak 			rval = &(lc->int_p_sign_posn);
    615       1.1  tshiozak 			break;
    616       1.1  tshiozak 		case KW_INT_N_SIGN_POSN:
    617       1.1  tshiozak 			rval = &(lc->int_n_sign_posn);
    618       1.1  tshiozak 			break;
    619       1.1  tshiozak 		default:
    620       1.1  tshiozak 			break;
    621       1.1  tshiozak 	}
    622       1.1  tshiozak 	return (rval);
    623       1.1  tshiozak }
    624       1.1  tshiozak 
    625       1.1  tshiozak /*
    626       1.1  tshiozak  * keyword value and properties lookup
    627       1.1  tshiozak  */
    628       1.1  tshiozak int
    629       1.1  tshiozak kwval_lookup(char *kwname, char **kwval, int *cat, int *isstr)
    630       1.1  tshiozak {
    631       1.1  tshiozak 	int	rval;
    632       1.1  tshiozak 	size_t	i;
    633       1.1  tshiozak 
    634       1.1  tshiozak 	rval = 0;
    635       1.1  tshiozak 	for (i = 0; i < NKWINFO; i++) {
    636       1.1  tshiozak 		if (strcasecmp(kwname, kwinfo[i].name) == 0) {
    637       1.1  tshiozak 			rval = 1;
    638       1.1  tshiozak 			*cat = kwinfo[i].catid;
    639       1.1  tshiozak 			*isstr = kwinfo[i].isstr;
    640       1.1  tshiozak 			if (kwinfo[i].value_ref < KW_ZERO) {
    641       1.1  tshiozak 				*kwval = nl_langinfo(kwinfo[i].value_ref);
    642       1.1  tshiozak 			} else {
    643       1.1  tshiozak 				*kwval = kwval_lconv(kwinfo[i].value_ref);
    644       1.1  tshiozak 			}
    645       1.1  tshiozak 			break;
    646       1.1  tshiozak 		}
    647       1.1  tshiozak 	}
    648       1.1  tshiozak 
    649       1.1  tshiozak 	return (rval);
    650       1.1  tshiozak }
    651       1.1  tshiozak 
    652       1.1  tshiozak /*
    653       1.1  tshiozak  * Show details about requested keyword according to '-k' and/or '-c'
    654       1.1  tshiozak  * command line options specified.
    655       1.1  tshiozak  */
    656       1.1  tshiozak void
    657       1.1  tshiozak showdetails(char *kw)
    658       1.1  tshiozak {
    659       1.1  tshiozak 	int	isstr, cat, tmpval;
    660       1.1  tshiozak 	char	*kwval;
    661       1.1  tshiozak 
    662       1.1  tshiozak 	if (kwval_lookup(kw, &kwval, &cat, &isstr) == 0) {
    663       1.1  tshiozak 		/*
    664       1.1  tshiozak 		 * invalid keyword specified.
    665       1.1  tshiozak 		 * XXX: any actions?
    666       1.1  tshiozak 		 */
    667       1.1  tshiozak 		return;
    668       1.1  tshiozak 	}
    669       1.1  tshiozak 
    670       1.1  tshiozak 	if (prt_categories) {
    671       1.1  tshiozak 		printf("%s\n", lookup_localecat(cat));
    672       1.1  tshiozak 	}
    673       1.1  tshiozak 
    674       1.1  tshiozak 	if (prt_keywords) {
    675       1.1  tshiozak 		if (isstr) {
    676       1.1  tshiozak 			printf("%s=\"%s\"\n", kw, kwval);
    677       1.1  tshiozak 		} else {
    678       1.1  tshiozak 			tmpval = (char) *kwval;
    679       1.1  tshiozak 			printf("%s=%d\n", kw, tmpval);
    680       1.1  tshiozak 		}
    681       1.1  tshiozak 	}
    682       1.1  tshiozak 
    683       1.1  tshiozak 	if (!prt_categories && !prt_keywords) {
    684       1.1  tshiozak 		if (isstr) {
    685       1.1  tshiozak 			printf("%s\n", kwval);
    686       1.1  tshiozak 		} else {
    687       1.1  tshiozak 			tmpval = (char) *kwval;
    688       1.1  tshiozak 			printf("%d\n", tmpval);
    689       1.1  tshiozak 		}
    690       1.1  tshiozak 	}
    691       1.1  tshiozak }
    692       1.1  tshiozak 
    693       1.1  tshiozak /*
    694       1.1  tshiozak  * Convert locale category id into string
    695       1.1  tshiozak  */
    696       1.1  tshiozak const char *
    697       1.1  tshiozak lookup_localecat(int cat)
    698       1.1  tshiozak {
    699       1.1  tshiozak 	size_t	i;
    700       1.1  tshiozak 
    701       1.1  tshiozak 	for (i = 0; i < NLCINFO; i++)
    702       1.1  tshiozak 		if (lcinfo[i].id == cat) {
    703       1.1  tshiozak 			return (lcinfo[i].name);
    704       1.1  tshiozak 		}
    705       1.1  tshiozak 	return ("UNKNOWN");
    706       1.1  tshiozak }
    707       1.1  tshiozak 
    708       1.1  tshiozak /*
    709       1.1  tshiozak  * Show list of keywords
    710       1.1  tshiozak  */
    711       1.1  tshiozak void
    712       1.1  tshiozak showkeywordslist(void)
    713       1.1  tshiozak {
    714       1.1  tshiozak 	size_t	i;
    715       1.1  tshiozak 
    716       1.1  tshiozak #define FMT "%-20s %-12s %-7s %-20s\n"
    717       1.1  tshiozak 
    718       1.1  tshiozak 	printf("List of available keywords\n\n");
    719       1.1  tshiozak 	printf(FMT, "Keyword", "Category", "Type", "Comment");
    720       1.1  tshiozak 	printf("-------------------- ------------ ------- --------------------\n");
    721       1.1  tshiozak 	for (i = 0; i < NKWINFO; i++) {
    722       1.1  tshiozak 		printf(FMT,
    723       1.1  tshiozak 			kwinfo[i].name,
    724       1.1  tshiozak 			lookup_localecat(kwinfo[i].catid),
    725       1.1  tshiozak 			(kwinfo[i].isstr == 0) ? "number" : "string",
    726       1.1  tshiozak 			kwinfo[i].comment);
    727       1.1  tshiozak 	}
    728       1.1  tshiozak }
    729