Mercurial > pidgin.yaz
comparison libpurple/protocols/novell/nmrtf.c @ 15374:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | 32c366eeeb99 |
comparison
equal
deleted
inserted
replaced
15373:f79e0f4df793 | 15374:5fe8042783c1 |
---|---|
1 /* | |
2 * nmrtf.c | |
3 * | |
4 * Copyright (c) 2004 Novell, Inc. All Rights Reserved. | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; version 2 of the License. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 * GNU General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU General Public License | |
16 * along with this program; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 * | |
19 */ | |
20 | |
21 /* This code was adapted from the sample RTF reader found here: | |
22 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnrtfspec/html/rtfspec.asp | |
23 */ | |
24 | |
25 #include <glib.h> | |
26 #include <stdlib.h> | |
27 #include <stdio.h> | |
28 #include <stddef.h> | |
29 #include <ctype.h> | |
30 #include <string.h> | |
31 #include "nmrtf.h" | |
32 #include "debug.h" | |
33 | |
34 /* Internal RTF parser error codes */ | |
35 #define NMRTF_OK 0 /* Everything's fine! */ | |
36 #define NMRTF_STACK_UNDERFLOW 1 /* Unmatched '}' */ | |
37 #define NMRTF_STACK_OVERFLOW 2 /* Too many '{' -- memory exhausted */ | |
38 #define NMRTF_UNMATCHED_BRACE 3 /* RTF ended during an open group. */ | |
39 #define NMRTF_INVALID_HEX 4 /* invalid hex character found in data */ | |
40 #define NMRTF_BAD_TABLE 5 /* RTF table (sym or prop) invalid */ | |
41 #define NMRTF_ASSERTION 6 /* Assertion failure */ | |
42 #define NMRTF_EOF 7 /* End of file reached while reading RTF */ | |
43 #define NMRTF_CONVERT_ERROR 8 /* Error converting text */ | |
44 | |
45 #define NMRTF_MAX_DEPTH 256 | |
46 | |
47 typedef enum | |
48 { | |
49 NMRTF_STATE_NORMAL, | |
50 NMRTF_STATE_SKIP, | |
51 NMRTF_STATE_FONTTABLE, | |
52 NMRTF_STATE_BIN, | |
53 NMRTF_STATE_HEX | |
54 } NMRtfState; /* Rtf State */ | |
55 | |
56 /* Property types that we care about */ | |
57 typedef enum | |
58 { | |
59 NMRTF_PROP_FONT_IDX, | |
60 NMRTF_PROP_FONT_CHARSET, | |
61 NMRTF_PROP_MAX | |
62 } NMRtfProperty; | |
63 | |
64 typedef enum | |
65 { | |
66 NMRTF_SPECIAL_BIN, | |
67 NMRTF_SPECIAL_HEX, | |
68 NMRTF_SPECIAL_UNICODE, | |
69 NMRTF_SPECIAL_SKIP | |
70 } NMRtfSpecialKwd; | |
71 | |
72 typedef enum | |
73 { | |
74 NMRTF_DEST_FONTTABLE, | |
75 NMRTF_DEST_SKIP | |
76 } NMRtfDestinationType; | |
77 | |
78 typedef enum | |
79 { | |
80 NMRTF_KWD_CHAR, | |
81 NMRTF_KWD_DEST, | |
82 NMRTF_KWD_PROP, | |
83 NMRTF_KWD_SPEC | |
84 } NMRtfKeywordType; | |
85 | |
86 typedef struct _NMRTFCharProp | |
87 { | |
88 /* All we care about for now is the font. | |
89 * bold, italic, underline, etc. should be | |
90 * added here | |
91 */ | |
92 int font_idx; | |
93 int font_charset; | |
94 } NMRtfCharProp; | |
95 | |
96 typedef struct _NMRtfStateSave | |
97 { | |
98 NMRtfCharProp chp; | |
99 NMRtfState rds; | |
100 NMRtfState ris; | |
101 } NMRtfStateSave; | |
102 | |
103 typedef struct _NMRtfSymbol | |
104 { | |
105 char *keyword; /* RTF keyword */ | |
106 int default_val; /* default value to use */ | |
107 gboolean pass_default; /* true to use default value from this table */ | |
108 NMRtfKeywordType kwd_type; /* the type of the keyword */ | |
109 int action; /* property type if the keyword represents a property */ | |
110 /* destination type if the keyword represents a destination */ | |
111 /* character to print if the keyword represents a character */ | |
112 } NMRtfSymbol; | |
113 | |
114 | |
115 typedef struct _NMRtfFont | |
116 { | |
117 int number; | |
118 char *name; | |
119 int charset; | |
120 } NMRtfFont; | |
121 | |
122 /* RTF Context */ | |
123 struct _NMRtfContext | |
124 { | |
125 NMRtfState rds; /* destination state */ | |
126 NMRtfState ris; /* internal state */ | |
127 NMRtfCharProp chp; /* current character properties (ie. font, bold, italic, etc.) */ | |
128 GSList *font_table; /* the font table */ | |
129 GSList *saved; /* saved state stack */ | |
130 int param; /* numeric parameter for the current keyword */ | |
131 long bytes_to_skip; /* number of bytes to skip (after encountering \bin) */ | |
132 int depth; /* how many groups deep are we */ | |
133 gboolean skip_unknown; /* if true, skip any unknown destinations (this is set after encountering '\*') */ | |
134 char *input; /* input string */ | |
135 char nextch; /* next char in input */ | |
136 GString *ansi; /* Temporary ansi text, will be convert/flushed to the output string */ | |
137 GString *output; /* The plain text UTF8 string */ | |
138 }; | |
139 | |
140 static int rtf_parse(NMRtfContext *ctx); | |
141 static int rtf_push_state(NMRtfContext *ctx); | |
142 static int rtf_pop_state(NMRtfContext *ctx); | |
143 static NMRtfFont *rtf_get_font(NMRtfContext *ctx, int index); | |
144 static int rtf_get_char(NMRtfContext *ctx, guchar *ch); | |
145 static int rtf_unget_char(NMRtfContext *ctx, guchar ch); | |
146 static int rtf_flush_data(NMRtfContext *ctx); | |
147 static int rtf_parse_keyword(NMRtfContext *ctx); | |
148 static int rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set); | |
149 static int rtf_dispatch_char(NMRtfContext *ctx, guchar ch); | |
150 static int rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch); | |
151 static int rtf_print_char(NMRtfContext *ctx, guchar ch); | |
152 static int rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch); | |
153 static int rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType dest); | |
154 static int rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd special); | |
155 static int rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val); | |
156 | |
157 /* RTF parser tables */ | |
158 | |
159 /* Keyword descriptions */ | |
160 NMRtfSymbol rtf_symbols[] = { | |
161 /* keyword, default, pass_default, keyword_type, action */ | |
162 {"fonttbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_FONTTABLE}, | |
163 {"f", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_IDX}, | |
164 {"fcharset", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_CHARSET}, | |
165 {"par", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, | |
166 {"line", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, | |
167 {"\0x0a", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, | |
168 {"\0x0d", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, | |
169 {"tab", 0, FALSE, NMRTF_KWD_CHAR, 0x09}, | |
170 {"\r", 0, FALSE, NMRTF_KWD_CHAR, '\r'}, | |
171 {"\n", 0, FALSE, NMRTF_KWD_CHAR, '\n'}, | |
172 {"ldblquote",0, FALSE, NMRTF_KWD_CHAR, '"'}, | |
173 {"rdblquote",0, FALSE, NMRTF_KWD_CHAR, '"'}, | |
174 {"{", 0, FALSE, NMRTF_KWD_CHAR, '{'}, | |
175 {"}", 0, FALSE, NMRTF_KWD_CHAR, '}'}, | |
176 {"\\", 0, FALSE, NMRTF_KWD_CHAR, '\\'}, | |
177 {"bin", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_BIN}, | |
178 {"*", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_SKIP}, | |
179 {"'", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_HEX}, | |
180 {"u", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_UNICODE}, | |
181 {"colortbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
182 {"author", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
183 {"buptim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
184 {"comment", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
185 {"creatim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
186 {"doccomm", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
187 {"footer", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
188 {"footerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
189 {"footerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
190 {"footerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
191 {"footnote", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
192 {"ftncn", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
193 {"ftnsep", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
194 {"ftnsepc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
195 {"header", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
196 {"headerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
197 {"headerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
198 {"headerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
199 {"info", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
200 {"keywords", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
201 {"operator", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
202 {"pict", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
203 {"printim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
204 {"private1", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
205 {"revtim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
206 {"rxe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
207 {"stylesheet", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
208 {"subject", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
209 {"tc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
210 {"title", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
211 {"txe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, | |
212 {"xe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP} | |
213 }; | |
214 int table_size = sizeof(rtf_symbols) / sizeof(NMRtfSymbol); | |
215 | |
216 NMRtfContext * | |
217 nm_rtf_init() | |
218 { | |
219 NMRtfContext *ctx = g_new0(NMRtfContext, 1); | |
220 ctx->nextch = -1; | |
221 ctx->ansi = g_string_new(""); | |
222 ctx->output = g_string_new(""); | |
223 return ctx; | |
224 } | |
225 | |
226 char * | |
227 nm_rtf_strip_formatting(NMRtfContext *ctx, const char *input) | |
228 { | |
229 int status; | |
230 | |
231 ctx->input = (char *)input; | |
232 status = rtf_parse(ctx); | |
233 if (status == NMRTF_OK) | |
234 return g_strdup(ctx->output->str); | |
235 | |
236 gaim_debug_info("novell", "RTF parser failed with error code %d", status); | |
237 return NULL; | |
238 } | |
239 | |
240 void | |
241 nm_rtf_deinit(NMRtfContext *ctx) | |
242 { | |
243 GSList *node; | |
244 NMRtfFont *font; | |
245 NMRtfStateSave *save; | |
246 | |
247 if (ctx) { | |
248 for (node = ctx->font_table; node; node = node->next) { | |
249 font = node->data; | |
250 g_free(font->name); | |
251 g_free(font); | |
252 node->data = NULL; | |
253 } | |
254 g_slist_free(ctx->font_table); | |
255 for (node = ctx->saved; node; node = node->next) { | |
256 save = node->data; | |
257 g_free(save); | |
258 node->data = NULL; | |
259 } | |
260 g_slist_free(ctx->saved); | |
261 g_string_free(ctx->ansi, TRUE); | |
262 g_string_free(ctx->output, TRUE); | |
263 g_free(ctx); | |
264 } | |
265 } | |
266 | |
267 static const char * | |
268 get_current_encoding(NMRtfContext *ctx) | |
269 { | |
270 NMRtfFont *font; | |
271 | |
272 font = rtf_get_font(ctx, ctx->chp.font_idx); | |
273 | |
274 switch (font->charset) { | |
275 case 0: | |
276 return "CP1252"; | |
277 case 77: | |
278 return "MACINTOSH"; | |
279 case 78: | |
280 return "SJIS"; | |
281 case 128: | |
282 return "CP932"; | |
283 case 129: | |
284 return "CP949"; | |
285 case 130: | |
286 return "CP1361"; | |
287 case 134: | |
288 return "CP936"; | |
289 case 136: | |
290 return "CP950"; | |
291 case 161: | |
292 return "CP1253"; | |
293 case 162: | |
294 return "CP1254"; | |
295 case 163: | |
296 return "CP1258"; | |
297 case 181: | |
298 case 177: | |
299 return "CP1255"; | |
300 case 178: | |
301 case 179: | |
302 case 180: | |
303 return "CP1256"; | |
304 case 186: | |
305 return "CP1257"; | |
306 case 204: | |
307 return "CP1251"; | |
308 case 222: | |
309 return "CP874"; | |
310 case 238: | |
311 return "CP1250"; | |
312 case 254: | |
313 return "CP437"; | |
314 default: | |
315 gaim_debug_info("novell", "Unhandled font charset %d\n", font->charset); | |
316 return "CP1252"; | |
317 } | |
318 return "CP1252"; | |
319 } | |
320 | |
321 | |
322 /* | |
323 * Add an entry to the font table | |
324 */ | |
325 static int | |
326 rtf_add_font_entry(NMRtfContext *ctx, int number, const char *name, int charset) | |
327 { | |
328 NMRtfFont *font = g_new0(NMRtfFont, 1); | |
329 | |
330 font->number = number; | |
331 font->name = g_strdup(name); | |
332 font->charset = charset; | |
333 | |
334 gaim_debug_info("novell", "Adding font to table: #%d\t%s\t%d\n", | |
335 font->number, font->name, font->charset); | |
336 | |
337 ctx->font_table = g_slist_append(ctx->font_table, font); | |
338 | |
339 return NMRTF_OK; | |
340 } | |
341 | |
342 /* | |
343 * Return the nth entry in the font table | |
344 */ | |
345 static NMRtfFont * | |
346 rtf_get_font(NMRtfContext *ctx, int nth) | |
347 { | |
348 NMRtfFont *font; | |
349 | |
350 font = g_slist_nth_data(ctx->font_table, nth); | |
351 | |
352 return font; | |
353 } | |
354 | |
355 /* | |
356 * Step 1: | |
357 * Isolate RTF keywords and send them to rtf_parse_keyword; | |
358 * Push and pop state at the start and end of RTF groups; | |
359 * Send text to rtf_dispatch_char for further processing. | |
360 */ | |
361 static int | |
362 rtf_parse(NMRtfContext *ctx) | |
363 { | |
364 int status; | |
365 guchar ch; | |
366 guchar hex_byte = 0; | |
367 int hex_count = 2; | |
368 int len; | |
369 | |
370 if (ctx->input == NULL) | |
371 return NMRTF_OK; | |
372 | |
373 while (rtf_get_char(ctx, &ch) == NMRTF_OK) { | |
374 if (ctx->depth < 0) | |
375 return NMRTF_STACK_UNDERFLOW; | |
376 | |
377 /* if we're parsing binary data, handle it directly */ | |
378 if (ctx->ris == NMRTF_STATE_BIN) { | |
379 if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK) | |
380 return status; | |
381 } else { | |
382 switch (ch) { | |
383 case '{': | |
384 if (ctx->depth > NMRTF_MAX_DEPTH) | |
385 return NMRTF_STACK_OVERFLOW; | |
386 rtf_flush_data(ctx); | |
387 if ((status = rtf_push_state(ctx)) != NMRTF_OK) | |
388 return status; | |
389 break; | |
390 case '}': | |
391 rtf_flush_data(ctx); | |
392 | |
393 /* for some reason there is always an unwanted '\par' at the end */ | |
394 if (ctx->rds == NMRTF_STATE_NORMAL) { | |
395 len = ctx->output->len; | |
396 if (ctx->output->str[len-1] == '\n') | |
397 ctx->output = g_string_truncate(ctx->output, len-1); | |
398 } | |
399 | |
400 if ((status = rtf_pop_state(ctx)) != NMRTF_OK) | |
401 return status; | |
402 | |
403 if (ctx->depth < 0) | |
404 return NMRTF_STACK_OVERFLOW; | |
405 break; | |
406 case '\\': | |
407 if ((status = rtf_parse_keyword(ctx)) != NMRTF_OK) | |
408 return status; | |
409 break; | |
410 case 0x0d: | |
411 case 0x0a: /* cr and lf are noise characters... */ | |
412 break; | |
413 default: | |
414 if (ctx->ris == NMRTF_STATE_NORMAL) { | |
415 if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK) | |
416 return status; | |
417 } else { /* parsing a hex encoded character */ | |
418 if (ctx->ris != NMRTF_STATE_HEX) | |
419 return NMRTF_ASSERTION; | |
420 | |
421 hex_byte = hex_byte << 4; | |
422 if (isdigit(ch)) | |
423 hex_byte += (char) ch - '0'; | |
424 else { | |
425 if (islower(ch)) { | |
426 if (ch < 'a' || ch > 'f') | |
427 return NMRTF_INVALID_HEX; | |
428 hex_byte += (char) ch - 'a' + 10; | |
429 } else { | |
430 if (ch < 'A' || ch > 'F') | |
431 return NMRTF_INVALID_HEX; | |
432 hex_byte += (char) ch - 'A' + 10; | |
433 } | |
434 } | |
435 hex_count--; | |
436 if (hex_count == 0) { | |
437 if ((status = rtf_dispatch_char(ctx, hex_byte)) != NMRTF_OK) | |
438 return status; | |
439 hex_count = 2; | |
440 hex_byte = 0; | |
441 ctx->ris = NMRTF_STATE_NORMAL; | |
442 } | |
443 } | |
444 break; | |
445 } | |
446 } | |
447 } | |
448 if (ctx->depth < 0) | |
449 return NMRTF_STACK_OVERFLOW; | |
450 if (ctx->depth > 0) | |
451 return NMRTF_UNMATCHED_BRACE; | |
452 return NMRTF_OK; | |
453 } | |
454 | |
455 /* | |
456 * Push the current state onto stack | |
457 */ | |
458 static int | |
459 rtf_push_state(NMRtfContext *ctx) | |
460 { | |
461 NMRtfStateSave *save = g_new0(NMRtfStateSave, 1); | |
462 save->chp = ctx->chp; | |
463 save->rds = ctx->rds; | |
464 save->ris = ctx->ris; | |
465 ctx->saved = g_slist_prepend(ctx->saved, save); | |
466 ctx->ris = NMRTF_STATE_NORMAL; | |
467 (ctx->depth)++; | |
468 return NMRTF_OK; | |
469 } | |
470 | |
471 /* | |
472 * Restore the state at the top of the stack | |
473 */ | |
474 static int | |
475 rtf_pop_state(NMRtfContext *ctx) | |
476 { | |
477 NMRtfStateSave *save_old; | |
478 GSList *link_old; | |
479 | |
480 if (ctx->saved == NULL) | |
481 return NMRTF_STACK_UNDERFLOW; | |
482 | |
483 save_old = ctx->saved->data; | |
484 ctx->chp = save_old->chp; | |
485 ctx->rds = save_old->rds; | |
486 ctx->ris = save_old->ris; | |
487 (ctx->depth)--; | |
488 | |
489 g_free(save_old); | |
490 link_old = ctx->saved; | |
491 ctx->saved = g_slist_remove_link(ctx->saved, link_old); | |
492 g_slist_free_1(link_old); | |
493 return NMRTF_OK; | |
494 } | |
495 | |
496 /* | |
497 * Step 2: | |
498 * Get a control word (and its associated value) and | |
499 * dispatch the control. | |
500 */ | |
501 static int | |
502 rtf_parse_keyword(NMRtfContext *ctx) | |
503 { | |
504 int status = NMRTF_OK; | |
505 guchar ch; | |
506 gboolean param_set = FALSE; | |
507 gboolean is_neg = FALSE; | |
508 int param = 0; | |
509 char keyword[30]; | |
510 char parameter[20]; | |
511 int i; | |
512 | |
513 keyword[0] = '\0'; | |
514 parameter[0] = '\0'; | |
515 if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK) | |
516 return status; | |
517 | |
518 if (!isalpha(ch)) { | |
519 /* a control symbol; no delimiter. */ | |
520 keyword[0] = (char) ch; | |
521 keyword[1] = '\0'; | |
522 return rtf_dispatch_control(ctx, keyword, 0, param_set); | |
523 } | |
524 | |
525 /* parse keyword */ | |
526 for (i = 0; isalpha(ch) && (i < sizeof(keyword) - 1); rtf_get_char(ctx, &ch)) { | |
527 keyword[i] = (char) ch; | |
528 i++; | |
529 } | |
530 keyword[i] = '\0'; | |
531 | |
532 /* check for '-' indicated a negative parameter value */ | |
533 if (ch == '-') { | |
534 is_neg = TRUE; | |
535 if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK) | |
536 return status; | |
537 } | |
538 | |
539 /* check for numerical param */ | |
540 if (isdigit(ch)) { | |
541 | |
542 param_set = TRUE; | |
543 for (i = 0; isdigit(ch) && (i < sizeof(parameter) - 1); rtf_get_char(ctx, &ch)) { | |
544 parameter[i] = (char) ch; | |
545 i++; | |
546 } | |
547 parameter[i] = '\0'; | |
548 | |
549 ctx->param = param = atoi(parameter); | |
550 if (is_neg) | |
551 ctx->param = param = -param; | |
552 } | |
553 | |
554 /* space after control is optional, put character back if it is not a space */ | |
555 if (ch != ' ') | |
556 rtf_unget_char(ctx, ch); | |
557 | |
558 return rtf_dispatch_control(ctx, keyword, param, param_set); | |
559 } | |
560 | |
561 /* | |
562 * Route the character to the appropriate destination | |
563 */ | |
564 static int | |
565 rtf_dispatch_char(NMRtfContext *ctx, guchar ch) | |
566 { | |
567 if (ctx->ris == NMRTF_STATE_BIN && --(ctx->bytes_to_skip) <= 0) | |
568 ctx->ris = NMRTF_STATE_NORMAL; | |
569 | |
570 switch (ctx->rds) { | |
571 case NMRTF_STATE_SKIP: | |
572 return NMRTF_OK; | |
573 case NMRTF_STATE_NORMAL: | |
574 return rtf_print_char(ctx, ch); | |
575 case NMRTF_STATE_FONTTABLE: | |
576 if (ch == ';') { | |
577 rtf_add_font_entry(ctx, ctx->chp.font_idx, | |
578 ctx->ansi->str, ctx->chp.font_charset); | |
579 g_string_truncate(ctx->ansi, 0); | |
580 } | |
581 else { | |
582 return rtf_print_char(ctx, ch); | |
583 } | |
584 return NMRTF_OK; | |
585 default: | |
586 return NMRTF_OK; | |
587 } | |
588 } | |
589 | |
590 /* Handle a unicode character */ | |
591 static int | |
592 rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch) | |
593 { | |
594 switch (ctx->rds) { | |
595 case NMRTF_STATE_SKIP: | |
596 return NMRTF_OK; | |
597 case NMRTF_STATE_NORMAL: | |
598 case NMRTF_STATE_FONTTABLE: | |
599 return rtf_print_unicode_char(ctx, ch); | |
600 default: | |
601 return NMRTF_OK; | |
602 } | |
603 } | |
604 | |
605 /* | |
606 * Output a character | |
607 */ | |
608 static int | |
609 rtf_print_char(NMRtfContext *ctx, guchar ch) | |
610 { | |
611 | |
612 ctx->ansi = g_string_append_c(ctx->ansi, ch); | |
613 | |
614 return NMRTF_OK; | |
615 } | |
616 | |
617 /* | |
618 * Output a unicode character | |
619 */ | |
620 static int | |
621 rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch) | |
622 { | |
623 char buf[7]; | |
624 int num; | |
625 | |
626 /* convert and flush the ansi buffer to the utf8 buffer */ | |
627 rtf_flush_data(ctx); | |
628 | |
629 /* convert the unicode character to utf8 and add directly to the output buffer */ | |
630 num = g_unichar_to_utf8((gunichar) ch, buf); | |
631 buf[num] = 0; | |
632 gaim_debug_info("novell", "converted unichar 0x%X to utf8 char %s\n", ch, buf); | |
633 | |
634 ctx->output = g_string_append(ctx->output, buf); | |
635 return NMRTF_OK; | |
636 } | |
637 | |
638 /* | |
639 * Flush the output text | |
640 */ | |
641 static int | |
642 rtf_flush_data(NMRtfContext *ctx) | |
643 { | |
644 int status = NMRTF_OK; | |
645 char *conv_data = NULL; | |
646 const char *enc = NULL; | |
647 GError *gerror = NULL; | |
648 | |
649 if (ctx->rds == NMRTF_STATE_NORMAL && ctx->ansi->len > 0) { | |
650 enc = get_current_encoding(ctx); | |
651 conv_data = g_convert(ctx->ansi->str, ctx->ansi->len, "UTF-8", enc, | |
652 NULL, NULL, &gerror); | |
653 if (conv_data) { | |
654 ctx->output = g_string_append(ctx->output, conv_data); | |
655 g_free(conv_data); | |
656 ctx->ansi = g_string_truncate(ctx->ansi, 0); | |
657 } else { | |
658 status = NMRTF_CONVERT_ERROR; | |
659 gaim_debug_info("novell", "failed to convert data! error code = %d msg = %s\n", | |
660 gerror->code, gerror->message); | |
661 g_free(gerror); | |
662 } | |
663 } | |
664 | |
665 return status; | |
666 } | |
667 | |
668 /* | |
669 * Handle a property change | |
670 */ | |
671 static int | |
672 rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val) | |
673 { | |
674 if (ctx->rds == NMRTF_STATE_SKIP) /* If we're skipping text, */ | |
675 return NMRTF_OK; /* don't do anything. */ | |
676 | |
677 /* Need to flush any temporary data before a property change*/ | |
678 rtf_flush_data(ctx); | |
679 | |
680 switch (prop) { | |
681 case NMRTF_PROP_FONT_IDX: | |
682 ctx->chp.font_idx = val; | |
683 break; | |
684 case NMRTF_PROP_FONT_CHARSET: | |
685 ctx->chp.font_charset = val; | |
686 break; | |
687 default: | |
688 return NMRTF_BAD_TABLE; | |
689 } | |
690 | |
691 return NMRTF_OK; | |
692 } | |
693 | |
694 /* | |
695 * Step 3. | |
696 * Search the table for keyword and evaluate it appropriately. | |
697 * | |
698 * Inputs: | |
699 * keyword: The RTF control to evaluate. | |
700 * param: The parameter of the RTF control. | |
701 * param_set: TRUE if the control had a parameter; (that is, if param is valid) | |
702 * FALSE if it did not. | |
703 */ | |
704 static int | |
705 rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set) | |
706 { | |
707 int idx; | |
708 | |
709 for (idx = 0; idx < table_size; idx++) { | |
710 if (strcmp(keyword, rtf_symbols[idx].keyword) == 0) | |
711 break; | |
712 } | |
713 | |
714 if (idx == table_size) { | |
715 if (ctx->skip_unknown) | |
716 ctx->rds = NMRTF_STATE_SKIP; | |
717 ctx->skip_unknown = FALSE; | |
718 return NMRTF_OK; | |
719 } | |
720 | |
721 /* found it! use kwd_type and action to determine what to do with it. */ | |
722 ctx->skip_unknown = FALSE; | |
723 switch (rtf_symbols[idx].kwd_type) { | |
724 case NMRTF_KWD_PROP: | |
725 if (rtf_symbols[idx].pass_default || !param_set) | |
726 param = rtf_symbols[idx].default_val; | |
727 return rtf_apply_property(ctx, rtf_symbols[idx].action, param); | |
728 case NMRTF_KWD_CHAR: | |
729 return rtf_dispatch_char(ctx, rtf_symbols[idx].action); | |
730 case NMRTF_KWD_DEST: | |
731 return rtf_change_destination(ctx, rtf_symbols[idx].action); | |
732 case NMRTF_KWD_SPEC: | |
733 return rtf_dispatch_special(ctx, rtf_symbols[idx].action); | |
734 default: | |
735 return NMRTF_BAD_TABLE; | |
736 } | |
737 return NMRTF_BAD_TABLE; | |
738 } | |
739 | |
740 /* | |
741 * Change to the destination specified. | |
742 */ | |
743 static int | |
744 rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType type) | |
745 { | |
746 /* if we're skipping text, don't do anything */ | |
747 if (ctx->rds == NMRTF_STATE_SKIP) | |
748 return NMRTF_OK; | |
749 | |
750 switch (type) { | |
751 case NMRTF_DEST_FONTTABLE: | |
752 ctx->rds = NMRTF_STATE_FONTTABLE; | |
753 g_string_truncate(ctx->ansi, 0); | |
754 break; | |
755 default: | |
756 ctx->rds = NMRTF_STATE_SKIP; /* when in doubt, skip it... */ | |
757 break; | |
758 } | |
759 return NMRTF_OK; | |
760 } | |
761 | |
762 /* | |
763 * Dispatch an RTF control that needs special processing | |
764 */ | |
765 static int | |
766 rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd type) | |
767 { | |
768 int status = NMRTF_OK; | |
769 guchar ch; | |
770 | |
771 if (ctx->rds == NMRTF_STATE_SKIP && type != NMRTF_SPECIAL_BIN) /* if we're skipping, and it's not */ | |
772 return NMRTF_OK; /* the \bin keyword, ignore it. */ | |
773 | |
774 switch (type) { | |
775 case NMRTF_SPECIAL_BIN: | |
776 ctx->ris = NMRTF_STATE_BIN; | |
777 ctx->bytes_to_skip = ctx->param; | |
778 break; | |
779 case NMRTF_SPECIAL_SKIP: | |
780 ctx->skip_unknown = TRUE; | |
781 break; | |
782 case NMRTF_SPECIAL_HEX: | |
783 ctx->ris = NMRTF_STATE_HEX; | |
784 break; | |
785 case NMRTF_SPECIAL_UNICODE: | |
786 gaim_debug_info("novell", "parsing unichar\n"); | |
787 status = rtf_dispatch_unicode_char(ctx, ctx->param); | |
788 /* Skip next char */ | |
789 if (status == NMRTF_OK) | |
790 status = rtf_get_char(ctx, &ch); | |
791 break; | |
792 default: | |
793 status = NMRTF_BAD_TABLE; | |
794 break; | |
795 } | |
796 | |
797 return status; | |
798 } | |
799 | |
800 /* | |
801 * Get the next character from the input stream | |
802 */ | |
803 static int | |
804 rtf_get_char(NMRtfContext *ctx, guchar *ch) | |
805 { | |
806 if (ctx->nextch >= 0) { | |
807 *ch = ctx->nextch; | |
808 ctx->nextch = -1; | |
809 } | |
810 else { | |
811 *ch = *(ctx->input); | |
812 ctx->input++; | |
813 } | |
814 | |
815 if (*ch) | |
816 return NMRTF_OK; | |
817 else | |
818 return NMRTF_EOF; | |
819 } | |
820 | |
821 /* | |
822 * Move a character back into the input stream | |
823 */ | |
824 static int | |
825 rtf_unget_char(NMRtfContext *ctx, guchar ch) | |
826 { | |
827 ctx->nextch = ch; | |
828 return NMRTF_OK; | |
829 } |