1 /* idn2.c - command line interface to libidn2
2 Copyright (C) 2011-2021 Simon Josefsson, Tim Ruehsen
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <config.h>
19
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <locale.h>
26 #include <unistd.h>
27
28 #include <idn2.h>
29
30 #include <unitypes.h>
31 #include <uniconv.h>
32 #include <unistr.h>
33 #include <unistring/localcharset.h>
34
35 /* Gnulib headers. */
36 #include "err.h"
37 #include "gettext.h"
38 #define _(String) dgettext (PACKAGE, String)
39 #include "progname.h"
40 #include "version-etc.h"
41
42 #include "idn2_cmd.h"
43
44 #include "blurbs.h"
45
46 #ifdef __cplusplus
47 extern // define a global const variable in C++, C doesn't need it.
48 #endif
49 const char version_etc_copyright[] =
50 /* Do *not* mark this string for translation. %s is a copyright
51 symbol suitable for this locale, and %d is the copyright
52 year. */
53 "Copyright %s 2011-%d Simon Josefsson, Tim Ruehsen.";
54
55 static void
usage(int status)56 usage (int status)
57 {
58 if (status != EXIT_SUCCESS)
59 fprintf (stderr, _("Try `%s --help' for more information.\n"),
60 program_name);
61 else
62 {
63 printf (_("\
64 Usage: %s [OPTION]... [STRINGS]...\n\
65 "), program_name);
66 fputs (_("\
67 Internationalized Domain Name (IDNA2008) convert STRINGS, or standard input.\n\
68 \n\
69 "), stdout);
70 fputs (_("\
71 Command line interface to the Libidn2 implementation of IDNA2008.\n\
72 \n\
73 All strings are expected to be encoded in the locale charset.\n\
74 \n\
75 To process a string that starts with `-', for example `-foo', use `--'\n\
76 to signal the end of parameters, as in `idn2 --quiet -- -foo'.\n\
77 \n\
78 Mandatory arguments to long options are mandatory for short options too.\n\
79 "), stdout);
80 fputs (_("\
81 -h, --help Print help and exit\n\
82 -V, --version Print version and exit\n\
83 "), stdout);
84 fputs (_("\
85 -d, --decode Decode (punycode) domain name\n\
86 -l, --lookup Lookup domain name (default)\n\
87 -r, --register Register label\n\
88 "), stdout);
89 fputs (_("\
90 -T, --tr46t Enable TR46 transitional processing\n\
91 -N, --tr46nt Enable TR46 non-transitional processing\n\
92 --no-tr46 Disable TR46 processing\n\
93 "), stdout);
94 fputs (_("\
95 --usestd3asciirules Enable STD3 ASCII rules\n\
96 --no-alabelroundtrip Disable A-label roundtrip for lookups\n\
97 --debug Print debugging information\n\
98 --quiet Silent operation\n\
99 "), stdout);
100 emit_bug_reporting_address ();
101 }
102 exit (status);
103 }
104
105 static void
hexdump(const char * prefix,const char * str)106 hexdump (const char *prefix, const char *str)
107 {
108 uint8_t *u8;
109 uint32_t *u32 = NULL;
110 size_t u32len;
111 size_t i;
112 const char *encoding = locale_charset ();
113
114 u8 = u8_strconv_from_encoding (str, encoding, iconveh_error);
115 if (u8)
116 u32 = u8_to_u32 (u8, strlen ((char *) u8), NULL, &u32len);
117
118 for (i = 0; i < strlen (str); i++)
119 fprintf (stderr, "%s[%lu] = 0x%02x\n",
120 prefix, (unsigned long) i, (unsigned) (str[i] & 0xFF));
121
122 if (u8 && strcmp (str, (char *) u8) != 0)
123 for (i = 0; i < strlen ((char *) u8); i++)
124 fprintf (stderr, "UTF-8 %s[%lu] = 0x%02x\n",
125 prefix, (unsigned long) i, u8[i] & 0xFF);
126
127 if (u8 && u32)
128 for (i = 0; i < u32len; i++)
129 fprintf (stderr, "UCS-4 %s[%lu] = U+%04x\n",
130 prefix, (unsigned long) i, u32[i]);
131 }
132
133 static struct gengetopt_args_info args_info;
134
135 static void
process_input(char * readbuf,int flags)136 process_input (char *readbuf, int flags)
137 {
138 size_t len = strlen (readbuf);
139 char *output;
140 const char *tag;
141 int rc;
142
143 if (len && readbuf[len - 1] == '\n')
144 readbuf[len - 1] = '\0';
145
146 if (strcmp (readbuf, "show w") == 0)
147 {
148 puts (WARRANTY);
149 return;
150 }
151 else if (strcmp (readbuf, "show c") == 0)
152 {
153 puts (CONDITIONS);
154 return;
155 }
156
157 if (args_info.debug_given)
158 hexdump ("input", readbuf);
159
160 if (args_info.register_given)
161 {
162 rc = idn2_register_ul (readbuf, NULL, &output, flags);
163 tag = "register";
164 }
165 else if (args_info.decode_given)
166 {
167 rc = idn2_to_unicode_lzlz (readbuf, &output, 0);
168 tag = "decode";
169 }
170 else
171 {
172 rc = idn2_to_ascii_lz (readbuf, &output, flags);
173 tag = "toAscii";
174 }
175
176 if (rc == IDN2_OK)
177 {
178 if (args_info.debug_given)
179 hexdump ("output", readbuf);
180
181 printf ("%s\n", output);
182 free (output);
183 }
184 else
185 errc (EXIT_FAILURE, 0, "%s: %s", tag, idn2_strerror (rc));
186 }
187
188 int
main(int argc,char * argv[])189 main (int argc, char *argv[])
190 {
191 unsigned cmdn;
192 int flags = IDN2_NONTRANSITIONAL;
193
194 setlocale (LC_ALL, "");
195 set_program_name (argv[0]);
196 bindtextdomain (PACKAGE, LOCALEDIR);
197 textdomain (PACKAGE);
198
199 if (cmdline_parser (argc, argv, &args_info) != 0)
200 return EXIT_FAILURE;
201
202 if (args_info.version_given)
203 {
204 version_etc (stdout, "idn2", PACKAGE_NAME, VERSION,
205 "Simon Josefsson, Tim Ruehsen", (char *) NULL);
206 return EXIT_SUCCESS;
207 }
208
209 if (args_info.help_given)
210 usage (EXIT_SUCCESS);
211
212 if (!args_info.quiet_given
213 && args_info.inputs_num == 0 && isatty (fileno (stdin)))
214 fprintf (stderr, "%s %s\n" GREETING, PACKAGE, VERSION);
215
216 if (args_info.debug_given)
217 fprintf (stderr, _("Charset: %s\n"), locale_charset ());
218
219 if (!args_info.quiet_given
220 && args_info.inputs_num == 0 && isatty (fileno (stdin)))
221 fprintf (stderr, "%s", _("Type each input string on a line by itself, "
222 "terminated by a newline character.\n"));
223
224 if (args_info.tr46t_given)
225 flags = IDN2_TRANSITIONAL;
226 else if (args_info.tr46nt_given)
227 flags = IDN2_NONTRANSITIONAL;
228 else if (args_info.no_tr46_given)
229 flags = IDN2_NO_TR46;
230
231 if (flags && args_info.usestd3asciirules_given)
232 flags |= IDN2_USE_STD3_ASCII_RULES;
233
234 if (flags && args_info.no_alabelroundtrip_given)
235 flags |= IDN2_NO_ALABEL_ROUNDTRIP;
236
237 for (cmdn = 0; cmdn < args_info.inputs_num; cmdn++)
238 process_input (args_info.inputs[cmdn], flags | IDN2_NFC_INPUT);
239
240 if (!cmdn)
241 {
242 char *buf = NULL;
243 size_t bufsize = 0;
244
245 while (getline (&buf, &bufsize, stdin) > 0)
246 process_input (buf, flags);
247
248 free (buf);
249 }
250
251 if (ferror (stdin))
252 errc (EXIT_FAILURE, errno, "%s", _("input error"));
253
254 cmdline_parser_free (&args_info);
255
256 return EXIT_SUCCESS;
257 }
258