Update FSF postal address.
[m17n/m17n-lib.git] / example / mconv.c
1 /* mconv.c -- Code converter.                           -*- coding: euc-jp; -*-
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21    02111-1307, USA.  */
22
23 /***en
24     @enpage m17n-conv convert file code
25
26     @section m17n-conv-synopsis SYNOPSIS
27
28     m17n-conv [ OPTION ... ] [ INFILE [ OUTFILE ] ]
29
30     @section m17n-conv-description DESCRIPTION
31
32     Convert encoding of given files from one to another.
33
34     If INFILE is omitted, the input is taken from standard input.  If
35     OUTFILE is omitted, the output written to standard output.
36
37     The following OPTIONs are available.
38
39     <ul>
40
41     <li> -f FROMCODE
42
43     FROMCODE is the encoding of INFILE (defaults to UTF-8).
44
45     <li> -t TOCODE
46
47     TOCODE is the encoding of OUTFILE (defaults to UTF-8).
48
49     <li> -k
50
51     Do not stop conversion on error.
52
53     <li> -s
54
55     Suppress warnings.
56
57     <li> -v
58
59     Print progress information.
60
61     <li> -l
62
63     List available encodings.
64
65     <li> --version
66
67     Print version number.
68
69     <li> -h, --help
70
71     Print this message.
72
73     </ul>
74 */
75 /***ja
76     @japage m17n-conv ¥Õ¥¡¥¤¥ë¤Î¥³¡¼¥É¤òÊÑ´¹¤¹¤ë
77
78     @section m17n-conv-synopsis SYNOPSIS
79
80     m17n-conv [ OPTION ... ] [ INFILE [ OUTFILE ] ]
81
82     @section m17n-conv-description ÀâÌÀ
83
84     Í¿¤¨¤é¤ì¤¿¥Õ¥¡¥¤¥ë¤Î¥³¡¼¥É¤òÊ̤Τâ¤Î¤ËÊÑ´¹¤¹¤ë¡£ 
85
86     INFILE ¤¬¾Êά¤µ¤ì¤¿¾ì¹ç¤Ï¡¢É¸½àÆþÎϤ«¤é¤È¤ë¡£OUTFILE ¤¬¾Êά¤µ¤ì¤¿ 
87     ¾ì¹ç¤Ï¡¢É¸½à½ÐÎϤؽñ¤­½Ð¤¹¡£
88
89     °Ê²¼¤Î¥ª¥×¥·¥ç¥ó¤¬ÍøÍѤǤ­¤ë¡£
90
91     <ul>
92
93     <li> -f FROMCODE
94
95     FROMCODE ¤Ï INFILE ¤Î¥³¡¼¥É·Ï¤Ç¤¢¤ë¡£(¥Ç¥Õ¥©¥ë¥È¤Ï UTF-8) 
96
97     <li> -t TOCODE
98
99     TOCODE ¤Ï OUTFILE ¤Î¥³¡¼¥É·Ï¤Ç¤¢¤ë¡£(¥Ç¥Õ¥©¥ë¥È¤Ï UTF-8) 
100
101     <li> -k
102
103     ¥¨¥é¡¼¤ÇÊÑ´¹¤òÄä»ß¤·¤Ê¤¤¡£
104
105     <li> -s
106
107     ·Ù¹ð¤òɽ¼¨¤·¤Ê¤¤¡£ 
108
109     <li> -v
110
111     ¿Ê¹Ô¾õ¶·¤òɽ¼¨¤¹¤ë¡£ 
112
113     <li> -l
114
115     ÍøÍѲÄǽ¤Ê¥³¡¼¥É·Ï¤òÎóµó¤¹¤ë¡£ 
116
117     <li> --version
118
119     ¥Ð¡¼¥¸¥ç¥óÈÖ¹æ¤òɽ¼¨¤¹¤ë¡£ 
120
121     <li> -h, --help
122
123     ¤³¤Î¥á¥Ã¥»¡¼¥¸¤òɽ¼¨¤¹¤ë¡£ 
124
125     </ul>
126 */
127
128 #ifndef FOR_DOXYGEN
129
130 #include <stdio.h>
131 #include <stdlib.h>
132 #include <string.h>
133
134 #include <m17n.h>
135 #include <m17n-misc.h>
136
137 #define VERSION "1.3.4"
138
139 /* Print all coding system names.  */
140
141 int
142 compare_coding_name (const void *elt1, const void *elt2)
143 {
144   const MSymbol *n1 = elt1;
145   const MSymbol *n2 = elt2;
146
147   return strcmp (msymbol_name (*n1), msymbol_name (*n2));
148 }
149
150 void
151 list_coding ()
152 {
153   MSymbol *codings;
154   int i, n;
155   char *name;
156   int len, clm;
157
158   n = mconv_list_codings (&codings);
159   qsort (codings, n, sizeof (MSymbol), compare_coding_name);
160   clm = 0;
161   for (i = 0; i < n; i++)
162     {
163       name = msymbol_name (codings[i]);
164       len = strlen (name) + 1;
165       if (clm + len >= 80)
166         {
167           printf ("\n");
168           clm = 0;
169         }
170       printf (" %s", name);
171       clm += len;
172     }
173   printf ("\n");
174   free (codings);
175 }
176
177
178 /* Print the usage of this program (the name is PROG), and exit with
179    EXIT_CODE.  */
180
181 void
182 help_exit (char *prog, int exit_code)
183 {
184   char *p = prog;
185
186   while (*p)
187     if (*p++ == '/')
188       prog = p;
189
190   printf ("Usage: %s [ OPTION ... ] [ INFILE [ OUTFILE ] ]\n", prog);
191   printf ("Convert encoding of given files from one to another.\n");
192   printf ("  If INFILE is omitted, the input is taken from standard input.\n");
193   printf ("  If OUTFILE is omitted, the output is written to standard output.\n");
194   printf ("The following OPTIONs are available.\n");
195   printf ("  %-13s %s", "-f FROMCODE",
196           "FROMCODE is the encoding of INFILE (defaults to UTF-8).\n");
197   printf ("  %-13s %s", "-t TOCODE",
198           "TOCODE is the encoding of OUTFILE (defaults to UTF-8).\n");
199   printf ("  %-13s %s", "-k", "Do not stop conversion on error.\n");
200   printf ("  %-13s %s", "-s", "Suppress warnings.\n");
201   printf ("  %-13s %s", "-v", "Print progress information.\n");
202   printf ("  %-13s %s", "-l", "List available encodings.\n");
203   printf ("  %-13s %s", "--version", "Print version number.\n");
204   printf ("  %-13s %s", "-h, --help", "Print this message.\n");
205   exit (exit_code);
206 }
207
208
209 /* Check invalid bytes found in the last decoding.  Text property
210    Mcharset of such a byte is Mcharset_binary.  */
211
212 void
213 check_invalid_bytes (MText *mt)
214 {
215   int from = 0, to = 0;
216   int len = mtext_len (mt);
217   int first = 1;
218
219   while (to < len)
220     {
221       int n = mtext_prop_range (mt, Mcharset, from, NULL, &to, 1);
222       MSymbol charset
223         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcharset) : Mnil;
224
225       if (charset == Mcharset_binary)
226         {
227           if (first)
228             {
229               fprintf (stderr,
230                        "Invalid bytes (at each character position);\n");
231               first = 0;
232             }
233           for (; from < to; from++)
234             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
235         }
236       else
237         from = to;
238     }
239   if (! first)
240     fprintf (stderr, "\n");
241 }
242
243
244 /* Check unencoded characters in the last encoding.  Text property
245    Mcoding of such a character is Mnil.  */
246
247 void
248 check_unencoded_chars (MText *mt, int len)
249 {
250   int from = 0, to = 0;
251   int first = 1;
252
253   while (to < len)
254     {
255       int n = mtext_prop_range (mt, Mcoding, from, NULL, &to, 1);
256       MSymbol coding
257         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcoding) : Mnil;
258
259       if (coding == Mnil)
260         {
261           if (first)
262             {
263               fprintf (stderr,
264                        "Unencoded characters (at each character position):\n");
265               first = 0;
266             }
267           for (; from < to; from++)
268             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
269         }
270       else
271         from = to;
272     }
273   if (! first)
274     fprintf (stderr, "\n");
275 }
276
277
278 /* Format MSG by FMT and print the result to the stderr, and exit.  */
279
280 #define FATAL_ERROR(fmt, arg)   \
281   do {                          \
282     fprintf (stderr, fmt, arg); \
283     exit (1);                   \
284   } while (0)
285
286
287 int
288 main (int argc, char **argv)
289 {
290   int suppress_warning, verbose, continue_on_error;
291   MSymbol incode, outcode;
292   FILE *in, *out;
293   MText *mt;
294   MConverter *converter;
295   int i;
296
297   /* Initialize the m17n library.  */
298   M17N_INIT ();
299   if (merror_code != MERROR_NONE)
300     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library.");
301
302   /* Default encodings are both UTF-8.  */
303   incode = outcode = Mcoding_utf_8;
304   /* By default, read from standard input and write to standard output. */
305   in = stdin, out = stdout;
306   /* By default, all these flags are 0.  */
307   suppress_warning = verbose = continue_on_error = 0;
308   /* Parse the command line arguments.  */
309   for (i = 1; i < argc; i++)
310     {
311       if (! strcmp (argv[i], "--help")
312                || ! strcmp (argv[i], "-h")
313                || ! strcmp (argv[i], "-?"))
314         help_exit (argv[0], 0);
315       else if (! strcmp (argv[i], "--version"))
316         {
317           printf ("m17n-conv (m17n library) %s\n", VERSION);
318           printf ("Copyright (C) 2003 AIST, JAPAN\n");
319           exit (0);
320         }
321       else if (! strcmp (argv[i], "-l"))
322         {
323           list_coding ();
324           M17N_FINI ();
325           exit (0);
326         }
327       else if (! strcmp (argv[i], "-f"))
328         {
329           incode = mconv_resolve_coding (msymbol (argv[++i]));
330           if (incode == Mnil)
331             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
332         }
333       else if (! strcmp (argv[i], "-t"))
334         {
335           outcode = mconv_resolve_coding (msymbol (argv[++i]));
336           if (outcode == Mnil)
337             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
338         }
339       else if (! strcmp (argv[i], "-k"))
340         continue_on_error = 1;
341       else if (! strcmp (argv[i], "-s"))
342         suppress_warning = 1;
343       else if (! strcmp (argv[i], "-v"))
344         verbose = 1;
345       else if (argv[i][0] != '-')
346         {
347           if (in == stdin)
348             {
349               in = fopen (argv[i], "r");
350               if (! in)
351                 FATAL_ERROR ("Can't read the file %s\n", argv[i]);
352             }
353           else if (out == stdout)
354             {
355               out = fopen (argv[i], "w");
356               if (! out)
357                 FATAL_ERROR ("Can't write the file %s\n", argv[i]);
358             }
359           else
360             help_exit (argv[0], 1);
361         }
362       else
363         help_exit (argv[0], 1);
364     }
365
366   /* Create an M-text to store the decoded characters.  */
367   mt = mtext ();
368
369   /* Create a converter for decoding.  */
370   converter = mconv_stream_converter (incode, in);
371   /* Instead of doing strict decoding, we decode all input bytes at
372      once, and check invalid bytes later by the fuction
373      check_invalid_bytes.  */
374   converter->lenient = 1;
375
376   mconv_decode (converter, mt);
377
378   if (! suppress_warning)
379     check_invalid_bytes (mt);
380   if (verbose)
381     fprintf (stderr, "%d bytes (%s) decoded into %d characters,\n",
382              converter->nbytes, msymbol_name (incode), mtext_len (mt));
383
384   mconv_free_converter (converter);
385
386   /* Create a converter for encoding.  */
387   converter = mconv_stream_converter (outcode, out);
388   /* Instead of doing strict encoding, we encode all characters at
389      once, and check unencoded characters later by the fuction
390      check_unencoded_chars.  */
391   converter->lenient = 1;
392   converter->last_block = 1;
393   if (mconv_encode (converter, mt) < 0
394       && ! suppress_warning)
395     fprintf (stderr, "I/O error on writing\n");
396   if (! suppress_warning)
397     check_unencoded_chars (mt, converter->nchars);
398   if (verbose)
399     fprintf (stderr, "%d characters encoded into %d bytes (%s).\n",
400              converter->nchars, converter->nbytes, msymbol_name (outcode));
401
402   /* Clear away.  */
403   mconv_free_converter (converter);
404   fclose (in);
405   fclose (out);
406   m17n_object_unref (mt);
407   M17N_FINI ();
408   exit (0);
409 }
410 #endif /* not FOR_DOXYGEN */