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