Use GSList functions instead of manual iterations
[pidgin-git.git] / libpurple / protocols / novell / nmrtf.c
blobb0b00872c18daa32eead6fa175f87604ab362f67
1 /*
2 * nmrtf.c
4 * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
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
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 #include "util.h"
35 /* Internal RTF parser error codes */
36 #define NMRTF_OK 0 /* Everything's fine! */
37 #define NMRTF_STACK_UNDERFLOW 1 /* Unmatched '}' */
38 #define NMRTF_STACK_OVERFLOW 2 /* Too many '{' -- memory exhausted */
39 #define NMRTF_UNMATCHED_BRACE 3 /* RTF ended during an open group. */
40 #define NMRTF_INVALID_HEX 4 /* invalid hex character found in data */
41 #define NMRTF_BAD_TABLE 5 /* RTF table (sym or prop) invalid */
42 #define NMRTF_ASSERTION 6 /* Assertion failure */
43 #define NMRTF_EOF 7 /* End of file reached while reading RTF */
44 #define NMRTF_CONVERT_ERROR 8 /* Error converting text */
46 #define NMRTF_MAX_DEPTH 256
48 typedef enum
50 NMRTF_STATE_NORMAL,
51 NMRTF_STATE_SKIP,
52 NMRTF_STATE_FONTTABLE,
53 NMRTF_STATE_BIN,
54 NMRTF_STATE_HEX
55 } NMRtfState; /* Rtf State */
57 /* Property types that we care about */
58 typedef enum
60 NMRTF_PROP_FONT_IDX,
61 NMRTF_PROP_FONT_CHARSET,
62 NMRTF_PROP_MAX
63 } NMRtfProperty;
65 typedef enum
67 NMRTF_SPECIAL_BIN,
68 NMRTF_SPECIAL_HEX,
69 NMRTF_SPECIAL_UNICODE,
70 NMRTF_SPECIAL_SKIP
71 } NMRtfSpecialKwd;
73 typedef enum
75 NMRTF_DEST_FONTTABLE,
76 NMRTF_DEST_SKIP
77 } NMRtfDestinationType;
79 typedef enum
81 NMRTF_KWD_CHAR,
82 NMRTF_KWD_DEST,
83 NMRTF_KWD_PROP,
84 NMRTF_KWD_SPEC
85 } NMRtfKeywordType;
87 typedef struct
89 /* All we care about for now is the font.
90 * bold, italic, underline, etc. should be
91 * added here
93 int font_idx;
94 int font_charset;
95 } NMRtfCharProp;
97 typedef struct
99 NMRtfCharProp chp;
100 NMRtfState rds;
101 NMRtfState ris;
102 } NMRtfStateSave;
104 typedef struct
106 char *keyword; /* RTF keyword */
107 int default_val; /* default value to use */
108 gboolean pass_default; /* true to use default value from this table */
109 NMRtfKeywordType kwd_type; /* the type of the keyword */
110 int action; /* property type if the keyword represents a property */
111 /* destination type if the keyword represents a destination */
112 /* character to print if the keyword represents a character */
113 } NMRtfSymbol;
116 typedef struct
118 int number;
119 char *name;
120 int charset;
121 } NMRtfFont;
123 /* RTF Context */
124 struct _NMRtfContext
126 NMRtfState rds; /* destination state */
127 NMRtfState ris; /* internal state */
128 NMRtfCharProp chp; /* current character properties (ie. font, bold, italic, etc.) */
129 GSList *font_table; /* the font table */
130 GSList *saved; /* saved state stack */
131 int param; /* numeric parameter for the current keyword */
132 long bytes_to_skip; /* number of bytes to skip (after encountering \bin) */
133 int depth; /* how many groups deep are we */
134 gboolean skip_unknown; /* if true, skip any unknown destinations (this is set after encountering '\*') */
135 char *input; /* input string */
136 guchar nextch; /* next char in input */
137 gboolean nextch_available; /* nextch value is set */
138 GString *ansi; /* Temporary ansi text, will be convert/flushed to the output string */
139 GString *output; /* The plain text UTF8 string */
142 static int rtf_parse(NMRtfContext *ctx);
143 static int rtf_push_state(NMRtfContext *ctx);
144 static int rtf_pop_state(NMRtfContext *ctx);
145 static NMRtfFont *rtf_get_font(NMRtfContext *ctx, int index);
146 static int rtf_get_char(NMRtfContext *ctx, guchar *ch);
147 static int rtf_unget_char(NMRtfContext *ctx, guchar ch);
148 static int rtf_flush_data(NMRtfContext *ctx);
149 static int rtf_parse_keyword(NMRtfContext *ctx);
150 static int rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set);
151 static int rtf_dispatch_char(NMRtfContext *ctx, guchar ch);
152 static int rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch);
153 static int rtf_print_char(NMRtfContext *ctx, guchar ch);
154 static int rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch);
155 static int rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType dest);
156 static int rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd special);
157 static int rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val);
159 /* RTF parser tables */
161 /* Keyword descriptions */
162 NMRtfSymbol rtf_symbols[] = {
163 /* keyword, default, pass_default, keyword_type, action */
164 {"fonttbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_FONTTABLE},
165 {"f", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_IDX},
166 {"fcharset", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_CHARSET},
167 {"par", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
168 {"line", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
169 {"\0x0a", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
170 {"\0x0d", 0, FALSE, NMRTF_KWD_CHAR, 0x0a},
171 {"tab", 0, FALSE, NMRTF_KWD_CHAR, 0x09},
172 {"\r", 0, FALSE, NMRTF_KWD_CHAR, '\r'},
173 {"\n", 0, FALSE, NMRTF_KWD_CHAR, '\n'},
174 {"ldblquote",0, FALSE, NMRTF_KWD_CHAR, '"'},
175 {"rdblquote",0, FALSE, NMRTF_KWD_CHAR, '"'},
176 {"{", 0, FALSE, NMRTF_KWD_CHAR, '{'},
177 {"}", 0, FALSE, NMRTF_KWD_CHAR, '}'},
178 {"\\", 0, FALSE, NMRTF_KWD_CHAR, '\\'},
179 {"bin", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_BIN},
180 {"*", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_SKIP},
181 {"'", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_HEX},
182 {"u", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_UNICODE},
183 {"colortbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
184 {"author", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
185 {"buptim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
186 {"comment", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
187 {"creatim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
188 {"doccomm", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
189 {"footer", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
190 {"footerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
191 {"footerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
192 {"footerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
193 {"footnote", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
194 {"ftncn", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
195 {"ftnsep", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
196 {"ftnsepc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
197 {"header", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
198 {"headerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
199 {"headerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
200 {"headerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
201 {"info", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
202 {"keywords", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
203 {"operator", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
204 {"pict", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
205 {"printim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
206 {"private1", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
207 {"revtim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
208 {"rxe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
209 {"stylesheet", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
210 {"subject", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
211 {"tc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
212 {"title", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
213 {"txe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP},
214 {"xe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}
216 int table_size = sizeof(rtf_symbols) / sizeof(NMRtfSymbol);
218 NMRtfContext *
219 nm_rtf_init()
221 NMRtfContext *ctx = g_new0(NMRtfContext, 1);
222 ctx->nextch_available = FALSE;
223 ctx->ansi = g_string_new("");
224 ctx->output = g_string_new("");
225 return ctx;
228 char *
229 nm_rtf_strip_formatting(NMRtfContext *ctx, const char *input)
231 int status;
233 ctx->input = (char *)input;
234 status = rtf_parse(ctx);
235 if (status == NMRTF_OK)
236 return g_strdup(ctx->output->str);
238 purple_debug_info("novell", "RTF parser failed with error code %d\n", status);
239 return NULL;
242 void
243 nm_rtf_deinit(NMRtfContext *ctx)
245 GSList *node;
246 NMRtfFont *font;
248 if (ctx) {
249 for (node = ctx->font_table; node; node = node->next) {
250 font = node->data;
251 g_free(font->name);
252 g_free(font);
253 node->data = NULL;
255 g_slist_free(ctx->font_table);
256 g_slist_free_full(ctx->saved, g_free);
257 g_string_free(ctx->ansi, TRUE);
258 g_string_free(ctx->output, TRUE);
259 g_free(ctx);
263 static const char *
264 get_current_encoding(NMRtfContext *ctx)
266 NMRtfFont *font;
268 font = rtf_get_font(ctx, ctx->chp.font_idx);
270 switch (font->charset) {
271 case 0:
272 return "CP1252";
273 case 77:
274 return "MACINTOSH";
275 case 78:
276 return "SJIS";
277 case 128:
278 return "CP932";
279 case 129:
280 return "CP949";
281 case 130:
282 return "CP1361";
283 case 134:
284 return "CP936";
285 case 136:
286 return "CP950";
287 case 161:
288 return "CP1253";
289 case 162:
290 return "CP1254";
291 case 163:
292 return "CP1258";
293 case 181:
294 case 177:
295 return "CP1255";
296 case 178:
297 case 179:
298 case 180:
299 return "CP1256";
300 case 186:
301 return "CP1257";
302 case 204:
303 return "CP1251";
304 case 222:
305 return "CP874";
306 case 238:
307 return "CP1250";
308 case 254:
309 return "CP437";
310 default:
311 purple_debug_info("novell", "Unhandled font charset %d\n", font->charset);
312 return "CP1252";
318 * Add an entry to the font table
320 static int
321 rtf_add_font_entry(NMRtfContext *ctx, int number, const char *name, int charset)
323 NMRtfFont *font = g_new0(NMRtfFont, 1);
325 font->number = number;
326 font->name = g_strdup(name);
327 font->charset = charset;
329 purple_debug_info("novell", "Adding font to table: #%d\t%s\t%d\n",
330 font->number, font->name, font->charset);
332 ctx->font_table = g_slist_append(ctx->font_table, font);
334 return NMRTF_OK;
338 * Return the nth entry in the font table
340 static NMRtfFont *
341 rtf_get_font(NMRtfContext *ctx, int nth)
343 NMRtfFont *font;
345 font = g_slist_nth_data(ctx->font_table, nth);
347 return font;
351 * Step 1:
352 * Isolate RTF keywords and send them to rtf_parse_keyword;
353 * Push and pop state at the start and end of RTF groups;
354 * Send text to rtf_dispatch_char for further processing.
356 static int
357 rtf_parse(NMRtfContext *ctx)
359 int status;
360 guchar ch;
361 guchar hex_byte = 0;
362 int hex_count = 2;
363 int len;
365 if (ctx->input == NULL)
366 return NMRTF_OK;
368 while (rtf_get_char(ctx, &ch) == NMRTF_OK) {
369 if (ctx->depth < 0)
370 return NMRTF_STACK_UNDERFLOW;
372 /* if we're parsing binary data, handle it directly */
373 if (ctx->ris == NMRTF_STATE_BIN) {
374 if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK)
375 return status;
376 } else {
377 switch (ch) {
378 case '{':
379 if (ctx->depth > NMRTF_MAX_DEPTH)
380 return NMRTF_STACK_OVERFLOW;
381 rtf_flush_data(ctx);
382 if ((status = rtf_push_state(ctx)) != NMRTF_OK)
383 return status;
384 break;
385 case '}':
386 rtf_flush_data(ctx);
388 /* for some reason there is always an unwanted '\par' at the end */
389 if (ctx->rds == NMRTF_STATE_NORMAL) {
390 len = ctx->output->len;
391 if (ctx->output->str[len-1] == '\n')
392 ctx->output = g_string_truncate(ctx->output, len-1);
395 if ((status = rtf_pop_state(ctx)) != NMRTF_OK)
396 return status;
398 if (ctx->depth < 0)
399 return NMRTF_STACK_OVERFLOW;
400 break;
401 case '\\':
402 if ((status = rtf_parse_keyword(ctx)) != NMRTF_OK)
403 return status;
404 break;
405 case 0x0d:
406 case 0x0a: /* cr and lf are noise characters... */
407 break;
408 default:
409 if (ctx->ris == NMRTF_STATE_NORMAL) {
410 if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK)
411 return status;
412 } else { /* parsing a hex encoded character */
413 if (ctx->ris != NMRTF_STATE_HEX)
414 return NMRTF_ASSERTION;
416 hex_byte = hex_byte << 4;
417 if (isdigit(ch))
418 hex_byte += (char) ch - '0';
419 else {
420 if (islower(ch)) {
421 if (ch < 'a' || ch > 'f')
422 return NMRTF_INVALID_HEX;
423 hex_byte += (char) ch - 'a' + 10;
424 } else {
425 if (ch < 'A' || ch > 'F')
426 return NMRTF_INVALID_HEX;
427 hex_byte += (char) ch - 'A' + 10;
430 hex_count--;
431 if (hex_count == 0) {
432 if ((status = rtf_dispatch_char(ctx, hex_byte)) != NMRTF_OK)
433 return status;
434 hex_count = 2;
435 hex_byte = 0;
436 ctx->ris = NMRTF_STATE_NORMAL;
439 break;
443 if (ctx->depth < 0)
444 return NMRTF_STACK_OVERFLOW;
445 if (ctx->depth > 0)
446 return NMRTF_UNMATCHED_BRACE;
447 return NMRTF_OK;
451 * Push the current state onto stack
453 static int
454 rtf_push_state(NMRtfContext *ctx)
456 NMRtfStateSave *save = g_new0(NMRtfStateSave, 1);
457 save->chp = ctx->chp;
458 save->rds = ctx->rds;
459 save->ris = ctx->ris;
460 ctx->saved = g_slist_prepend(ctx->saved, save);
461 ctx->ris = NMRTF_STATE_NORMAL;
462 (ctx->depth)++;
463 return NMRTF_OK;
467 * Restore the state at the top of the stack
469 static int
470 rtf_pop_state(NMRtfContext *ctx)
472 NMRtfStateSave *save_old;
473 GSList *link_old;
475 if (ctx->saved == NULL)
476 return NMRTF_STACK_UNDERFLOW;
478 save_old = ctx->saved->data;
479 ctx->chp = save_old->chp;
480 ctx->rds = save_old->rds;
481 ctx->ris = save_old->ris;
482 (ctx->depth)--;
484 g_free(save_old);
485 link_old = ctx->saved;
486 ctx->saved = g_slist_delete_link(ctx->saved, link_old);
487 return NMRTF_OK;
491 * Step 2:
492 * Get a control word (and its associated value) and
493 * dispatch the control.
495 static int
496 rtf_parse_keyword(NMRtfContext *ctx)
498 int status = NMRTF_OK;
499 guchar ch;
500 gboolean param_set = FALSE;
501 gboolean is_neg = FALSE;
502 int param = 0;
503 char keyword[30];
504 char parameter[20];
505 gsize i;
507 keyword[0] = '\0';
508 parameter[0] = '\0';
509 if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK)
510 return status;
512 if (!isalpha(ch)) {
513 /* a control symbol; no delimiter. */
514 keyword[0] = (char) ch;
515 keyword[1] = '\0';
516 return rtf_dispatch_control(ctx, keyword, 0, param_set);
519 /* parse keyword */
520 for (i = 0; isalpha(ch) && (i < sizeof(keyword) - 1); rtf_get_char(ctx, &ch)) {
521 keyword[i] = (char) ch;
522 i++;
524 keyword[i] = '\0';
526 /* check for '-' indicated a negative parameter value */
527 if (ch == '-') {
528 is_neg = TRUE;
529 if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK)
530 return status;
533 /* check for numerical param */
534 if (isdigit(ch)) {
536 param_set = TRUE;
537 for (i = 0; isdigit(ch) && (i < sizeof(parameter) - 1); rtf_get_char(ctx, &ch)) {
538 parameter[i] = (char) ch;
539 i++;
541 parameter[i] = '\0';
543 ctx->param = param = atoi(parameter);
544 if (is_neg)
545 ctx->param = param = -param;
548 /* space after control is optional, put character back if it is not a space */
549 if (ch != ' ')
550 rtf_unget_char(ctx, ch);
552 return rtf_dispatch_control(ctx, keyword, param, param_set);
556 * Route the character to the appropriate destination
558 static int
559 rtf_dispatch_char(NMRtfContext *ctx, guchar ch)
561 if (ctx->ris == NMRTF_STATE_BIN && --(ctx->bytes_to_skip) <= 0)
562 ctx->ris = NMRTF_STATE_NORMAL;
564 switch (ctx->rds) {
565 case NMRTF_STATE_SKIP:
566 return NMRTF_OK;
567 case NMRTF_STATE_NORMAL:
568 return rtf_print_char(ctx, ch);
569 case NMRTF_STATE_FONTTABLE:
570 if (ch == ';') {
571 rtf_add_font_entry(ctx, ctx->chp.font_idx,
572 ctx->ansi->str, ctx->chp.font_charset);
573 g_string_truncate(ctx->ansi, 0);
575 else {
576 return rtf_print_char(ctx, ch);
578 return NMRTF_OK;
579 default:
580 return NMRTF_OK;
584 /* Handle a unicode character */
585 static int
586 rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch)
588 switch (ctx->rds) {
589 case NMRTF_STATE_SKIP:
590 return NMRTF_OK;
591 case NMRTF_STATE_NORMAL:
592 case NMRTF_STATE_FONTTABLE:
593 return rtf_print_unicode_char(ctx, ch);
594 default:
595 return NMRTF_OK;
600 * Output a character
602 static int
603 rtf_print_char(NMRtfContext *ctx, guchar ch)
606 ctx->ansi = g_string_append_c(ctx->ansi, ch);
608 return NMRTF_OK;
612 * Output a unicode character
614 static int
615 rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch)
617 char buf[7];
618 int num;
620 /* convert and flush the ansi buffer to the utf8 buffer */
621 rtf_flush_data(ctx);
623 /* convert the unicode character to utf8 and add directly to the output buffer */
624 num = g_unichar_to_utf8((gunichar) ch, buf);
625 buf[num] = 0;
626 purple_debug_info("novell", "converted unichar 0x%X to utf8 char %s\n", ch, buf);
628 ctx->output = g_string_append(ctx->output, buf);
629 return NMRTF_OK;
633 * Flush the output text
635 static int
636 rtf_flush_data(NMRtfContext *ctx)
638 int status = NMRTF_OK;
639 char *conv_data = NULL;
640 const char *enc = NULL;
641 GError *gerror = NULL;
643 if (ctx->rds == NMRTF_STATE_NORMAL && ctx->ansi->len > 0) {
644 enc = get_current_encoding(ctx);
645 conv_data = g_convert(ctx->ansi->str, ctx->ansi->len, "UTF-8", enc,
646 NULL, NULL, &gerror);
647 if (conv_data) {
648 ctx->output = g_string_append(ctx->output, conv_data);
649 g_free(conv_data);
650 ctx->ansi = g_string_truncate(ctx->ansi, 0);
651 } else {
652 status = NMRTF_CONVERT_ERROR;
653 purple_debug_info("novell", "failed to convert data! error code = %d msg = %s\n",
654 gerror->code, gerror->message);
657 g_error_free(gerror);
659 return status;
663 * Handle a property change
665 static int
666 rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val)
668 if (ctx->rds == NMRTF_STATE_SKIP) /* If we're skipping text, */
669 return NMRTF_OK; /* don't do anything. */
671 /* Need to flush any temporary data before a property change*/
672 rtf_flush_data(ctx);
674 switch (prop) {
675 case NMRTF_PROP_FONT_IDX:
676 ctx->chp.font_idx = val;
677 break;
678 case NMRTF_PROP_FONT_CHARSET:
679 ctx->chp.font_charset = val;
680 break;
681 default:
682 return NMRTF_BAD_TABLE;
685 return NMRTF_OK;
689 * Step 3.
690 * Search the table for keyword and evaluate it appropriately.
692 * Inputs:
693 * keyword: The RTF control to evaluate.
694 * param: The parameter of the RTF control.
695 * param_set: TRUE if the control had a parameter; (that is, if param is valid)
696 * FALSE if it did not.
698 static int
699 rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set)
701 int idx;
703 for (idx = 0; idx < table_size; idx++) {
704 if (purple_strequal(keyword, rtf_symbols[idx].keyword))
705 break;
708 if (idx == table_size) {
709 if (ctx->skip_unknown)
710 ctx->rds = NMRTF_STATE_SKIP;
711 ctx->skip_unknown = FALSE;
712 return NMRTF_OK;
715 /* found it! use kwd_type and action to determine what to do with it. */
716 ctx->skip_unknown = FALSE;
717 switch (rtf_symbols[idx].kwd_type) {
718 case NMRTF_KWD_PROP:
719 if (rtf_symbols[idx].pass_default || !param_set)
720 param = rtf_symbols[idx].default_val;
721 return rtf_apply_property(ctx, rtf_symbols[idx].action, param);
722 case NMRTF_KWD_CHAR:
723 return rtf_dispatch_char(ctx, rtf_symbols[idx].action);
724 case NMRTF_KWD_DEST:
725 return rtf_change_destination(ctx, rtf_symbols[idx].action);
726 case NMRTF_KWD_SPEC:
727 return rtf_dispatch_special(ctx, rtf_symbols[idx].action);
728 default:
729 return NMRTF_BAD_TABLE;
731 return NMRTF_BAD_TABLE;
735 * Change to the destination specified.
737 static int
738 rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType type)
740 /* if we're skipping text, don't do anything */
741 if (ctx->rds == NMRTF_STATE_SKIP)
742 return NMRTF_OK;
744 switch (type) {
745 case NMRTF_DEST_FONTTABLE:
746 ctx->rds = NMRTF_STATE_FONTTABLE;
747 g_string_truncate(ctx->ansi, 0);
748 break;
749 default:
750 ctx->rds = NMRTF_STATE_SKIP; /* when in doubt, skip it... */
751 break;
753 return NMRTF_OK;
757 * Dispatch an RTF control that needs special processing
759 static int
760 rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd type)
762 int status = NMRTF_OK;
763 guchar ch;
765 if (ctx->rds == NMRTF_STATE_SKIP && type != NMRTF_SPECIAL_BIN) /* if we're skipping, and it's not */
766 return NMRTF_OK; /* the \bin keyword, ignore it. */
768 switch (type) {
769 case NMRTF_SPECIAL_BIN:
770 ctx->ris = NMRTF_STATE_BIN;
771 ctx->bytes_to_skip = ctx->param;
772 break;
773 case NMRTF_SPECIAL_SKIP:
774 ctx->skip_unknown = TRUE;
775 break;
776 case NMRTF_SPECIAL_HEX:
777 ctx->ris = NMRTF_STATE_HEX;
778 break;
779 case NMRTF_SPECIAL_UNICODE:
780 purple_debug_info("novell", "parsing unichar\n");
781 status = rtf_dispatch_unicode_char(ctx, ctx->param);
782 /* Skip next char */
783 if (status == NMRTF_OK)
784 status = rtf_get_char(ctx, &ch);
785 break;
786 default:
787 status = NMRTF_BAD_TABLE;
788 break;
791 return status;
795 * Get the next character from the input stream
797 static int
798 rtf_get_char(NMRtfContext *ctx, guchar *ch)
800 if (ctx->nextch_available) {
801 *ch = ctx->nextch;
802 ctx->nextch_available = FALSE;
803 } else {
804 *ch = *(ctx->input);
805 ctx->input++;
808 if (*ch)
809 return NMRTF_OK;
810 else
811 return NMRTF_EOF;
815 * Move a character back into the input stream
817 static int
818 rtf_unget_char(NMRtfContext *ctx, guchar ch)
820 ctx->nextch = ch;
821 ctx->nextch_available = TRUE;
822 return NMRTF_OK;