• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2011  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26 
27 #ifndef TEXT_OPTIONS_HH
28 #define TEXT_OPTIONS_HH
29 
30 #include "options.hh"
31 
32 struct text_options_t
33 {
text_options_ttext_options_t34   text_options_t ()
35   : gs (g_string_new (nullptr))
36   {}
~text_options_ttext_options_t37   ~text_options_t ()
38   {
39     g_free (text);
40     g_free (text_file);
41     if (gs)
42       g_string_free (gs, true);
43     if (in_fp && in_fp != stdin)
44       fclose (in_fp);
45   }
46 
47   void add_options (option_parser_t *parser);
48 
post_parsetext_options_t49   void post_parse (GError **error G_GNUC_UNUSED)
50   {
51     if (!text && !text_file)
52       text_file = g_strdup ("-");
53 
54     if (text && text_file)
55     {
56       g_set_error (error,
57 		   G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
58 		   "Only one of text and text-file can be set");
59       return;
60     }
61 
62     if (text_file)
63     {
64       if (0 != strcmp (text_file, "-"))
65 	in_fp = fopen (text_file, "r");
66       else
67 	in_fp = stdin;
68 
69       if (!in_fp)
70 	g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
71 		     "Failed opening text file `%s': %s",
72 		     text_file, strerror (errno));
73     }
74   }
75 
76   const char *get_line (unsigned int *len);
77 
78   int text_len = -1;
79   char *text = nullptr;
80   char *text_file = nullptr;
81 
82   private:
83   FILE *in_fp = nullptr;
84   GString *gs = nullptr;
85 };
86 
87 struct shape_text_options_t : text_options_t
88 {
~shape_text_options_tshape_text_options_t89   ~shape_text_options_t ()
90   {
91     g_free (text_before);
92     g_free (text_after);
93   }
94 
95   void add_options (option_parser_t *parser);
96 
97   char *text_before = nullptr;
98   char *text_after = nullptr;
99 };
100 
101 
102 static gboolean
parse_text(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)103 parse_text (const char *name G_GNUC_UNUSED,
104 	    const char *arg,
105 	    gpointer    data,
106 	    GError    **error G_GNUC_UNUSED)
107 {
108   text_options_t *text_opts = (text_options_t *) data;
109 
110   if (text_opts->text)
111   {
112     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
113 		 "Either --text or --unicodes can be provided but not both");
114     return false;
115   }
116 
117   text_opts->text_len = -1;
118   text_opts->text = g_strdup (arg);
119   return true;
120 }
121 
122 static bool
encode_unicodes(const char * unicodes,GString * gs,GError ** error)123 encode_unicodes (const char *unicodes,
124 		 GString    *gs,
125 		 GError    **error)
126 {
127 #define DELIMITERS "<+->{},;&#\\xXuUnNiI\n\t\v\f\r "
128 
129   char *s = (char *) unicodes;
130   char *p;
131 
132   while (s && *s)
133   {
134     while (*s && strchr (DELIMITERS, *s))
135       s++;
136     if (!*s)
137       break;
138 
139     errno = 0;
140     hb_codepoint_t u = strtoul (s, &p, 16);
141     if (errno || s == p)
142     {
143       g_string_free (gs, TRUE);
144       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
145 		   "Failed parsing Unicode value at: '%s'", s);
146       return false;
147     }
148 
149     g_string_append_unichar (gs, u);
150 
151     s = p;
152   }
153 
154 #undef DELIMITERS
155 
156   return true;
157 }
158 
159 static gboolean
parse_unicodes(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)160 parse_unicodes (const char *name G_GNUC_UNUSED,
161 		const char *arg,
162 		gpointer    data,
163 		GError    **error G_GNUC_UNUSED)
164 {
165   text_options_t *text_opts = (text_options_t *) data;
166 
167   if (text_opts->text)
168   {
169     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
170 		 "Either --text or --unicodes can be provided but not both");
171     return false;
172   }
173 
174   GString *gs = g_string_new (nullptr);
175   if (0 == strcmp (arg, "*"))
176     g_string_append_c (gs, '*');
177   else
178     if (!encode_unicodes (arg, gs, error))
179       return false;
180 
181   text_opts->text_len = gs->len;
182   text_opts->text = g_string_free (gs, FALSE);
183   return true;
184 }
185 
186 static gboolean
parse_text_before(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)187 parse_text_before (const char *name G_GNUC_UNUSED,
188 		   const char *arg,
189 		   gpointer    data,
190 		   GError    **error)
191 {
192   auto *opts = (shape_text_options_t *) data;
193 
194   if (opts->text_before)
195   {
196     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
197 		 "Either --text-before or --unicodes-before can be provided but not both");
198     return false;
199   }
200 
201   opts->text_before = g_strdup (arg);
202   fprintf(stderr, "%s\n", opts->text_before);
203   return true;
204 }
205 
206 static gboolean
parse_unicodes_before(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)207 parse_unicodes_before (const char *name G_GNUC_UNUSED,
208 		       const char *arg,
209 		       gpointer    data,
210 		       GError    **error)
211 {
212   auto *opts = (shape_text_options_t *) data;
213 
214   if (opts->text_before)
215   {
216     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
217 		 "Either --text-before or --unicodes-before can be provided but not both");
218     return false;
219   }
220 
221   GString *gs = g_string_new (nullptr);
222   if (!encode_unicodes (arg, gs, error))
223     return false;
224 
225   opts->text_before = g_string_free (gs, FALSE);
226   return true;
227 }
228 
229 static gboolean
parse_text_after(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)230 parse_text_after (const char *name G_GNUC_UNUSED,
231 		  const char *arg,
232 		  gpointer    data,
233 		  GError    **error)
234 {
235   auto *opts = (shape_text_options_t *) data;
236 
237   if (opts->text_after)
238   {
239     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
240 		 "Either --text-after or --unicodes-after can be provided but not both");
241     return false;
242   }
243 
244   opts->text_after = g_strdup (arg);
245   return true;
246 }
247 
248 static gboolean
parse_unicodes_after(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)249 parse_unicodes_after (const char *name G_GNUC_UNUSED,
250 		      const char *arg,
251 		      gpointer    data,
252 		      GError    **error)
253 {
254   auto *opts = (shape_text_options_t *) data;
255 
256   if (opts->text_after)
257   {
258     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
259 		 "Either --text-after or --unicodes-after can be provided but not both");
260     return false;
261   }
262 
263   GString *gs = g_string_new (nullptr);
264   if (!encode_unicodes (arg, gs, error))
265     return false;
266 
267   opts->text_after = g_string_free (gs, FALSE);
268   return true;
269 }
270 
271 const char *
get_line(unsigned int * len)272 text_options_t::get_line (unsigned int *len)
273 {
274   if (text)
275   {
276     if (text_len == -2)
277     {
278       *len = 0;
279       return nullptr;
280     }
281 
282     if (text_len == -1)
283       text_len = strlen (text);
284 
285     *len = text_len;
286     text_len = -2;
287     return text;
288   }
289 
290   g_string_set_size (gs, 0);
291   char buf[BUFSIZ];
292   while (fgets (buf, sizeof (buf), in_fp))
293   {
294     unsigned bytes = strlen (buf);
295     if (bytes && buf[bytes - 1] == '\n')
296     {
297       bytes--;
298       g_string_append_len (gs, buf, bytes);
299       break;
300     }
301     g_string_append_len (gs, buf, bytes);
302   }
303   if (ferror (in_fp))
304     fail (false, "Failed reading text: %s", strerror (errno));
305   *len = gs->len;
306   return !*len && feof (in_fp) ? nullptr : gs->str;
307 }
308 
309 void
add_options(option_parser_t * parser)310 text_options_t::add_options (option_parser_t *parser)
311 {
312   GOptionEntry entries[] =
313   {
314     {"text",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text,		"Set input text",			"string"},
315     {"text-file",	0, 0, G_OPTION_ARG_STRING,	&this->text_file,		"Set input text file-name",		"filename"},
316     {"unicodes",      'u', 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes,	"Set input Unicode codepoints\n\n    If no text is provided, standard input is used for input.",		"list of hex numbers"},
317     {nullptr}
318   };
319   parser->add_group (entries,
320 		     "text",
321 		     "Text options:",
322 		     "Options for the input text",
323 		     this);
324 }
325 
326 void
add_options(option_parser_t * parser)327 shape_text_options_t::add_options (option_parser_t *parser)
328 {
329   text_options_t::add_options (parser);
330 
331   GOptionEntry entries[] =
332   {
333     {"text-before",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text_before,		"Set text context before each line",	"string"},
334     {"text-after",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text_after,		"Set text context after each line",	"string"},
335     {"unicodes-before",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes_before,	"Set Unicode codepoints context before each line",	"list of hex numbers"},
336     {"unicodes-after",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes_after,	"Set Unicode codepoints context after each line",	"list of hex numbers"},
337     {nullptr}
338   };
339   parser->add_group (entries,
340 		     "text-context",
341 		     "Textual context options:",
342 		     "Options for the input context text",
343 		     this);
344 }
345 
346 #endif
347