Imported Upstream version 1.0.2
[platform/upstream/fribidi.git] / bin / fribidi-main.c
1 /* FriBidi
2  * fribidi-main.c - command line program for libfribidi
3  *
4  * Authors:
5  *   Behdad Esfahbod, 2001, 2002, 2004
6  *   Dov Grobgeld, 1999, 2000
7  *
8  * Copyright (C) 2004 Sharif FarsiWeb, Inc
9  * Copyright (C) 2001,2002 Behdad Esfahbod
10  * Copyright (C) 1999,2000 Dov Grobgeld
11  * 
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  * 
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  * 
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this library, in a file named COPYING; if not, write to the
24  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA
26  * 
27  * For licensing issues, contact <fribidi.license@gmail.com>.
28  */
29
30 #include <common.h>
31
32 #include <fribidi.h>
33 #include <fribidi-deprecated.h>
34
35 #include <stdio.h>
36 #ifdef STDC_HEADERS
37 # include <stdlib.h>
38 # include <stddef.h>
39 #else
40 # if HAVE_STDLIB_H
41 #  include <stdlib.h>
42 # endif
43 #endif
44 #ifdef HAVE_STRING_H
45 # if STDC_HEADERS && HAVE_MEMORY_H
46 # else
47 #  include <memory.h>
48 # endif
49 # include <string.h>
50 #endif
51 #ifdef HAVE_STRINGS_H
52 # include <strings.h>
53 #endif
54
55 #include "getopt.h"
56
57 #define appname "fribidi"
58
59 #define MAX_STR_LEN 65000
60
61
62 #define ALLOCATE(tp,ln) ((tp *) fribidi_malloc (sizeof (tp) * (ln)))
63
64 static void
65 die2 (
66   const char *fmt,
67   const char *arg
68 )
69 {
70   fprintf (stderr, "%s: ", appname);
71   if (fmt)
72     fprintf (stderr, fmt, arg);
73   fprintf (stderr, "Try `%s --help' for more information.\n", appname);
74   exit (-1);
75 }
76
77 #define die1(msg) die2("%s", msg)
78
79 fribidi_boolean do_break, do_pad, do_mirror, do_reorder_nsm, do_clean;
80 fribidi_boolean show_input, show_visual, show_basedir;
81 fribidi_boolean show_ltov, show_vtol, show_levels;
82 const int default_text_width = 80;
83 int text_width;
84 const char *char_set;
85 const char *bol_text, *eol_text;
86 FriBidiParType input_base_direction;
87 int char_set_num;
88
89 static void
90 help (
91   void
92 )
93 {
94   /* Break help string into little ones, to assure ISO C89 conformance */
95   printf ("Usage: " appname " [OPTION]... [FILE]...\n"
96           "A command line interface for the " FRIBIDI_NAME " library.\n"
97           "Convert a logical string to visual.\n"
98           "\n"
99           "  -h, --help            Display this information and exit\n"
100           "  -V, --version         Display version information and exit\n"
101           "  -v, --verbose         Verbose mode, same as --basedir --ltov --vtol\n"
102           "                        --levels --changes\n");
103   printf ("  -d, --debug           Output debug information\n"
104           "  -t, --test            Test " FRIBIDI_NAME
105           ", same as --clean --nobreak\n"
106           "                        --showinput --reordernsm --width %d\n",
107           default_text_width);
108   printf ("  -c, --charset CS      Specify character set, default is %s\n"
109           "      --charsetdesc CS  Show descriptions for character set CS and exit\n"
110           "      --caprtl          Old style: set character set to CapRTL\n",
111           char_set);
112   printf ("      --showinput       Output the input string too\n"
113           "      --nopad           Do not right justify RTL lines\n"
114           "      --nobreak         Do not break long lines\n"
115           "  -w, --width W         Screen width for padding, default is %d, but if\n"
116           "                        environment variable COLUMNS is defined, its value\n"
117           "                        will be used, --width overrides both of them.\n",
118           default_text_width);
119   printf
120     ("  -B, --bol BOL         Output string BOL before the visual string\n"
121      "  -E, --eol EOL         Output string EOL after the visual string\n"
122      "      --rtl             Force base direction to RTL\n"
123      "      --ltr             Force base direction to LTR\n"
124      "      --wrtl            Set base direction to RTL if no strong character found\n");
125   printf
126     ("      --wltr            Set base direction to LTR if no strong character found\n"
127      "                        (default)\n"
128      "      --nomirror        Turn mirroring off, to do it later\n"
129      "      --reordernsm      Reorder NSM sequences to follow their base character\n"
130      "      --clean           Remove explicit format codes in visual string\n"
131      "                        output, currently does not affect other outputs\n"
132      "      --basedir         Output Base Direction\n");
133   printf ("      --ltov            Output Logical to Visual position map\n"
134           "      --vtol            Output Visual to Logical position map\n"
135           "      --levels          Output Embedding Levels\n"
136           "      --novisual        Do not output the visual string, to be used with\n"
137           "                        --basedir, --ltov, --vtol, --levels, --changes\n");
138   printf ("  All string indexes are zero based\n" "\n" "Output:\n"
139           "  For each line of input, output something like this:\n"
140           "    [input-str` => '][BOL][[padding space]visual-str][EOL]\n"
141           "    [\\n base-dir][\\n ltov-map][\\n vtol-map][\\n levels][\\n changes]\n");
142
143   {
144     int i;
145     printf ("\n" "Available character sets:\n");
146     for (i = 1; i <= FRIBIDI_CHAR_SETS_NUM; i++)
147       printf ("  * %-10s: %-25s%1s\n",
148               fribidi_char_set_name (i), fribidi_char_set_title (i),
149               (fribidi_char_set_desc (i) ? "X" : ""));
150     printf
151       ("  X: Character set has descriptions, use --charsetdesc to see\n");
152   }
153
154   printf ("\nReport bugs online at\n<" FRIBIDI_BUGREPORT ">.\n");
155   exit (0);
156 }
157
158 static void
159 version (
160   void
161 )
162 {
163   printf (appname " %s", fribidi_version_info);
164   exit (0);
165 }
166
167 static char *
168 my_fribidi_strdup (
169   char *s
170 )
171 {
172   char *m;
173
174   m = fribidi_malloc (strlen (s) + 1);
175   if (!m)
176     return NULL;
177
178   strcpy (m, s);
179
180   return m;
181 }
182
183 int
184 main (
185   int argc,
186   char *argv[]
187 )
188 {
189   int exit_val;
190   fribidi_boolean file_found;
191   char *s;
192   FILE *IN;
193
194   text_width = default_text_width;
195   do_break = true;
196   do_pad = true;
197   do_mirror = true;
198   do_clean = false;
199   do_reorder_nsm = false;
200   show_input = false;
201   show_visual = true;
202   show_basedir = false;
203   show_ltov = false;
204   show_vtol = false;
205   show_levels = false;
206   char_set = "UTF-8";
207   bol_text = NULL;
208   eol_text = NULL;
209   input_base_direction = FRIBIDI_PAR_ON;
210
211   if ((s = (char *) getenv ("COLUMNS")))
212     {
213       int i;
214
215       i = atoi (s);
216       if (i > 0)
217         text_width = i;
218     }
219
220 #define CHARSETDESC 257
221 #define CAPRTL 258
222
223   /* Parse the command line with getopt library */
224   /* Must set argv[0], getopt uses it to generate error messages */
225   argv[0] = appname;
226   while (1)
227     {
228       int option_index = 0, c;
229       static struct option long_options[] = {
230         {"help", 0, 0, 'h'},
231         {"version", 0, 0, 'V'},
232         {"verbose", 0, 0, 'v'},
233         {"debug", 0, 0, 'd'},
234         {"test", 0, 0, 't'},
235         {"charset", 1, 0, 'c'},
236         {"charsetdesc", 1, 0, CHARSETDESC},
237         {"caprtl", 0, 0, CAPRTL},
238         {"showinput", 0, (int *) (void *) &show_input, true},
239         {"nopad", 0, (int *) (void *) &do_pad, false},
240         {"nobreak", 0, (int *) (void *) &do_break, false},
241         {"width", 1, 0, 'w'},
242         {"bol", 1, 0, 'B'},
243         {"eol", 1, 0, 'E'},
244         {"nomirror", 0, (int *) (void *) &do_mirror, false},
245         {"reordernsm", 0, (int *) (void *) &do_reorder_nsm, true},
246         {"clean", 0, (int *) (void *) &do_clean, true},
247         {"ltr", 0, (int *) (void *) &input_base_direction, FRIBIDI_PAR_LTR},
248         {"rtl", 0, (int *) (void *) &input_base_direction, FRIBIDI_PAR_RTL},
249         {"wltr", 0, (int *) (void *) &input_base_direction,
250          FRIBIDI_PAR_WLTR},
251         {"wrtl", 0, (int *) (void *) &input_base_direction,
252          FRIBIDI_PAR_WRTL},
253         {"basedir", 0, (int *) (void *) &show_basedir, true},
254         {"ltov", 0, (int *) (void *) &show_ltov, true},
255         {"vtol", 0, (int *) (void *) &show_vtol, true},
256         {"levels", 0, (int *) (void *) &show_levels, true},
257         {"novisual", 0, (int *) (void *) &show_visual, false},
258         {0, 0, 0, 0}
259       };
260
261       c =
262         getopt_long (argc, argv, "hVvdtc:w:B:E:", long_options,
263                      &option_index);
264       if (c == -1)
265         break;
266
267       switch (c)
268         {
269         case 0:
270           break;
271         case 'h':
272           help ();
273           break;
274         case 'V':
275           version ();
276           break;
277         case 'v':
278           show_basedir = show_ltov = show_vtol = show_levels = true;
279           break;
280         case 'w':
281           text_width = atoi (optarg);
282           if (text_width <= 0)
283             die2 ("invalid screen width `%s'\n", optarg);
284           break;
285         case 'B':
286           bol_text = optarg;
287           break;
288         case 'E':
289           eol_text = optarg;
290           break;
291         case 'd':
292           if (!fribidi_set_debug (true))
293             die1
294               ("lib" FRIBIDI
295                " must be compiled with DEBUG option to enable\nturn debug info on.\n");
296           break;
297         case 't':
298           do_clean = show_input = do_reorder_nsm = true;
299           do_break = false;
300           text_width = default_text_width;
301           break;
302         case 'c':
303           char_set = my_fribidi_strdup (optarg);
304           if (!char_set)
305             die1 ("memory allocation failed for char_set!");
306           break;
307         case CAPRTL:
308           char_set = "CapRTL";
309           break;
310         case CHARSETDESC:
311           char_set = optarg;
312           char_set_num = fribidi_parse_charset (char_set);
313           if (!char_set_num)
314             die2 ("unrecognized character set `%s'\n", char_set);
315           if (!fribidi_char_set_desc (char_set_num))
316             die2 ("no description available for character set `%s'\n",
317                   fribidi_char_set_name (char_set_num));
318           else
319             printf ("Descriptions for character set %s:\n"
320                     "\n" "%s", fribidi_char_set_title (char_set_num),
321                     fribidi_char_set_desc (char_set_num));
322           exit (0);
323           break;
324         case ':':
325         case '?':
326           die2 (NULL, NULL);
327           break;
328         default:
329           break;
330         }
331     }
332
333   char_set_num = fribidi_parse_charset (char_set);
334
335   if (!char_set_num)
336     die2 ("unrecognized character set `%s'\n", char_set);
337
338 FRIBIDI_BEGIN_IGNORE_DEPRECATIONS
339   fribidi_set_mirroring (do_mirror);
340   fribidi_set_reorder_nsm (do_reorder_nsm);
341 FRIBIDI_END_IGNORE_DEPRECATIONS
342   exit_val = 0;
343   file_found = false;
344   while (optind < argc || !file_found)
345     {
346       const char *filename;
347
348       filename = optind < argc ? argv[optind++] : "-";
349       file_found = true;
350
351       /* Open the infile for reading */
352       if (filename[0] == '-' && !filename[1])
353         {
354           IN = stdin;
355         }
356       else
357         {
358           IN = fopen (filename, "r");
359           if (!IN)
360             {
361               fprintf (stderr, "%s: %s: no such file or directory\n",
362                        appname, filename);
363               exit_val = 1;
364               continue;
365             }
366         }
367
368       /* Read and process input one line at a time */
369       {
370         char S_[MAX_STR_LEN];
371         int padding_width, break_width;
372
373         padding_width = show_input ? (text_width - 10) / 2 : text_width;
374         break_width = do_break ? padding_width : 3 * MAX_STR_LEN;
375
376         while (fgets (S_, sizeof (S_) - 1, IN))
377           {
378             const char *new_line, *nl_found;
379             FriBidiChar logical[MAX_STR_LEN];
380             char outstring[MAX_STR_LEN];
381             FriBidiParType base;
382             FriBidiStrIndex len;
383
384             nl_found = "";
385             S_[sizeof (S_) - 1] = 0;
386             len = strlen (S_);
387             /* chop */
388             if (S_[len - 1] == '\n')
389               {
390                 len--;
391                 S_[len] = '\0';
392                 new_line = "\n";
393               }
394             else
395               new_line = "";
396             /* TODO: handle \r */
397
398             len = fribidi_charset_to_unicode (char_set_num, S_, len, logical);
399
400             {
401               FriBidiChar *visual;
402               FriBidiStrIndex *ltov, *vtol;
403               FriBidiLevel *levels;
404               fribidi_boolean log2vis;
405
406               visual = show_visual ? ALLOCATE (FriBidiChar,
407                                                len + 1
408               ) : NULL;
409               ltov = show_ltov ? ALLOCATE (FriBidiStrIndex,
410                                            len + 1
411               ) : NULL;
412               vtol = show_vtol ? ALLOCATE (FriBidiStrIndex,
413                                            len + 1
414               ) : NULL;
415               levels = show_levels ? ALLOCATE (FriBidiLevel,
416                                                len + 1
417               ) : NULL;
418
419               /* Create a bidi string. */
420               base = input_base_direction;
421 FRIBIDI_BEGIN_IGNORE_DEPRECATIONS
422               log2vis = fribidi_log2vis (logical, len, &base,
423                                          /* output */
424                                          visual, ltov, vtol, levels);
425 FRIBIDI_END_IGNORE_DEPRECATIONS
426               if (log2vis)
427                 {
428
429                   if (show_input)
430                     printf ("%-*s => ", padding_width, S_);
431
432                   /* Remove explicit marks, if asked for. */
433 FRIBIDI_BEGIN_IGNORE_DEPRECATIONS
434                   if (do_clean)
435                     len =
436                       fribidi_remove_bidi_marks (visual, len, ltov, vtol,
437                                                  levels);
438 FRIBIDI_END_IGNORE_DEPRECATIONS
439
440                   if (show_visual)
441                     {
442                       printf ("%s", nl_found);
443
444                       if (bol_text)
445                         printf ("%s", bol_text);
446
447                       /* Convert it to input charset and print. */
448                       {
449                         FriBidiStrIndex idx, st;
450                         for (idx = 0; idx < len;)
451                           {
452                             FriBidiStrIndex wid, inlen;
453
454                             wid = break_width;
455                             st = idx;
456                             if (char_set_num != FRIBIDI_CHAR_SET_CAP_RTL)
457                               while (wid > 0 && idx < len)
458                                 {
459                                   wid -=
460                                     FRIBIDI_IS_EXPLICIT_OR_ISOLATE_OR_BN_OR_NSM
461                                     (fribidi_get_bidi_type (visual[idx])) ? 0
462                                     : 1;
463                                   idx++;
464                                 }
465                             else
466                               while (wid > 0 && idx < len)
467                                 {
468                                   wid--;
469                                   idx++;
470                                 }
471                             if (wid < 0 && idx - st > 1)
472                               idx--;
473                             inlen = idx - st;
474
475                             fribidi_unicode_to_charset (char_set_num,
476                                                         visual + st, inlen,
477                                                         outstring);
478                             if (FRIBIDI_IS_RTL (base))
479                               printf ("%*s",
480                                       (int) (do_pad ? (padding_width +
481                                                        strlen (outstring) -
482                                                        (break_width -
483                                                         wid)) : 0),
484                                       outstring);
485                             else
486                               printf ("%s", outstring);
487                             if (idx < len)
488                               printf ("\n");
489                           }
490                       }
491                       if (eol_text)
492                         printf ("%s", eol_text);
493
494                       nl_found = "\n";
495                     }
496                   if (show_basedir)
497                     {
498                       printf ("%s", nl_found);
499                       printf ("Base direction: %s",
500                               (FRIBIDI_DIR_TO_LEVEL (base) ? "R" : "L"));
501                       nl_found = "\n";
502                     }
503                   if (show_ltov)
504                     {
505                       FriBidiStrIndex i;
506
507                       printf ("%s", nl_found);
508                       for (i = 0; i < len; i++)
509                         printf ("%ld ", (long) ltov[i]);
510                       nl_found = "\n";
511                     }
512                   if (show_vtol)
513                     {
514                       FriBidiStrIndex i;
515
516                       printf ("%s", nl_found);
517                       for (i = 0; i < len; i++)
518                         printf ("%ld ", (long) vtol[i]);
519                       nl_found = "\n";
520                     }
521                   if (show_levels)
522                     {
523                       FriBidiStrIndex i;
524
525                       printf ("%s", nl_found);
526                       for (i = 0; i < len; i++)
527                         printf ("%d ", (int) levels[i]);
528                       nl_found = "\n";
529                     }
530                 }
531               else
532                 {
533                   exit_val = 2;
534                 }
535
536               if (show_visual)
537                 free (visual);
538               if (show_ltov)
539                 free (ltov);
540               if (show_vtol)
541                 free (vtol);
542               if (show_levels)
543                 free (levels);
544             }
545
546             if (*nl_found)
547               printf ("%s", new_line);
548           }
549       }
550     }
551
552   return exit_val;
553 }
554
555 /* Editor directions:
556  * vim:textwidth=78:tabstop=8:shiftwidth=2:autoindent:cindent
557  */