4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 1995 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984 AT&T */
28 /* All Rights Reserved */
31 #pragma ident "%Z%%M% %I% %E% SMI"
33 #include <sys/fcntl.h>
41 #include <sys/param.h> /* for MAXPATHLEN */
49 struct _code_set_info _code_set_info
= {
51 CODESET_NONE
, /* no codeset */
52 NULL
, /* not defined */
56 /* tolower() and toupper() conversion table
57 * is hidden here to avoid being placed in the
58 * extern .sa file in the dynamic version of libc
61 char _ctype_ul
[] = { 0,
64 '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
65 '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
66 '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
67 '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
68 ' ', '!', '"', '#', '$', '%', '&', '\'',
69 '(', ')', '*', '+', ',', '-', '.', '/',
70 '0', '1', '2', '3', '4', '5', '6', '7',
71 '8', '9', ':', ';', '<', '=', '>', '?',
72 '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
73 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
74 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
75 'x', 'y', 'z', '[', '\\', ']', '^', '_',
76 '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
77 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
78 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
79 'X', 'Y', 'Z', '{', '|', '}', '~', '\177',
80 0, 0, 0, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 0,
87 0, 0, 0, 0, 0, 0, 0, 0,
88 0, 0, 0, 0, 0, 0, 0, 0,
89 0, 0, 0, 0, 0, 0, 0, 0,
90 0, 0, 0, 0, 0, 0, 0, 0,
91 0, 0, 0, 0, 0, 0, 0, 0,
92 0, 0, 0, 0, 0, 0, 0, 0,
93 0, 0, 0, 0, 0, 0, 0, 0,
94 0, 0, 0, 0, 0, 0, 0, 0,
95 0, 0, 0, 0, 0, 0, 0, 0,
98 /* following layout is:
99 * LC_NUMERIC LC_TIME LC_MONETARY LANGINFO LC_COLLATE LC_MESSAGES
101 char _locales
[MAXLOCALE
- 1][MAXLOCALENAME
+ 1] ;
103 char _my_time
[MAXLOCALENAME
+ 1];
105 /* The array Default holds the systems notion of default locale. It is normally
106 * found in {LOCALE}/.default and moved to here. Note there is only one
107 * default locale spanning all categories
110 static char Default
[MAXLOCALENAME
+1];
112 struct langinfo _langinfo
;
113 struct dtconv
*_dtconv
= NULL
;
115 static char *realmonths
= NULL
;
116 static char *realdays
= NULL
;
117 static char *realfmts
= NULL
;
118 static short lang_succ
= ON
; /* setlocale success */
121 /* Set the values here to guarantee stdio use of the
124 static struct lconv lconv_arr
= {
127 CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
,
128 CHAR_MAX
, CHAR_MAX
, CHAR_MAX
, CHAR_MAX
131 /* lconv is externally defined by ANSI C */
132 struct lconv
*lconv
= &lconv_arr
;
134 static char *lconv_numeric_str
= NULL
;
135 static char *lconv_monetary_str
= NULL
;
137 int openlocale(char *, int, char *, char *);
138 int getlocale_ctype(char *, char *, char *);
139 char *getlocale_numeric(char *, struct lconv
*, char *);
140 void init_statics(void);
141 static char *getlocale_monetary(char *, struct lconv
*, char *);
142 static char *getstr(char *, char **);
143 static char *getgrouping(char *, char **);
144 static char *getnum(char *, char *);
145 static char *getbool(char *, char *);
146 static void set_default(void);
149 setlocale(int category
, char *locale
)
151 static char buf
[MAXLOCALE
*(MAXLOCALENAME
+ 1) + 1];
152 /* buffer for current LC_ALL value */
155 char my_ctype
[CTYPE_SIZE
]; /* local copy */
156 struct lconv my_lconv
; /* local copy */
157 char *my_lconv_numeric_str
;
158 char *my_lconv_monetary_str
;
163 /* initialize my_lconv to lconv */
164 memcpy(&my_lconv
, lconv
, sizeof(my_lconv
));
167 * Following code is to avoid static initialisation of
168 * strings which would otherwise blow up "xstr".
170 if (_locales
[0][0] == '\0')
173 if (locale
== NULL
) {
174 if (category
== LC_ALL
) {
176 * Assume all locales are set to the same value. Then
177 * scan through the locales to see if any are
178 * different. If they are the same, return the common
179 * value; otherwise, construct a "composite" value.
181 nonuniform
= 0; /* assume all locales set the same */
182 for (i
= 0; i
< MAXLOCALE
- 2; i
++) {
183 if (strcmp(_locales
[i
], _locales
[i
+ 1]) != 0) {
190 * They're not all the same. Construct a list
191 * of all the locale values, in order,
192 * separated by slashes. Return that value.
194 (void) strcpy(buf
, _locales
[0]);
195 for (i
= 1; i
< MAXLOCALE
- 1; i
++) {
196 (void) strcat(buf
, "/");
197 (void) strcat(buf
, _locales
[i
]);
202 * They're all the same; any one you return is
205 return (_locales
[0]);
208 return (_locales
[category
- 1]);
214 if (strchr(locale
, '/') != NULL
) {
216 * Composite value; extract each category.
218 if (strlen(locale
) > sizeof buf
- 1)
219 return (NULL
); /* too long */
220 (void) strcpy(buf
, locale
);
224 * LC_CTYPE and LC_NUMERIC are set here.
225 * Others locales won't be set here,
226 * they will be just marked.
228 for (i
= 0; i
< MAXLOCALE
- 1; i
++) {
231 return (NULL
); /* missing item */
235 if (setlocale(LC_CTYPE
,p
) == NULL
)
239 if (setlocale(LC_NUMERIC
,p
) == NULL
)
243 if (setlocale(LC_TIME
,p
) == NULL
)
246 case LC_MONETARY
- 1:
247 if (setlocale(LC_MONETARY
,p
) == NULL
)
251 if (setlocale(LANGINFO
,p
) == NULL
)
255 if (setlocale(LC_COLLATE
,p
) == NULL
)
258 case LC_MESSAGES
- 1:
259 if (setlocale(LC_MESSAGES
,p
) == NULL
)
265 if (strtok((char *)NULL
, "/") != NULL
)
266 return (NULL
); /* extra stuff at end */
269 /* If category = LC_ALL, Drop through to test each individual
270 * category, one at a time. Note default rules where env vars
275 if ((ret
= getlocale_ctype(locale
, my_ctype
,
276 _locales
[LC_CTYPE
- 1])) < 0)
279 (void) memcpy(_ctype_
, my_ctype
, CTYPE_SIZE
/2);
280 (void) memcpy(_ctype_ul
, my_ctype
+(CTYPE_SIZE
/2), CTYPE_SIZE
/2);
282 if (category
!= LC_ALL
)
286 if ((my_lconv_numeric_str
=
287 getlocale_numeric(locale
, &my_lconv
,
288 _locales
[LC_NUMERIC
- 1])) == NULL
)
290 if (*my_lconv_numeric_str
) {
291 if (lconv_numeric_str
!= NULL
)
292 free((malloc_t
)lconv_numeric_str
);
293 lconv_numeric_str
= my_lconv_numeric_str
;
294 memcpy(lconv
, my_lconv
, sizeof(my_lconv
));
296 if (category
!= LC_ALL
)
300 if ((ret
= openlocale("LC_TIME", LC_TIME
, locale
,
301 _locales
[LC_TIME
-1])) < 0)
305 if (category
!= LC_ALL
)
309 if ((my_lconv_monetary_str
=
310 getlocale_monetary(locale
, &my_lconv
,
311 _locales
[LC_MONETARY
- 1])) == NULL
)
313 if (*my_lconv_monetary_str
) {
314 if (lconv_monetary_str
!= NULL
)
315 free((malloc_t
)lconv_monetary_str
);
316 lconv_monetary_str
= my_lconv_monetary_str
;
317 memcpy(lconv
, &my_lconv
, sizeof(my_lconv
));
319 if (category
!= LC_ALL
)
323 if ((ret
= openlocale("LANGINFO", LANGINFO
, locale
,
324 _locales
[LANGINFO
- 1])) < 0) {
332 if (category
!= LC_ALL
)
336 if ((ret
= openlocale("LC_COLLATE", LC_COLLATE
, locale
,
337 _locales
[LC_COLLATE
- 1])) < 0)
342 if (category
!= LC_ALL
)
346 if ((ret
= openlocale("LC_MESSAGES", LC_MESSAGES
, locale
,
347 _locales
[LC_MESSAGES
- 1])) < 0)
353 return (setlocale(category
, (char *)NULL
));
357 getlocale_ctype(char *locale
, char *ctypep
, char *newlocale
)
361 if ((fd
= openlocale("LC_CTYPE", LC_CTYPE
, locale
, newlocale
)) > 0) {
362 if (read(fd
, (char *)ctypep
, CTYPE_SIZE
) != CTYPE_SIZE
) {
371 /* open and load the numeric information */
374 getlocale_numeric(char *locale
, struct lconv
*lconvp
, char *newlocale
)
381 if ((fd
= openlocale("LC_NUMERIC", LC_NUMERIC
, locale
, newlocale
)) < 0)
385 if ((fstat(fd
, &buf
)) != 0)
387 if ((str
= (char*)malloc((unsigned)buf
.st_size
+ 2)) == NULL
)
390 if ((read(fd
, str
, (int)buf
.st_size
)) != buf
.st_size
) {
395 /* Set last character of str to '\0' */
396 p
= &str
[buf
.st_size
];
400 /* p will "walk thru" str */
403 p
= getstr(p
, &lconvp
->decimal_point
);
406 p
= getstr(p
, &lconvp
->thousands_sep
);
409 p
= getgrouping(p
, &lconvp
->grouping
);
424 getlocale_monetary(char *locale
, struct lconv
*lconvp
, char *newlocale
)
431 if ((fd
= openlocale("LC_MONETARY", LC_MONETARY
, locale
, newlocale
)) < 0)
435 if ((fstat(fd
, &buf
)) != 0)
437 if ((str
= (char*)malloc((unsigned)buf
.st_size
+ 2)) == NULL
)
440 if ((read(fd
, str
, (int)buf
.st_size
)) != buf
.st_size
) {
445 /* Set last character of str to '\0' */
446 p
= &str
[buf
.st_size
];
450 /* p will "walk thru" str */
453 p
= getstr(p
, &lconvp
->int_curr_symbol
);
456 p
= getstr(p
, &lconvp
->currency_symbol
);
459 p
= getstr(p
, &lconvp
->mon_decimal_point
);
462 p
= getstr(p
, &lconvp
->mon_thousands_sep
);
465 p
= getgrouping(p
, &lconvp
->mon_grouping
);
468 p
= getstr(p
, &lconvp
->positive_sign
);
471 p
= getstr(p
, &lconvp
->negative_sign
);
474 p
= getnum(p
, &lconvp
->frac_digits
);
477 p
= getbool(p
, &lconvp
->p_cs_precedes
);
480 p
= getbool(p
, &lconvp
->p_sep_by_space
);
483 p
= getbool(p
, &lconvp
->n_cs_precedes
);
486 p
= getbool(p
, &lconvp
->n_sep_by_space
);
489 p
= getnum(p
, &lconvp
->p_sign_posn
);
492 p
= getnum(p
, &lconvp
->n_sign_posn
);
506 getstr(char *p
, char **strp
)
511 return (NULL
); /* no end-of-line */
517 getgrouping(char *p
, char **groupingp
)
522 return (NULL
); /* no grouping */
524 while ((c
= *p
) != '\n') {
526 return (NULL
); /* no end-of-line */
527 if (c
>= '0' && c
<= '9')
537 getnum(char *p
, char *nump
)
543 return (NULL
); /* no number */
545 *nump
= '\177'; /* blank line - no value */
548 while ((c
= *p
) != '\n') {
549 if (c
< '0' || c
> '9')
550 return (NULL
); /* bad number */
551 num
= num
*10 + c
- '0';
561 getbool(char *p
, char *boolp
)
565 return (NULL
); /* no number */
567 *boolp
= '\177'; /* blank line - no value */
575 *boolp
= 1; /* true */
582 *boolp
= 0; /* false */
586 return (NULL
); /* bad boolean */
589 return (NULL
); /* noise at end of line */
596 * Open a locale file. First, check the value of "locale"; if it's a null
597 * string, first check the environment variable with the same name as the
598 * category, and then check the environment variable "LANG". If neither of
599 * them are set to non-null strings, use the LC_default env.var and if this
600 * has no meaning then assume we are running in the C locale. It is expected
601 * That LC_default is set across the whole system. If the resulting locale is
602 * longer than MAXLOCALENAME characters, reject it. Then, try looking in the
603 * per-machine locale directory for the file in question; if it's not found
604 * there, try looking in the shared locale directory.
605 * If there is no work to do, that is, the last setting of locales is equal
606 * to the current request, then we don't do anything, and exit with value 0.
607 * Copy the name of the locale used into "newlocale".
608 * Exit with positive value if we opened a file
609 * Exit with -1 if an error occured (invalid locale).
610 * Exit with 0 if there is no need to look at the disk file.
611 * (Assumption - there is always at least one fd open before setlocale
615 openlocale(char *category
, int cat_id
, char *locale
, char *newlocale
)
617 char pathname
[MAXPATHLEN
], *defp
;
619 struct _code_header code_header
;
622 if (*locale
== '\0') {
623 locale
= getenv(category
);
624 if (locale
== NULL
|| *locale
== '\0') {
625 locale
= getenv("LANG");
626 if (locale
== NULL
|| *locale
== '\0') {
627 if (*Default
== '\0') {
628 defp
= getenv("LC_default");
629 if (defp
== NULL
|| *defp
== '\0')
632 strcpy(Default
, defp
);
638 if (strcmp(locale
,_locales
[cat_id
-1]) == 0) {
639 (void) strcpy(newlocale
, locale
);
642 if (strlen(locale
) > MAXLOCALENAME
)
645 (void) strcpy(pathname
, PRIVATE_LOCALE_DIR
);
646 (void) strcat(pathname
, category
);
647 (void) strcat(pathname
, "/");
648 (void) strcat(pathname
, locale
);
649 if ((fd
= open(pathname
, O_RDONLY
)) < 0 && errno
== ENOENT
) {
650 (void) strcpy(pathname
, LOCALE_DIR
);
651 (void) strcat(pathname
, category
);
652 (void) strcat(pathname
, "/");
653 (void) strcat(pathname
, locale
);
654 fd
= open(pathname
, O_RDONLY
);
657 (void) strcpy(newlocale
, locale
);
659 * bug id 1072740; if by some chance the actual fd we're going to
660 * return is 0, change it to be some non-zero descriptor, because
661 * returning 0 means something different. If '0' is the only
662 * descriptor left, return an error.
667 if ((dupfd
= dup(fd
)) < 1) {
676 if (cat_id
== LC_CTYPE
) {
678 /* Go and get the trailer file */
680 (void) strcat(pathname
, TRAILER
);
681 fd2
= open(pathname
, O_RDONLY
);
693 * ctype trailer file exists - read it
696 if (read (fd2
, (char *)&code_header
, sizeof (code_header
)) !=
697 sizeof (code_header
)) {
699 * File format not correct
706 * set up trailer file
708 strcpy(_code_set_info
.code_name
, code_header
.code_name
);
709 _code_set_info
.code_id
= code_header
.code_id
;
710 if (_code_set_info
.code_info
!= NULL
)
711 free (_code_set_info
.code_info
);
712 if (code_header
.code_info_size
> 0) {
713 my_info
= malloc(code_header
.code_info_size
);
714 if (read (fd2
, (char *)my_info
,
715 code_header
.code_info_size
) !=
716 code_header
.code_info_size
) {
721 _code_set_info
.code_info
= my_info
;
725 * We have a corrupted file too
727 _code_set_info
.code_info
= NULL
;
749 char *rawmonths
= "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember";
751 char *rawdays
= "Sun\nMon\nTue\nWed\nThu\nFri\nSat\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday";
753 char *rawfmts
= "%H:%M:%S\n%m/%d/%y\n%a %b %e %T %Z %Y\nAM\nPM\n%A, %B %e, %Y\n";
755 /* fix for bugid 1067574 ... robinson */
756 (void)getlocale_time();
758 if (_dtconv
== NULL
) {
760 /* We malloc both the space for the dtconv struct and the
761 * copy of the strings above because this program is later run
762 * through xstr and the resultant strings are put in read-only
763 * text segment. Therefore we cannot write to the original
764 * raw strings but we can to their copies.
767 _dtconv
= (struct dtconv
*)malloc(sizeof (struct dtconv
));
770 if ((realmonths
= malloc(strlen(rawmonths
)+1)) == NULL
)
772 strcpy(realmonths
, rawmonths
);
773 if ((realdays
= malloc(strlen(rawdays
)+1)) == NULL
)
775 strcpy(realdays
, rawdays
);
776 if ((realfmts
= malloc(strlen(rawfmts
)+1)) == NULL
)
778 strcpy(realfmts
, rawfmts
);
780 /* p will "walk thru" str */
784 for (i
= 0; i
< 12; i
++)
785 p
= getstr(p
, &(_dtconv
->abbrev_month_names
[i
]));
787 for (i
= 0; i
< 12; i
++)
788 p
= getstr(p
, &(_dtconv
->month_names
[i
]));
790 for (i
= 0; i
< 7; i
++)
791 p
= getstr(p
, &(_dtconv
->abbrev_weekday_names
[i
]));
792 for (i
= 0; i
< 7; i
++)
793 p
= getstr(p
, &(_dtconv
->weekday_names
[i
]));
795 p
= getstr(p
, &_dtconv
->time_format
);
796 p
= getstr(p
, &_dtconv
->sdate_format
);
797 p
= getstr(p
, &_dtconv
->dtime_format
);
798 p
= getstr(p
, &_dtconv
->am_string
);
799 p
= getstr(p
, &_dtconv
->pm_string
);
800 p
= getstr(p
, &_dtconv
->ldate_format
);
811 strcpy(_code_set_info
.code_name
, Default
);
812 _code_set_info
.code_id
= CODESET_NONE
;
813 if (_code_set_info
.code_info
!= NULL
)
814 free (_code_set_info
.code_info
);
815 _code_set_info
.code_info
= NULL
;
816 _code_set_info
.open_flag
= 0;
825 for (i
=0; i
<MAXLOCALE
-1;i
++)
826 strcpy(_locales
[i
],"C");
827 strcpy(_code_set_info
.code_name
, "default");
828 strcpy(_my_time
,"C");
829 _langinfo
.yesstr
= "yes";
830 _langinfo
.nostr
= "no";