xref: /aosp_15_r20/external/harfbuzz_ng/util/text-options.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
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   char *line = nullptr;
86   unsigned line_len = UINT_MAX;
87   hb_bool_t single_par = false;
88 };
89 
90 struct shape_text_options_t : text_options_t
91 {
~shape_text_options_tshape_text_options_t92   ~shape_text_options_t ()
93   {
94     g_free (text_before);
95     g_free (text_after);
96   }
97 
98   void add_options (option_parser_t *parser);
99 
100   char *text_before = nullptr;
101   char *text_after = nullptr;
102 };
103 
104 
105 static gboolean
parse_text(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)106 parse_text (const char *name G_GNUC_UNUSED,
107 	    const char *arg,
108 	    gpointer    data,
109 	    GError    **error G_GNUC_UNUSED)
110 {
111   text_options_t *text_opts = (text_options_t *) data;
112 
113   if (text_opts->text)
114   {
115     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
116 		 "Either --text or --unicodes can be provided but not both");
117     return false;
118   }
119 
120   text_opts->text_len = -1;
121   text_opts->text = g_strdup (arg);
122   return true;
123 }
124 
125 static bool
encode_unicodes(const char * unicodes,GString * gs,GError ** error)126 encode_unicodes (const char *unicodes,
127 		 GString    *gs,
128 		 GError    **error)
129 {
130 #define DELIMITERS "<+-|>{},;&#\\xXuUnNiI\n\t\v\f\r "
131 
132   char *s = (char *) unicodes;
133   char *p;
134 
135   while (s && *s)
136   {
137     while (*s && strchr (DELIMITERS, *s))
138       s++;
139     if (!*s)
140       break;
141 
142     errno = 0;
143     hb_codepoint_t u = strtoul (s, &p, 16);
144     if (errno || s == p)
145     {
146       g_string_free (gs, TRUE);
147       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
148 		   "Failed parsing Unicode value at: '%s'", s);
149       return false;
150     }
151 
152     g_string_append_unichar (gs, u);
153 
154     s = p;
155   }
156 
157 #undef DELIMITERS
158 
159   return true;
160 }
161 
162 static gboolean
parse_unicodes(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)163 parse_unicodes (const char *name G_GNUC_UNUSED,
164 		const char *arg,
165 		gpointer    data,
166 		GError    **error G_GNUC_UNUSED)
167 {
168   text_options_t *text_opts = (text_options_t *) data;
169 
170   if (text_opts->text)
171   {
172     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
173 		 "Either --text or --unicodes can be provided but not both");
174     return false;
175   }
176 
177   GString *gs = g_string_new (nullptr);
178   if (0 == strcmp (arg, "*"))
179     g_string_append_c (gs, '*');
180   else
181     if (!encode_unicodes (arg, gs, error))
182       return false;
183 
184   text_opts->text_len = gs->len;
185   text_opts->text = g_string_free (gs, FALSE);
186   return true;
187 }
188 
189 static gboolean
parse_text_before(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)190 parse_text_before (const char *name G_GNUC_UNUSED,
191 		   const char *arg,
192 		   gpointer    data,
193 		   GError    **error)
194 {
195   auto *opts = (shape_text_options_t *) data;
196 
197   if (opts->text_before)
198   {
199     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
200 		 "Either --text-before or --unicodes-before can be provided but not both");
201     return false;
202   }
203 
204   opts->text_before = g_strdup (arg);
205   fprintf(stderr, "%s\n", opts->text_before);
206   return true;
207 }
208 
209 static gboolean
parse_unicodes_before(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)210 parse_unicodes_before (const char *name G_GNUC_UNUSED,
211 		       const char *arg,
212 		       gpointer    data,
213 		       GError    **error)
214 {
215   auto *opts = (shape_text_options_t *) data;
216 
217   if (opts->text_before)
218   {
219     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
220 		 "Either --text-before or --unicodes-before can be provided but not both");
221     return false;
222   }
223 
224   GString *gs = g_string_new (nullptr);
225   if (!encode_unicodes (arg, gs, error))
226     return false;
227 
228   opts->text_before = g_string_free (gs, FALSE);
229   return true;
230 }
231 
232 static gboolean
parse_text_after(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)233 parse_text_after (const char *name G_GNUC_UNUSED,
234 		  const char *arg,
235 		  gpointer    data,
236 		  GError    **error)
237 {
238   auto *opts = (shape_text_options_t *) data;
239 
240   if (opts->text_after)
241   {
242     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
243 		 "Either --text-after or --unicodes-after can be provided but not both");
244     return false;
245   }
246 
247   opts->text_after = g_strdup (arg);
248   return true;
249 }
250 
251 static gboolean
parse_unicodes_after(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error)252 parse_unicodes_after (const char *name G_GNUC_UNUSED,
253 		      const char *arg,
254 		      gpointer    data,
255 		      GError    **error)
256 {
257   auto *opts = (shape_text_options_t *) data;
258 
259   if (opts->text_after)
260   {
261     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
262 		 "Either --text-after or --unicodes-after can be provided but not both");
263     return false;
264   }
265 
266   GString *gs = g_string_new (nullptr);
267   if (!encode_unicodes (arg, gs, error))
268     return false;
269 
270   opts->text_after = g_string_free (gs, FALSE);
271   return true;
272 }
273 
274 const char *
get_line(unsigned int * len)275 text_options_t::get_line (unsigned int *len)
276 {
277   if (text)
278   {
279     if (!line)
280     {
281       line = text;
282       line_len = text_len;
283     }
284     if (line_len == UINT_MAX)
285       line_len = strlen (line);
286 
287     if (!line_len)
288     {
289       *len = 0;
290       return nullptr;
291     }
292 
293     const char *ret = line;
294     const char *p = single_par ? nullptr : (const char *) memchr (line, '\n', line_len);
295     unsigned int ret_len;
296     if (!p)
297     {
298       ret_len = line_len;
299       line += ret_len;
300       line_len = 0;
301     }
302     else
303     {
304       ret_len = p - ret;
305       line += ret_len + 1;
306       line_len -= ret_len + 1;
307     }
308 
309     *len = ret_len;
310     return ret;
311   }
312 
313   g_string_set_size (gs, 0);
314   char buf[BUFSIZ];
315   while (fgets (buf, sizeof (buf), in_fp))
316   {
317     unsigned bytes = strlen (buf);
318     if (!single_par && bytes && buf[bytes - 1] == '\n')
319     {
320       bytes--;
321       g_string_append_len (gs, buf, bytes);
322       break;
323     }
324     g_string_append_len (gs, buf, bytes);
325   }
326   if (ferror (in_fp))
327     fail (false, "Failed reading text: %s", strerror (errno));
328   *len = gs->len;
329   return !*len && feof (in_fp) ? nullptr : gs->str;
330 }
331 
332 void
add_options(option_parser_t * parser)333 text_options_t::add_options (option_parser_t *parser)
334 {
335   GOptionEntry entries[] =
336   {
337     {"text",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text,		"Set input text",			"string"},
338     {"text-file",	0, 0, G_OPTION_ARG_STRING,	&this->text_file,		"Set input text file-name",		"filename"},
339     {"unicodes",      'u', 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes,	"Set input Unicode codepoints",		"list of hex numbers"},
340     {"single-par",	0, 0, G_OPTION_ARG_NONE,	&this->single_par,		"Treat text as single paragraph",	nullptr},
341     {nullptr}
342   };
343   parser->add_group (entries,
344 		     "text",
345 		     "Text options:\n\nIf no text is provided, standard input is used for input.\n",
346 		     "Options for the input text",
347 		     this);
348 }
349 
350 void
add_options(option_parser_t * parser)351 shape_text_options_t::add_options (option_parser_t *parser)
352 {
353   text_options_t::add_options (parser);
354 
355   GOptionEntry entries[] =
356   {
357     {"text-before",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text_before,		"Set text context before each line",	"string"},
358     {"text-after",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text_after,		"Set text context after each line",	"string"},
359     {"unicodes-before",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes_before,	"Set Unicode codepoints context before each line",	"list of hex numbers"},
360     {"unicodes-after",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes_after,	"Set Unicode codepoints context after each line",	"list of hex numbers"},
361     {nullptr}
362   };
363   parser->add_group (entries,
364 		     "text-context",
365 		     "Textual context options:",
366 		     "Options for the input context text",
367 		     this);
368 }
369 
370 #endif
371