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