1 /* Implementation of the locale program according to POSIX 9945-2.
2 Copyright (C) 1995-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <https://www.gnu.org/licenses/>. */
35 #include <stdio_ext.h>
43 #include "record-status.h"
44 #include "localeinfo.h"
45 #include "charmap-dir.h"
46 #include "../locarchive.h"
47 #include <programs/xmalloc.h>
49 #define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
51 /* If set print the name of the category. */
52 static int show_category_name
;
54 /* If set print the name of the item. */
55 static int show_keyword_name
;
57 /* Print names of all available locales. */
60 /* Print names of all available character maps. */
61 static int do_charmaps
= 0;
63 /* Name and version of program. */
64 static void print_version (FILE *stream
, struct argp_state
*state
);
65 void (*argp_program_version_hook
) (FILE *, struct argp_state
*) = print_version
;
67 /* Definitions of arguments for argp functions. */
68 static const struct argp_option options
[] =
70 { NULL
, 0, NULL
, 0, N_("System information:") },
71 { "all-locales", 'a', NULL
, OPTION_NO_USAGE
,
72 N_("Write names of available locales") },
73 { "charmaps", 'm', NULL
, OPTION_NO_USAGE
,
74 N_("Write names of available charmaps") },
75 { NULL
, 0, NULL
, 0, N_("Modify output format:") },
76 { "category-name", 'c', NULL
, 0, N_("Write names of selected categories") },
77 { "keyword-name", 'k', NULL
, 0, N_("Write names of selected keywords") },
78 { "verbose", 'v', NULL
, 0, N_("Print more information") },
79 { NULL
, 0, NULL
, 0, NULL
}
82 /* Short description of program. */
83 static const char doc
[] = N_("Get locale-specific information.");
85 /* Strings for arguments in help texts. */
86 static const char args_doc
[] = N_("NAME\n[-a|-m]");
88 /* Prototype for option handler. */
89 static error_t
parse_opt (int key
, char *arg
, struct argp_state
*state
);
91 /* Function to print some extra text in the help message. */
92 static char *more_help (int key
, const char *text
, void *input
);
94 /* Data structure to communicate with argp functions. */
95 static struct argp argp
=
97 options
, parse_opt
, args_doc
, doc
, NULL
, more_help
101 /* We don't have these constants defined because we don't use them. Give
103 #define CTYPE_MB_CUR_MIN 0
104 #define CTYPE_MB_CUR_MAX 0
105 #define CTYPE_HASH_SIZE 0
106 #define CTYPE_HASH_LAYERS 0
107 #define CTYPE_CLASS 0
108 #define CTYPE_TOUPPER_EB 0
109 #define CTYPE_TOLOWER_EB 0
110 #define CTYPE_TOUPPER_EL 0
111 #define CTYPE_TOLOWER_EL 0
113 /* Definition of the data structure which represents a category and its
124 enum { std
, opt
} status
;
125 enum value_type value_type
;
131 /* Simple helper macro. */
132 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
134 /* For some tricky stuff. */
135 #define NO_PAREN(Item, More...) Item, ## More
137 /* We have all categories defined in `categories.def'. Now construct
138 the description and data structure used for all categories. */
139 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
140 #define DEFINE_CATEGORY(category, name, items, postload) \
141 static struct cat_item category##_desc[] = \
146 #include "categories.def"
147 #undef DEFINE_CATEGORY
149 static struct category category
[] =
151 #define DEFINE_CATEGORY(category, name, items, postload) \
152 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
154 #include "categories.def"
155 #undef DEFINE_CATEGORY
157 #define NCATEGORIES NELEMS (category)
160 /* Automatically set variable. */
161 extern const char *__progname
;
163 /* helper function for extended name handling. */
164 extern void locale_special (const char *name
, int show_category_name
,
165 int show_keyword_name
);
167 /* Prototypes for local functions. */
168 static void print_LC_IDENTIFICATION (void *mapped
, size_t size
);
169 static void print_LC_CTYPE (void *mapped
, size_t size
);
170 static void write_locales (void);
171 static int nameentcmp (const void *a
, const void *b
);
172 static int write_archive_locales (void **all_datap
, char *linebuf
);
173 static void write_charmaps (void);
174 static void show_locale_vars (void);
175 static void show_info (const char *name
);
176 static void try_setlocale (int category
, const char *category_name
);
177 static char *quote_string (const char *input
);
178 static void setlocale_diagnostics (void);
182 main (int argc
, char *argv
[])
186 /* Set initial values for global variables. */
187 show_category_name
= 0;
188 show_keyword_name
= 0;
190 /* Set locale. Do not set LC_ALL because the other categories must
191 not be affected (according to POSIX.2). */
192 try_setlocale (LC_CTYPE
, "LC_CTYPE");
193 try_setlocale (LC_MESSAGES
, "LC_MESSAGES");
195 /* Initialize the message catalog. */
196 textdomain (PACKAGE
);
198 /* Parse and process arguments. */
199 argp_parse (&argp
, argc
, argv
, 0, &remaining
, NULL
);
201 /* `-a' requests the names of all available locales. */
204 setlocale_diagnostics ();
205 try_setlocale (LC_COLLATE
, "LC_COLLATE");
210 /* `m' requests the names of all available charmaps. The names can be
211 used for the -f argument to localedef(1). */
212 if (do_charmaps
!= 0)
214 setlocale_diagnostics ();
219 /* Specific information about the current locale are requested.
220 Change to this locale now. */
221 try_setlocale (LC_ALL
, "LC_ALL");
222 setlocale_diagnostics ();
224 /* If no real argument is given we have to print the contents of the
225 current locale definition variables. These are LANG and the LC_*. */
226 if (remaining
== argc
&& show_keyword_name
== 0 && show_category_name
== 0)
232 /* Process all given names. */
233 while (remaining
< argc
)
234 show_info (argv
[remaining
++]);
240 /* Handle program arguments. */
242 parse_opt (int key
, char *arg
, struct argp_state
*state
)
250 show_category_name
= 1;
256 show_keyword_name
= 1;
262 return ARGP_ERR_UNKNOWN
;
269 more_help (int key
, const char *text
, void *input
)
274 case ARGP_KEY_HELP_EXTRA
:
275 /* We print some extra information. */
276 if (asprintf (&tp
, gettext ("\
277 For bug reporting instructions, please see:\n\
278 %s.\n"), REPORT_BUGS_TO
) < 0)
284 return (char *) text
;
288 /* Print the version information. */
290 print_version (FILE *stream
, struct argp_state
*state
)
292 fprintf (stream
, "locale %s%s\n", PKGVERSION
, VERSION
);
293 fprintf (stream
, gettext ("\
294 Copyright (C) %s Free Software Foundation, Inc.\n\
295 This is free software; see the source for copying conditions. There is NO\n\
296 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
298 fprintf (stream
, gettext ("Written by %s.\n"), "Ulrich Drepper");
302 /* Simple action function which prints arguments as strings. */
304 print_names (const void *nodep
, VISIT value
, int level
)
306 if (value
== postorder
|| value
== leaf
)
307 puts (*(char **) nodep
);
312 select_dirs (const struct dirent
*dirent
)
316 if (strcmp (dirent
->d_name
, ".") != 0 && strcmp (dirent
->d_name
, "..") != 0)
320 if (dirent
->d_type
!= DT_UNKNOWN
&& dirent
->d_type
!= DT_LNK
)
321 mode
= DTTOIF (dirent
->d_type
);
325 char buf
[sizeof (COMPLOCALEDIR
)
326 + strlen (dirent
->d_name
) + 1];
328 stpcpy (stpcpy (stpcpy (buf
, COMPLOCALEDIR
), "/"),
331 if (stat64 (buf
, &st
) == 0)
335 result
= S_ISDIR (mode
);
343 print_LC_IDENTIFICATION (void *mapped
, size_t size
)
345 /* Read the information from the file. */
349 unsigned int nstrings
;
350 unsigned int strindex
[0];
351 } *filedata
= mapped
;
353 if (filedata
->magic
== LIMAGIC (LC_IDENTIFICATION
)
355 + (filedata
->nstrings
356 * sizeof (unsigned int))
361 #define HANDLE(idx, name) \
362 str = ((char *) mapped \
363 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]); \
365 printf ("%9s | %s\n", name, str)
366 HANDLE (TITLE
, "title");
367 HANDLE (SOURCE
, "source");
368 HANDLE (ADDRESS
, "address");
369 HANDLE (CONTACT
, "contact");
370 HANDLE (EMAIL
, "email");
371 HANDLE (TEL
, "telephone");
373 HANDLE (LANGUAGE
, "language");
374 HANDLE (TERRITORY
, "territory");
375 HANDLE (AUDIENCE
, "audience");
376 HANDLE (APPLICATION
, "application");
377 HANDLE (ABBREVIATION
, "abbreviation");
378 HANDLE (REVISION
, "revision");
379 HANDLE (DATE
, "date");
385 print_LC_CTYPE (void *mapped
, size_t size
)
390 unsigned int nstrings
;
391 unsigned int strindex
[0];
392 } *filedata
= mapped
;
394 if (filedata
->magic
== LIMAGIC (LC_CTYPE
)
396 + (filedata
->nstrings
397 * sizeof (unsigned int))
402 str
= ((char *) mapped
403 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)]);
405 printf (" codeset | %s\n", str
);
410 /* Write the names of all available locales to stdout. We have some
411 sources of the information: the contents of the locale directory
412 and the locale.alias file. To avoid duplicates and print the
413 result is a reasonable order we put all entries is a search tree
414 and print them afterwards. */
419 void *all_data
= NULL
;
420 struct dirent
**dirents
;
424 size_t alias_path_len
;
426 int first_locale
= 1;
428 #define PUT(name) tsearch (name, &all_data, \
429 (int (*) (const void *, const void *)) strcoll)
430 #define GET(name) tfind (name, &all_data, \
431 (int (*) (const void *, const void *)) strcoll)
433 /* `POSIX' locale is always available (POSIX.2 4.34.3). */
435 /* And so is the "C" locale. */
438 memset (linebuf
, '-', sizeof (linebuf
) - 1);
439 linebuf
[sizeof (linebuf
) - 1] = '\0';
441 /* First scan the locale archive. */
442 if (write_archive_locales (&all_data
, linebuf
))
445 /* Now we can look for all files in the directory. */
446 ndirents
= scandir (COMPLOCALEDIR
, &dirents
, select_dirs
,
448 for (cnt
= 0; cnt
< ndirents
; ++cnt
)
450 /* Test whether at least the LC_CTYPE data is there. Some
451 directories only contain translations. */
452 char buf
[sizeof (COMPLOCALEDIR
)
453 + strlen (dirents
[cnt
]->d_name
)
454 + sizeof "/LC_IDENTIFICATION"];
458 stpcpy (enddir
= stpcpy (stpcpy (stpcpy (buf
,
461 dirents
[cnt
]->d_name
),
462 "/LC_IDENTIFICATION");
464 if (stat64 (buf
, &st
) == 0 && S_ISREG (st
.st_mode
))
466 if (verbose
&& GET (dirents
[cnt
]->d_name
) == NULL
)
468 /* Provide some nice output of all kinds of
473 putchar_unlocked ('\n');
476 printf ("locale: %-15.15s directory: %.*s\n%s\n",
477 dirents
[cnt
]->d_name
, (int) (enddir
- buf
), buf
,
480 fd
= open64 (buf
, O_RDONLY
);
483 void *mapped
= mmap64 (NULL
, st
.st_size
, PROT_READ
,
485 if (mapped
!= MAP_FAILED
)
487 print_LC_IDENTIFICATION (mapped
, st
.st_size
);
489 munmap (mapped
, st
.st_size
);
494 /* Now try to get the charset information. */
495 strcpy (enddir
, "/LC_CTYPE");
496 fd
= open64 (buf
, O_RDONLY
);
497 if (fd
!= -1 && fstat64 (fd
, &st
) >= 0
498 && ((mapped
= mmap64 (NULL
, st
.st_size
, PROT_READ
,
502 print_LC_CTYPE (mapped
, st
.st_size
);
504 munmap (mapped
, st
.st_size
);
512 /* If the verbose format is not selected we simply
513 collect the names. */
514 PUT (xstrdup (dirents
[cnt
]->d_name
));
520 /* Now read the locale.alias files. */
521 if (argz_create_sep (LOCALE_ALIAS_PATH
, ':', &alias_path
, &alias_path_len
))
522 error (1, errno
, gettext ("while preparing output"));
525 while ((entry
= argz_next (alias_path
, alias_path_len
, entry
)))
527 static const char aliasfile
[] = "/locale.alias";
529 char full_name
[strlen (entry
) + sizeof aliasfile
];
531 stpcpy (stpcpy (full_name
, entry
), aliasfile
);
532 fp
= fopen (full_name
, "rm");
534 /* Ignore non-existing files. */
537 /* No threads present. */
538 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
540 while (! feof_unlocked (fp
))
542 /* It is a reasonable approach to use a fix buffer here
544 a) we are only interested in the first two fields
545 b) these fields must be usable as file names and so must
552 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
557 /* Ignore leading white space. */
558 while (isspace (cp
[0]) && cp
[0] != '\n')
561 /* A leading '#' signals a comment line. */
562 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
565 while (cp
[0] != '\0' && !isspace (cp
[0]))
567 /* Terminate alias name. */
571 /* Now look for the beginning of the value. */
572 while (isspace (cp
[0]))
578 while (cp
[0] != '\0' && !isspace (cp
[0]))
580 /* Terminate value. */
583 /* This has to be done to make the following
584 test for the end of line possible. We are
585 looking for the terminating '\n' which do not
590 else if (cp
[0] != '\0')
594 if (! verbose
&& GET (value
) != NULL
)
595 PUT (xstrdup (alias
));
599 /* Possibly not the whole line fits into the buffer.
600 Ignore the rest of the line. */
601 while (strchr (cp
, '\n') == NULL
)
604 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
605 /* Make sure the inner loop will be left. The outer
606 loop will exit at the `feof' test. */
616 twalk (all_data
, print_names
);
624 uint32_t locrec_offset
;
629 nameentcmp (const void *a
, const void *b
)
631 return strcoll (((const struct nameent
*) a
)->name
,
632 ((const struct nameent
*) b
)->name
);
637 write_archive_locales (void **all_datap
, char *linebuf
)
640 void *all_data
= *all_datap
;
642 struct locarhead
*head
;
643 struct namehashent
*namehashtab
;
644 char *addr
= MAP_FAILED
;
648 fd
= open64 (ARCHIVE_NAME
, O_RDONLY
);
652 if (fstat64 (fd
, &st
) < 0 || st
.st_size
< sizeof (*head
))
656 addr
= mmap64 (NULL
, len
, PROT_READ
, MAP_SHARED
, fd
, 0);
657 if (addr
== MAP_FAILED
)
660 head
= (struct locarhead
*) addr
;
661 if (head
->namehash_offset
+ head
->namehash_size
> len
662 || head
->string_offset
+ head
->string_size
> len
663 || head
->locrectab_offset
+ head
->locrectab_size
> len
664 || head
->sumhash_offset
+ head
->sumhash_size
> len
)
667 namehashtab
= (struct namehashent
*) (addr
+ head
->namehash_offset
);
670 for (cnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
671 if (namehashtab
[cnt
].locrec_offset
!= 0)
673 PUT (xstrdup (addr
+ namehashtab
[cnt
].name_offset
));
679 struct nameent
*names
;
682 names
= (struct nameent
*) xmalloc (head
->namehash_used
683 * sizeof (struct nameent
));
684 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
685 if (namehashtab
[cnt
].locrec_offset
!= 0)
687 names
[used
].name
= addr
+ namehashtab
[cnt
].name_offset
;
688 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
691 /* Sort the names. */
692 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
694 for (cnt
= 0; cnt
< used
; ++cnt
)
696 struct locrecent
*locrec
;
698 PUT (xstrdup (names
[cnt
].name
));
701 putchar_unlocked ('\n');
703 printf ("locale: %-15.15s archive: " ARCHIVE_NAME
"\n%s\n",
704 names
[cnt
].name
, linebuf
);
706 locrec
= (struct locrecent
*) (addr
+ names
[cnt
].locrec_offset
);
708 print_LC_IDENTIFICATION (addr
709 + locrec
->record
[LC_IDENTIFICATION
].offset
,
710 locrec
->record
[LC_IDENTIFICATION
].len
);
712 print_LC_CTYPE (addr
+ locrec
->record
[LC_CTYPE
].offset
,
713 locrec
->record
[LC_CTYPE
].len
);
720 if (addr
!= MAP_FAILED
)
723 *all_datap
= all_data
;
728 /* Write the names of all available character maps to stdout. */
730 write_charmaps (void)
732 void *all_data
= NULL
;
736 /* Look for all files in the charmap directory. */
737 dir
= charmap_opendir (CHARMAP_PATH
);
741 while ((dirent
= charmap_readdir (dir
)) != NULL
)
746 PUT (xstrdup (dirent
));
748 aliases
= charmap_aliases (CHARMAP_PATH
, dirent
);
751 /* Add the code_set_name and the aliases. */
752 for (p
= aliases
; *p
; p
++)
755 /* Add the code_set_name only. Most aliases are obsolete. */
761 charmap_free_aliases (aliases
);
764 charmap_closedir (dir
);
766 twalk (all_data
, print_names
);
769 /* Print a properly quoted assignment of NAME with VAL, using double
770 quotes iff DQUOTE is true. */
772 print_assignment (const char *name
, const char *val
, bool dquote
)
774 printf ("%s=", name
);
780 = strcspn (val
, dquote
? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
781 printf ("%.*s", (int) segment
, val
);
793 /* We have to show the contents of the environments determining the
796 show_locale_vars (void)
798 const char *lcall
= getenv ("LC_ALL") ?: "";
799 const char *lang
= getenv ("LANG") ?: "";
801 /* LANG has to be the first value. */
802 print_assignment ("LANG", lang
, false);
804 /* Now all categories in an unspecified order. */
805 for (size_t cat_no
= 0; cat_no
< NCATEGORIES
; ++cat_no
)
806 if (cat_no
!= LC_ALL
)
808 const char *name
= category
[cat_no
].name
;
809 const char *val
= getenv (name
);
811 if (lcall
[0] != '\0' || val
== NULL
)
812 print_assignment (name
,
813 lcall
[0] != '\0' ? lcall
814 : lang
[0] != '\0' ? lang
818 print_assignment (name
, val
, false);
821 /* The last is the LC_ALL value. */
822 print_assignment ("LC_ALL", lcall
, false);
826 /* Subroutine of show_info, below. */
828 print_item (struct cat_item
*item
)
830 switch (item
->value_type
)
833 if (show_keyword_name
)
834 printf ("%s=\"", item
->name
);
835 fputs (nl_langinfo (item
->item_id
) ? : "", stdout
);
836 if (show_keyword_name
)
845 if (show_keyword_name
)
846 printf ("%s=\"", item
->name
);
848 for (cnt
= 0; cnt
< item
->max
- 1; ++cnt
)
850 val
= nl_langinfo (item
->item_id
+ cnt
);
856 val
= nl_langinfo (item
->item_id
+ cnt
);
860 if (show_keyword_name
)
868 const char *val
= nl_langinfo (item
->item_id
) ? : "";
870 if (show_keyword_name
)
871 printf ("%s=", item
->name
);
873 for (int cnt
= 0; cnt
< item
->max
&& *val
!= '\0'; ++cnt
)
875 printf ("%s%s%s%s", first
? "" : ";",
876 show_keyword_name
? "\"" : "", val
,
877 show_keyword_name
? "\"" : "");
878 val
= strchr (val
, '\0') + 1;
886 const char *val
= nl_langinfo (item
->item_id
);
888 if (show_keyword_name
)
889 printf ("%s=", item
->name
);
892 printf ("%d", *val
== '\377' ? -1 : *val
);
898 const char *val
= nl_langinfo (item
->item_id
);
899 int cnt
= val
? strlen (val
) : 0;
901 if (show_keyword_name
)
902 printf ("%s=", item
->name
);
906 printf ("%d;", *val
== '\177' ? -1 : *val
);
911 printf ("%d\n", cnt
== 0 || *val
== '\177' ? -1 : *val
);
916 union { unsigned int word
; char *string
; } val
;
917 val
.string
= nl_langinfo (item
->item_id
);
918 if (show_keyword_name
)
919 printf ("%s=", item
->name
);
921 printf ("%d\n", val
.word
);
927 union { unsigned int *wordarray
; char *string
; } val
;
929 val
.string
= nl_langinfo (item
->item_id
);
930 if (show_keyword_name
)
931 printf ("%s=", item
->name
);
933 for (int cnt
= 0; cnt
< item
->max
; ++cnt
)
935 printf ("%s%d", first
? "" : ";", val
.wordarray
[cnt
]);
944 /* We don't print wide character information since the same
945 information is available in a multibyte string. */
951 /* Show the information request for NAME. */
953 show_info (const char *name
)
955 for (size_t cat_no
= 0; cat_no
< NCATEGORIES
; ++cat_no
)
956 if (cat_no
!= LC_ALL
)
958 if (strcmp (name
, category
[cat_no
].name
) == 0)
959 /* Print the whole category. */
961 if (show_category_name
!= 0)
962 puts (category
[cat_no
].name
);
964 for (size_t item_no
= 0;
965 item_no
< category
[cat_no
].number
;
967 print_item (&category
[cat_no
].item_desc
[item_no
]);
972 for (size_t item_no
= 0; item_no
< category
[cat_no
].number
; ++item_no
)
973 if (strcmp (name
, category
[cat_no
].item_desc
[item_no
].name
) == 0)
975 if (show_category_name
!= 0)
976 puts (category
[cat_no
].name
);
978 print_item (&category
[cat_no
].item_desc
[item_no
]);
983 /* The name is not a standard one.
984 For testing and perhaps advanced use allow some more symbols. */
985 locale_special (name
, show_category_name
, show_keyword_name
);
988 /* Set to true by try_setlocale if setlocale fails. Used by
989 setlocale_diagnostics. */
990 static bool setlocale_failed
;
992 /* Call setlocale, with non-fatal error reporting. */
994 try_setlocale (int category
, const char *category_name
)
996 if (setlocale (category
, "") == NULL
)
998 error (0, errno
, gettext ("Cannot set %s to default locale"),
1000 setlocale_failed
= true;
1004 /* Return a quoted version of the passed string, or NULL on error. */
1006 quote_string (const char *input
)
1010 FILE *stream
= open_memstream (&buffer
, &length
);
1016 unsigned char ch
= *input
++;
1020 /* Use C backslash escapes for those control characters for
1021 which they are defined. */
1025 putc_unlocked ('\\', stream
);
1026 putc_unlocked ('a', stream
);
1029 putc_unlocked ('\\', stream
);
1030 putc_unlocked ('b', stream
);
1033 putc_unlocked ('\\', stream
);
1034 putc_unlocked ('f', stream
);
1037 putc_unlocked ('\\', stream
);
1038 putc_unlocked ('n', stream
);
1041 putc_unlocked ('\\', stream
);
1042 putc_unlocked ('r', stream
);
1045 putc_unlocked ('\\', stream
);
1046 putc_unlocked ('t', stream
);
1049 putc_unlocked ('\\', stream
);
1050 putc_unlocked ('v', stream
);
1055 putc_unlocked ('\\', stream
);
1056 putc_unlocked (ch
, stream
);
1059 if (ch
< ' ' || ch
> '~')
1060 /* Use octal sequences because they are fixed width,
1061 unlike hexadecimal sequences. */
1062 fprintf (stream
, "\\%03o", ch
);
1064 putc_unlocked (ch
, stream
);
1068 if (ferror (stream
))
1074 if (fclose (stream
) != 0)
1083 /* Print additional information if there was a setlocale error (during
1086 setlocale_diagnostics (void)
1088 if (setlocale_failed
)
1090 const char *locpath
= getenv ("LOCPATH");
1091 if (locpath
!= NULL
)
1093 char *quoted
= quote_string (locpath
);
1097 warning: The LOCPATH variable is set to \"%s\"\n"),
1100 fputs ("warning: The LOCPATH variable is set\n", stderr
);