Imported Upstream version 8.2.2
[platform/upstream/harfbuzz.git] / util / text-options.hh
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 {
34   text_options_t ()
35   : gs (g_string_new (nullptr))
36   {}
37   ~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
49   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 {
92   ~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
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
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
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
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
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
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
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 *
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
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
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