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 }