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