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