2 * Copyright (c) 2010, 2011 Corinna Vinschen
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/cygwin.h>
38 #define LOCALE_ALIAS "/usr/share/locale/locale.alias"
39 #define LOCALE_ALIAS_LINE_LEN 255
41 extern char *__progname;
43 void usage (FILE *, int) __attribute__ ((noreturn));
46 usage (FILE * stream, int status)
49 "Usage: %s [-amsuUvh]\n"
50 " or: %s [-ck] NAME\n"
51 "Get locale-specific information.\n"
55 " -a, --all-locales List all available supported locales\n"
56 " -c, --category-name List information about given category NAME\n"
57 " -k, --keyword-name Print information about given keyword NAME\n"
58 " -m, --charmaps List all available character maps\n"
59 " -s, --system Print system default locale\n"
60 " -u, --user Print user's default locale\n"
61 " -U, --utf Attach \".UTF-8\" to the result\n"
62 " -v, --verbose More verbose output\n"
63 " -h, --help This text\n",
64 __progname, __progname);
68 struct option longopts[] = {
69 {"all-locales", no_argument, NULL, 'a'},
70 {"category-name", no_argument, NULL, 'c'},
71 {"keyword-name", no_argument, NULL, 'k'},
72 {"charmaps", no_argument, NULL, 'm'},
73 {"system", no_argument, NULL, 's'},
74 {"user", no_argument, NULL, 'u'},
75 {"utf", no_argument, NULL, 'U'},
76 {"verbose", no_argument, NULL, 'v'},
77 {"help", no_argument, NULL, 'h'},
78 {0, no_argument, NULL, 0}
80 const char *opts = "achkmsuUv";
83 getlocale (LCID lcid, char *name)
89 if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, 10))
91 GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, 10);
92 sprintf (name, "%s%s%s", iso639, lcid > 0x3ff ? "_" : "",
93 lcid > 0x3ff ? iso3166 : "");
99 const wchar_t *language;
100 const wchar_t *territory;
109 print_codeset (const char *codeset)
111 for (; *codeset; ++codeset)
113 putc (tolower ((int)(unsigned char) *codeset), stdout);
117 print_locale_with_codeset (int verbose, loc_t *locale, bool utf8,
118 const char *modifier)
120 static const char *sysroot;
124 && (!strcmp (locale->name, "C") || !strcmp (locale->name, "POSIX")))
128 char sysbuf[PATH_MAX];
129 HMODULE k32 = GetModuleHandleW (L"kernel32.dll");
130 if (GetModuleFileName (k32, sysbuf, PATH_MAX))
131 sysroot = (const char *) cygwin_create_path (CCP_WIN_A_TO_POSIX,
134 sysroot = "kernel32.dll";
136 snprintf (locname, 32, "%s%s%s%s", locale->name, utf8 ? ".utf8" : "",
137 modifier ? "@" : "", modifier ?: "");
139 fputs ("locale: ", stdout);
142 printf ("%-15s ", locname);
143 printf ("archive: %s\n",
144 locale->alias ? LOCALE_ALIAS : sysroot);
145 puts ("-------------------------------------------------------------------------------");
146 printf (" language | %ls\n", locale->language);
147 printf ("territory | %ls\n", locale->territory);
148 printf (" codeset | %s\n\n", utf8 ? "UTF-8" : locale->codeset);
151 printf ("%s\n", locname);
155 print_locale (int verbose, loc_t *locale)
157 print_locale_with_codeset (verbose, locale, false, NULL);
158 char *modifier = strchr (locale->name, '@');
162 print_locale_with_codeset (verbose, locale, true, NULL);
163 else if (!strcmp (modifier, "@cjknarrow"))
166 print_locale_with_codeset (verbose, locale, true, modifier);
172 compare_locales (const void *a, const void *b)
174 const loc_t *la = (const loc_t *) a;
175 const loc_t *lb = (const loc_t *) b;
176 return strcmp (la->name, lb->name);
180 add_locale (const char *name, const wchar_t *language, const wchar_t *territory,
183 char orig_locale[32];
185 if (loc_num >= loc_max)
187 loc_t *tmp = (loc_t *) realloc (locale, (loc_max + 32) * sizeof (loc_t));
190 fprintf (stderr, "Out of memory!\n");
196 locale[loc_num].name = strdup (name);
197 locale[loc_num].language = wcsdup (language);
198 locale[loc_num].territory = wcsdup (territory);
199 strcpy (orig_locale, setlocale (LC_CTYPE, NULL));
200 setlocale (LC_CTYPE, name);
201 locale[loc_num].codeset = strdup (nl_langinfo (CODESET));
202 setlocale (LC_CTYPE, orig_locale);
203 locale[loc_num].alias = alias;
208 add_locale_alias_locales ()
210 char alias_buf[LOCALE_ALIAS_LINE_LEN + 1], *c;
211 const char *alias, *replace;
212 char orig_locale[32];
214 size_t orig_loc_num = loc_num;
216 FILE *fp = fopen (LOCALE_ALIAS, "rt");
219 strcpy (orig_locale, setlocale (LC_CTYPE, NULL));
220 while (fgets (alias_buf, LOCALE_ALIAS_LINE_LEN + 1, fp))
222 alias_buf[LOCALE_ALIAS_LINE_LEN] = '\0';
223 c = strrchr (alias_buf, '\n');
227 c += strspn (c, " \t");
228 if (!*c || *c == '#')
231 c += strcspn (c, " \t");
233 c += strspn (c, " \t");
237 c += strcspn (c, " \t");
239 c = strchr (replace, '.');
242 search.name = replace;
243 loc = (loc_t *) bsearch (&search, locale, orig_loc_num, sizeof (loc_t),
245 add_locale (alias, loc ? loc->language : L"", loc ? loc->territory : L"",
252 print_all_locales (int verbose)
258 unsigned lang, sublang;
260 add_locale ("C", L"C", L"POSIX");
261 add_locale ("POSIX", L"C", L"POSIX", true);
262 for (lang = 1; lang <= 0xff; ++lang)
265 wchar_t language[256];
266 wchar_t country[256];
271 for (sublang = 1; sublang <= 0x3f; ++sublang)
273 lcid = (sublang << 10) | lang;
274 if (getlocale (lcid, name))
276 wchar_t language[256];
277 wchar_t country[256];
282 /* Go figure. Even the English name of a language or
283 locale might contain native characters. */
284 GetLocaleInfoW (lcid, LOCALE_SENGLANGUAGE, language, 256);
285 GetLocaleInfoW (lcid, LOCALE_SENGCOUNTRY, country, 256);
287 for (i = 0; i < lcnt; ++ i)
288 if (!wcscmp (loc_list[i].language, language)
289 && !wcscmp (loc_list[i].country, country))
295 wcscpy (loc_list[lcnt].language, language);
296 wcscpy (loc_list[lcnt].country, country);
298 c = stpcpy (loc, name);
299 /* Convert old sr_SP silently to sr_CS on old systems.
300 Make sure sr_CS country is in recent shape. */
301 if (lang == LANG_SERBIAN
302 && (sublang == SUBLANG_SERBIAN_LATIN
303 || sublang == SUBLANG_SERBIAN_CYRILLIC))
305 c = stpcpy (loc, "sr_CS");
306 wcscpy (country, L"Serbia and Montenegro (Former)");
308 /* Now check certain conditions to figure out if that
309 locale requires a modifier. */
310 if (lang == LANG_SERBIAN && !strncmp (loc, "sr_", 3)
311 && wcsstr (language, L"(Latin)"))
312 stpcpy (c, "@latin");
313 else if (lang == LANG_UZBEK
314 && sublang == SUBLANG_UZBEK_CYRILLIC)
315 stpcpy (c, "@cyrillic");
316 /* Avoid more dups */
317 for (i = 0; i < lcnt; ++ i)
318 if (!strcmp (loc_list[i].loc, loc))
326 strcpy (loc_list[lcnt++].loc, loc);
328 add_locale (loc, language, country);
329 /* Check for locales which sport a modifier for
330 changing the codeset and other stuff. */
331 if (lang == LANG_BELARUSIAN
332 && sublang == SUBLANG_BELARUSIAN_BELARUS)
333 stpcpy (c, "@latin");
334 else if (lang == LANG_TATAR
335 && sublang == SUBLANG_TATAR_RUSSIA)
336 stpcpy (c, "@iqtelif");
337 else if (GetLocaleInfoW (lcid,
338 LOCALE_IDEFAULTANSICODEPAGE
339 | LOCALE_RETURN_NUMBER,
340 (PWCHAR) &cp, sizeof cp)
341 && cp == 1252 /* Latin1*/
342 && GetLocaleInfoW (lcid, LOCALE_SINTLSYMBOL, wbuf, 9)
343 && !wcsncmp (wbuf, L"EUR", 3))
345 else if (lang == LANG_JAPANESE
346 || lang == LANG_KOREAN
347 || lang == LANG_CHINESE)
348 stpcpy (c, "@cjknarrow");
351 add_locale (loc, language, country);
354 /* Check Serbian language for the available territories. Up to
355 Server 2003 we only had sr_SP (silently converted to sr_CS
356 above), in Vista we had only sr_CS. First starting with W7 we
357 have the actual sr_RS and sr_ME. However, all of them are
358 supported on all systems in Cygwin. So we fake them here, if
360 if (lang == LANG_SERBIAN)
366 for (i = 0; i < lcnt; ++ i)
367 if (!strcmp (loc_list[i].loc, "sr_CS"))
369 else if (!strcmp (loc_list[i].loc, "sr_RS"))
371 if (sr_CS_idx > 0 && sr_RS_idx == -1)
373 add_locale ("sr_RS@latin", L"Serbian (Latin)", L"Serbia");
374 add_locale ("sr_RS", L"Serbian (Cyrillic)", L"Serbia");
375 add_locale ("sr_ME@latin", L"Serbian (Latin)", L"Montenegro");
376 add_locale ("sr_ME", L"Serbian (Cyrillic)", L"Montenegro");
380 /* First sort allows add_locale_alias_locales to bsearch in locales. */
381 qsort (locale, loc_num, sizeof (loc_t), compare_locales);
382 add_locale_alias_locales ();
383 qsort (locale, loc_num, sizeof (loc_t), compare_locales);
384 for (size_t i = 0; i < loc_num; ++i)
385 print_locale (verbose, &locale[i]);
391 /* FIXME: We need a method to fetch the available charsets from Cygwin, */
392 const char *charmaps[] =
448 const char **charmap = charmaps;
450 printf ("%s\n", *charmap++);
454 print_lc_ivalue (int key, const char *name, int value)
457 printf ("%s=", name);
458 printf ("%d", value == CHAR_MAX ? -1 : value);
459 fputc ('\n', stdout);
463 print_lc_svalue (int key, const char *name, const char *value)
466 printf ("%s=\"", name);
467 fputs (value, stdout);
470 fputc ('\n', stdout);
474 print_lc_sepstrings (int key, const char *name, const char *value)
479 printf ("%s=", name);
480 while (value && *value)
484 c = strchr (value, ';');
487 fputs (value, stdout);
492 printf ("%.*s", c - value, value);
500 fputc ('\n', stdout);
504 print_lc_strings (int key, const char *name, int from, int to)
507 printf ("%s=\"", name);
508 for (int i = from; i <= to; ++i)
509 printf ("%s%s", i > from ? ";" : "", nl_langinfo (i));
512 fputc ('\n', stdout);
516 print_lc_grouping (int key, const char *name, const char *grouping)
519 printf ("%s=", name);
520 for (const char *g = grouping; *g; ++g)
521 printf ("%s%d", g > grouping ? ";" : "", *g == CHAR_MAX ? -1 : *g);
522 fputc ('\n', stdout);
545 const char *fake_string[] = {
546 "upper;lower;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum",
547 "upper\";\"lower\";\"alpha\";\"digit\";\"xdigit\";\"space\";\"print\";\"graph\";\"blank\";\"cntrl\";\"punct\";\"alnum",
549 "toupper\";\"tolower"
552 lc_names_t lc_ctype_names[] =
554 { "ctype-class-names", is_string_fake, 0, 0 },
555 { "ctype-map-names", is_string_fake, 2, 0 },
556 { "ctype-outdigit0_mb", is_string, _NL_CTYPE_OUTDIGITS0_MB, 0 },
557 { "ctype-outdigit1_mb", is_string, _NL_CTYPE_OUTDIGITS1_MB, 0 },
558 { "ctype-outdigit2_mb", is_string, _NL_CTYPE_OUTDIGITS2_MB, 0 },
559 { "ctype-outdigit3_mb", is_string, _NL_CTYPE_OUTDIGITS3_MB, 0 },
560 { "ctype-outdigit4_mb", is_string, _NL_CTYPE_OUTDIGITS4_MB, 0 },
561 { "ctype-outdigit5_mb", is_string, _NL_CTYPE_OUTDIGITS5_MB, 0 },
562 { "ctype-outdigit6_mb", is_string, _NL_CTYPE_OUTDIGITS6_MB, 0 },
563 { "ctype-outdigit7_mb", is_string, _NL_CTYPE_OUTDIGITS7_MB, 0 },
564 { "ctype-outdigit8_mb", is_string, _NL_CTYPE_OUTDIGITS8_MB, 0 },
565 { "ctype-outdigit9_mb", is_string, _NL_CTYPE_OUTDIGITS9_MB, 0 },
566 { "ctype-outdigit0_wc", is_wchar, _NL_CTYPE_OUTDIGITS0_WC, 0 },
567 { "ctype-outdigit1_wc", is_wchar, _NL_CTYPE_OUTDIGITS1_WC, 0 },
568 { "ctype-outdigit2_wc", is_wchar, _NL_CTYPE_OUTDIGITS2_WC, 0 },
569 { "ctype-outdigit3_wc", is_wchar, _NL_CTYPE_OUTDIGITS3_WC, 0 },
570 { "ctype-outdigit4_wc", is_wchar, _NL_CTYPE_OUTDIGITS4_WC, 0 },
571 { "ctype-outdigit5_wc", is_wchar, _NL_CTYPE_OUTDIGITS5_WC, 0 },
572 { "ctype-outdigit6_wc", is_wchar, _NL_CTYPE_OUTDIGITS6_WC, 0 },
573 { "ctype-outdigit7_wc", is_wchar, _NL_CTYPE_OUTDIGITS7_WC, 0 },
574 { "ctype-outdigit8_wc", is_wchar, _NL_CTYPE_OUTDIGITS8_WC, 0 },
575 { "ctype-outdigit9_wc", is_wchar, _NL_CTYPE_OUTDIGITS9_WC, 0 },
576 { "charmap", is_string, CODESET, 0 },
577 { "ctype-mb-cur-max", is_int, _NL_CTYPE_MB_CUR_MAX, 0 },
578 { NULL, is_end, 0, 0 }
581 lc_names_t lc_numeric_names[] =
583 { "decimal_point", is_string, RADIXCHAR, 0 },
584 { "thousands_sep", is_string, THOUSEP, 0 },
585 { "grouping", is_grouping, _NL_NUMERIC_GROUPING, 0 },
586 { "numeric-decimal-point-wc", is_wchar, _NL_NUMERIC_DECIMAL_POINT_WC, 0 },
587 { "numeric-thousands-sep-wc", is_wchar, _NL_NUMERIC_THOUSANDS_SEP_WC, 0 },
588 { "numeric-codeset", is_string, _NL_NUMERIC_CODESET, 0 },
589 { NULL, is_end, 0, 0 }
592 lc_names_t lc_time_names[] =
594 { "abday", is_mstrings, ABDAY_1, ABDAY_7 },
595 { "day", is_mstrings, DAY_1, DAY_7 },
596 { "abmon", is_mstrings, ABMON_1, ABMON_12 },
597 { "mon", is_mstrings, MON_1, MON_12 },
598 { "am_pm", is_mstrings, AM_STR, PM_STR },
599 { "d_t_fmt", is_string, D_T_FMT, 0 },
600 { "d_fmt", is_string, D_FMT, 0 },
601 { "t_fmt", is_string, T_FMT, 0 },
602 { "t_fmt_ampm", is_string, T_FMT_AMPM, 0 },
603 { "era", is_sepstrings, ERA, 0 },
604 { "era_d_fmt", is_string, ERA_D_FMT, 0 },
605 { "alt_digits", is_sepstrings,ALT_DIGITS, 0 },
606 { "era_d_t_fmt", is_string, ERA_D_T_FMT, 0 },
607 { "era_t_fmt", is_string, ERA_T_FMT, 0 },
608 { "date_fmt", is_string, _DATE_FMT, 0 },
609 { "time-codeset", is_string, _NL_TIME_CODESET, 0 },
610 { NULL, is_end, 0, 0 }
613 lc_names_t lc_collate_names[] =
615 { "collate-codeset", is_string, _NL_COLLATE_CODESET, 0 },
616 { NULL, is_end, 0, 0 }
619 lc_names_t lc_monetary_names[] =
621 { "int_curr_symbol", is_string, _NL_MONETARY_INT_CURR_SYMBOL, 0 },
622 { "currency_symbol", is_string, _NL_MONETARY_CURRENCY_SYMBOL, 0 },
623 { "mon_decimal_point", is_string, _NL_MONETARY_MON_DECIMAL_POINT, 0 },
624 { "mon_thousands_sep", is_string, _NL_MONETARY_MON_THOUSANDS_SEP, 0 },
625 { "mon_grouping", is_grouping, _NL_MONETARY_MON_GROUPING, 0 },
626 { "positive_sign", is_string, _NL_MONETARY_POSITIVE_SIGN, 0 },
627 { "negative_sign", is_string, _NL_MONETARY_NEGATIVE_SIGN, 0 },
628 { "int_frac_digits", is_int, _NL_MONETARY_INT_FRAC_DIGITS, 0 },
629 { "frac_digits", is_int, _NL_MONETARY_FRAC_DIGITS, 0 },
630 { "p_cs_precedes", is_int, _NL_MONETARY_P_CS_PRECEDES, 0 },
631 { "p_sep_by_space", is_int, _NL_MONETARY_P_SEP_BY_SPACE, 0 },
632 { "n_cs_precedes", is_int, _NL_MONETARY_N_CS_PRECEDES, 0 },
633 { "n_sep_by_space", is_int, _NL_MONETARY_N_SEP_BY_SPACE, 0 },
634 { "p_sign_posn", is_int, _NL_MONETARY_P_SIGN_POSN, 0 },
635 { "n_sign_posn", is_int, _NL_MONETARY_N_SIGN_POSN, 0 },
636 { "int_p_cs_precedes", is_int, _NL_MONETARY_INT_P_CS_PRECEDES, 0 },
637 { "int_p_sep_by_space", is_int, _NL_MONETARY_INT_P_SEP_BY_SPACE,0 },
638 { "int_n_cs_precedes", is_int, _NL_MONETARY_INT_N_CS_PRECEDES, 0 },
639 { "int_n_sep_by_space", is_int, _NL_MONETARY_INT_N_SEP_BY_SPACE,0 },
640 { "int_p_sign_posn", is_int, _NL_MONETARY_INT_P_SIGN_POSN, 0 },
641 { "int_n_sign_posn", is_int, _NL_MONETARY_INT_N_SIGN_POSN, 0 },
642 { "monetary-decimal-point-wc", is_wchar, _NL_MONETARY_WMON_DECIMAL_POINT, 0 },
643 { "monetary-thousands-sep-wc", is_wchar, _NL_MONETARY_WMON_THOUSANDS_SEP, 0 },
644 { "monetary-codeset", is_string, _NL_MONETARY_CODESET, 0 },
645 { NULL, is_end, 0, 0 }
648 lc_names_t lc_messages_names[] =
650 { "yesexpr", is_string, YESEXPR, 0 },
651 { "noexpr", is_string, NOEXPR, 0 },
652 { "yesstr", is_string, YESSTR, 0 },
653 { "nostr", is_string, NOSTR, 0 },
654 { "messages-codeset", is_string, _NL_MESSAGES_CODESET, 0 },
655 { NULL, is_end, 0, 0 }
659 print_lc (int cat, int key, const char *category, const char *name,
663 printf ("%s\n", category);
664 for (lc_names_t *lc = lc_name; lc->type != is_end; ++lc)
665 if (!name || !strcmp (name, lc->name))
669 print_lc_svalue (key, lc->name, fake_string[lc->fromval + key]);
672 print_lc_grouping (key, lc->name, nl_langinfo (lc->fromval));
675 print_lc_svalue (key, lc->name, nl_langinfo (lc->fromval));
678 print_lc_sepstrings (key, lc->name, nl_langinfo (lc->fromval));
681 print_lc_strings (key, lc->name, lc->fromval, lc->toval);
684 print_lc_ivalue (key, lc->name, (int) *nl_langinfo (lc->fromval));
687 print_lc_ivalue (key, lc->name,
688 *(wchar_t *) nl_langinfo (lc->fromval));
697 const char *category;
699 lc_names_t *lc_names;
702 { "LC_CTYPE", LC_CTYPE, lc_ctype_names },
703 { "LC_NUMERIC", LC_NUMERIC, lc_numeric_names },
704 { "LC_TIME", LC_TIME, lc_time_names },
705 { "LC_COLLATE", LC_COLLATE, lc_collate_names },
706 { "LC_MONETARY", LC_MONETARY, lc_monetary_names },
707 { "LC_MESSAGES", LC_MESSAGES, lc_messages_names },
712 print_names (int cat, int key, const char *name)
717 for (c = categories; c->category; ++c)
718 if (!strcmp (name, c->category))
720 print_lc (cat, key, c->category, NULL, c->lc_names);
723 for (c = categories; c->category; ++c)
724 for (lc = c->lc_names; lc->type != is_end; ++lc)
725 if (!strcmp (name, lc->name))
727 print_lc (cat, key, c->category, lc->name, lc);
735 printf ("LANG=%s\n", getenv ("LANG") ?: "");
736 printf ("LC_CTYPE=\"%s\"\n", setlocale (LC_CTYPE, NULL));
737 printf ("LC_NUMERIC=\"%s\"\n", setlocale (LC_NUMERIC, NULL));
738 printf ("LC_TIME=\"%s\"\n", setlocale (LC_TIME, NULL));
739 printf ("LC_COLLATE=\"%s\"\n", setlocale (LC_COLLATE, NULL));
740 printf ("LC_MONETARY=\"%s\"\n", setlocale (LC_MONETARY, NULL));
741 printf ("LC_MESSAGES=\"%s\"\n", setlocale (LC_MESSAGES, NULL));
742 printf ("LC_ALL=%s\n", getenv ("LC_ALL") ?: "");
746 main (int argc, char **argv)
755 const char *utf = "";
758 setlocale (LC_ALL, "");
759 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
775 lcid = LOCALE_SYSTEM_DEFAULT;
778 lcid = LOCALE_USER_DEFAULT;
794 print_all_locales (verbose);
799 if (getlocale (lcid, name))
800 printf ("%s%s\n", name, utf);
802 else if (optind < argc)
803 while (optind < argc)
804 print_names (cat, key, argv[optind++]);