xref: /haiku/src/add-ons/translators/rtf/RTF.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
19949213aSStephan Aßmus /*
2b70311a3SAxel Dörfler  * Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de.
39949213aSStephan Aßmus  * Distributed under the terms of the MIT License.
49949213aSStephan Aßmus  */
59949213aSStephan Aßmus 
69949213aSStephan Aßmus 
79949213aSStephan Aßmus #include "RTF.h"
89949213aSStephan Aßmus 
9b70311a3SAxel Dörfler #include <ctype.h>
10b70311a3SAxel Dörfler #include <stdio.h>
11b70311a3SAxel Dörfler #include <stdlib.h>
12b70311a3SAxel Dörfler #include <string.h>
13b70311a3SAxel Dörfler 
149949213aSStephan Aßmus #include <DataIO.h>
159949213aSStephan Aßmus 
16b70311a3SAxel Dörfler 
17b70311a3SAxel Dörfler //#define TRACE_RTF
18b70311a3SAxel Dörfler #ifdef TRACE_RTF
19b70311a3SAxel Dörfler #	define TRACE(x...) printf(x)
20b70311a3SAxel Dörfler #else
21b70311a3SAxel Dörfler #	define TRACE(x...) ;
22b70311a3SAxel Dörfler #endif
239949213aSStephan Aßmus 
249949213aSStephan Aßmus 
259949213aSStephan Aßmus static const char *kDestinationControlWords[] = {
269949213aSStephan Aßmus 	"aftncn", "aftnsep", "aftnsepc", "annotation", "atnauthor", "atndate",
279949213aSStephan Aßmus 	"atnicn", "atnid", "atnparent", "atnref", "atntime", "atrfend",
289949213aSStephan Aßmus 	"atrfstart", "author", "background", "bkmkend", "buptim", "colortbl",
299949213aSStephan Aßmus 	"comment", "creatim", "do", "doccomm", "docvar", "fonttbl", "footer",
309949213aSStephan Aßmus 	"footerf", "footerl", "footerr", "footnote", "ftncn", "ftnsep",
319949213aSStephan Aßmus 	"ftnsepc", "header", "headerf", "headerl", "headerr", "info",
329949213aSStephan Aßmus 	"keywords", "operator", "pict", "printim", "private1", "revtim",
339949213aSStephan Aßmus 	"rxe", "stylesheet", "subject", "tc", "title", "txe", "xe",
349949213aSStephan Aßmus };
359949213aSStephan Aßmus 
36*365c228dSAugustin Cavalier static char read_char(BDataIO &stream, bool endOfFileAllowed = false);
37*365c228dSAugustin Cavalier static int32 parse_integer(char first, BDataIO &stream, char &_last, int32 base = 10);
389949213aSStephan Aßmus 
399949213aSStephan Aßmus 
409949213aSStephan Aßmus using namespace RTF;
419949213aSStephan Aßmus 
429949213aSStephan Aßmus 
439949213aSStephan Aßmus static char
read_char(BDataIO & stream,bool endOfFileAllowed)44*365c228dSAugustin Cavalier read_char(BDataIO &stream, bool endOfFileAllowed)
459949213aSStephan Aßmus {
469949213aSStephan Aßmus 	char c;
479949213aSStephan Aßmus 	ssize_t bytesRead = stream.Read(&c, 1);
489949213aSStephan Aßmus 
499949213aSStephan Aßmus 	if (bytesRead < B_OK)
509949213aSStephan Aßmus 		throw (status_t)bytesRead;
519949213aSStephan Aßmus 
529949213aSStephan Aßmus 	if (bytesRead == 0 && !endOfFileAllowed)
539949213aSStephan Aßmus 		throw (status_t)B_ERROR;
549949213aSStephan Aßmus 
559949213aSStephan Aßmus 	return c;
569949213aSStephan Aßmus }
579949213aSStephan Aßmus 
589949213aSStephan Aßmus 
599949213aSStephan Aßmus static int32
parse_integer(char first,BDataIO & stream,char & _last,int32 base)60b70311a3SAxel Dörfler parse_integer(char first, BDataIO &stream, char &_last, int32 base)
619949213aSStephan Aßmus {
629949213aSStephan Aßmus 	const char *kDigits = "0123456789abcdef";
639949213aSStephan Aßmus 	int32 integer = 0;
649949213aSStephan Aßmus 	int32 count = 0;
659949213aSStephan Aßmus 
669949213aSStephan Aßmus 	char digit = first;
679949213aSStephan Aßmus 
689949213aSStephan Aßmus 	if (digit == '\0')
699949213aSStephan Aßmus 		digit = read_char(stream);
709949213aSStephan Aßmus 
719949213aSStephan Aßmus 	while (true) {
729949213aSStephan Aßmus 		int32 pos = 0;
739949213aSStephan Aßmus 		for (; pos < base; pos++) {
74b70311a3SAxel Dörfler 			if (kDigits[pos] == tolower(digit)) {
759949213aSStephan Aßmus 				integer = integer * base + pos;
769949213aSStephan Aßmus 				count++;
779949213aSStephan Aßmus 				break;
789949213aSStephan Aßmus 			}
799949213aSStephan Aßmus 		}
809949213aSStephan Aßmus 		if (pos == base) {
819949213aSStephan Aßmus 			_last = digit;
829949213aSStephan Aßmus 			goto out;
839949213aSStephan Aßmus 		}
849949213aSStephan Aßmus 
859949213aSStephan Aßmus 		digit = read_char(stream);
869949213aSStephan Aßmus 	}
879949213aSStephan Aßmus 
889949213aSStephan Aßmus out:
899949213aSStephan Aßmus 	if (count == 0)
909949213aSStephan Aßmus 		throw (status_t)B_BAD_TYPE;
919949213aSStephan Aßmus 
929949213aSStephan Aßmus 	return integer;
939949213aSStephan Aßmus }
949949213aSStephan Aßmus 
959949213aSStephan Aßmus 
969949213aSStephan Aßmus static int
string_array_compare(const char * key,const char ** array)979949213aSStephan Aßmus string_array_compare(const char *key, const char **array)
989949213aSStephan Aßmus {
999949213aSStephan Aßmus 	return strcmp(key, array[0]);
1009949213aSStephan Aßmus }
1019949213aSStephan Aßmus 
1029949213aSStephan Aßmus 
1039949213aSStephan Aßmus static void
dump(Element & element,int32 level=0)1049949213aSStephan Aßmus dump(Element &element, int32 level = 0)
1059949213aSStephan Aßmus {
1062b861dd2SAlex Smith 	printf("%03" B_PRId32 " (%p):", level, &element);
1079949213aSStephan Aßmus 	for (int32 i = 0; i < level; i++)
1089949213aSStephan Aßmus 		printf("  ");
1099949213aSStephan Aßmus 
1109949213aSStephan Aßmus 	if (RTF::Header *header = dynamic_cast<RTF::Header *>(&element)) {
1112b861dd2SAlex Smith 		printf("<RTF header, major version %" B_PRId32 ">\n", header->Version());
1129949213aSStephan Aßmus 	} else if (RTF::Command *command = dynamic_cast<RTF::Command *>(&element)) {
1139949213aSStephan Aßmus 		printf("<Command: %s", command->Name());
1149949213aSStephan Aßmus 		if (command->HasOption())
1152b861dd2SAlex Smith 			printf(", Option %" B_PRId32, command->Option());
1169949213aSStephan Aßmus 		puts(">");
1179949213aSStephan Aßmus 	} else if (RTF::Text *text = dynamic_cast<RTF::Text *>(&element)) {
1189949213aSStephan Aßmus 		printf("<Text>");
1199949213aSStephan Aßmus 		puts(text->String());
1209949213aSStephan Aßmus 	} else if (RTF::Group *group = dynamic_cast<RTF::Group *>(&element))
1219949213aSStephan Aßmus 		printf("<Group \"%s\">\n", group->Name());
1229949213aSStephan Aßmus 
1239949213aSStephan Aßmus 	if (RTF::Group *group = dynamic_cast<RTF::Group *>(&element)) {
1249949213aSStephan Aßmus 		for (uint32 i = 0; i < group->CountElements(); i++)
1259949213aSStephan Aßmus 			dump(*group->ElementAt(i), level + 1);
1269949213aSStephan Aßmus 	}
1279949213aSStephan Aßmus }
1289949213aSStephan Aßmus 
1299949213aSStephan Aßmus 
1309949213aSStephan Aßmus //	#pragma mark -
1319949213aSStephan Aßmus 
1329949213aSStephan Aßmus 
Parser(BPositionIO & stream)1339949213aSStephan Aßmus Parser::Parser(BPositionIO &stream)
1349949213aSStephan Aßmus 	:
1359949213aSStephan Aßmus 	fStream(&stream, 65536, false),
1369949213aSStephan Aßmus 	fIdentified(false)
1379949213aSStephan Aßmus {
1389949213aSStephan Aßmus }
1399949213aSStephan Aßmus 
1409949213aSStephan Aßmus 
1419949213aSStephan Aßmus status_t
Identify()1429949213aSStephan Aßmus Parser::Identify()
1439949213aSStephan Aßmus {
1449949213aSStephan Aßmus 	char header[5];
1459949213aSStephan Aßmus 	if (fStream.Read(header, sizeof(header)) < (ssize_t)sizeof(header))
1469949213aSStephan Aßmus 		return B_IO_ERROR;
1479949213aSStephan Aßmus 
1489949213aSStephan Aßmus 	if (strncmp(header, "{\\rtf", 5))
1499949213aSStephan Aßmus 		return B_BAD_TYPE;
1509949213aSStephan Aßmus 
1519949213aSStephan Aßmus 	fIdentified = true;
1529949213aSStephan Aßmus 	return B_OK;
1539949213aSStephan Aßmus }
1549949213aSStephan Aßmus 
1559949213aSStephan Aßmus 
1569949213aSStephan Aßmus status_t
Parse(Header & header)1579949213aSStephan Aßmus Parser::Parse(Header &header)
1589949213aSStephan Aßmus {
1599949213aSStephan Aßmus 	if (!fIdentified && Identify() != B_OK)
1609949213aSStephan Aßmus 		return B_BAD_TYPE;
1619949213aSStephan Aßmus 
1629949213aSStephan Aßmus 	try {
1639949213aSStephan Aßmus 		int32 openBrackets = 1;
1649949213aSStephan Aßmus 
1659949213aSStephan Aßmus 		// since we already preparsed parts of the RTF header, the header
1669949213aSStephan Aßmus 		// is handled here directly
1679949213aSStephan Aßmus 		char last;
1689949213aSStephan Aßmus 		header.Parse('\0', fStream, last);
1699949213aSStephan Aßmus 
1709949213aSStephan Aßmus 		Group *parent = &header;
1719949213aSStephan Aßmus 		char c = last;
1729949213aSStephan Aßmus 
1739949213aSStephan Aßmus 		while (true) {
1749949213aSStephan Aßmus 			Element *element = NULL;
1759949213aSStephan Aßmus 
1769949213aSStephan Aßmus 			// we'll just ignore the end of the stream
1779949213aSStephan Aßmus 			if (parent == NULL)
1789949213aSStephan Aßmus 				return B_OK;
1799949213aSStephan Aßmus 
1809949213aSStephan Aßmus 			switch (c) {
1819949213aSStephan Aßmus 				case '{':
1829949213aSStephan Aßmus 					openBrackets++;
1839949213aSStephan Aßmus 					parent->AddElement(element = new Group());
1849949213aSStephan Aßmus 					parent = static_cast<Group *>(element);
1859949213aSStephan Aßmus 					break;
1869949213aSStephan Aßmus 
1879949213aSStephan Aßmus 				case '\\':
1889949213aSStephan Aßmus 					parent->AddElement(element = new Command());
1899949213aSStephan Aßmus 					break;
1909949213aSStephan Aßmus 
1919949213aSStephan Aßmus 				case '}':
1929949213aSStephan Aßmus 					openBrackets--;
1939949213aSStephan Aßmus 					parent->DetermineDestination();
1949949213aSStephan Aßmus 					parent = parent->Parent();
1959949213aSStephan Aßmus 					// supposed to fall through
1969949213aSStephan Aßmus 				case '\n':
1979949213aSStephan Aßmus 				case '\r':
1989949213aSStephan Aßmus 				{
1999949213aSStephan Aßmus 					ssize_t bytesRead = fStream.Read(&c, 1);
2009949213aSStephan Aßmus 					if (bytesRead < B_OK)
2019949213aSStephan Aßmus 						throw (status_t)bytesRead;
2029949213aSStephan Aßmus 					else if (bytesRead != 1) {
2039949213aSStephan Aßmus 						// this is the only valid exit status
2049949213aSStephan Aßmus 						if (openBrackets == 0)
2059949213aSStephan Aßmus 							return B_OK;
2069949213aSStephan Aßmus 
207e36d4dadSStefano Ceccherini 						throw (status_t)B_ERROR;
2089949213aSStephan Aßmus 					}
2099949213aSStephan Aßmus 					continue;
2109949213aSStephan Aßmus 				}
2119949213aSStephan Aßmus 
2129949213aSStephan Aßmus 				default:
2139949213aSStephan Aßmus 					parent->AddElement(element = new Text());
2149949213aSStephan Aßmus 					break;
2159949213aSStephan Aßmus 			}
2169949213aSStephan Aßmus 
2179949213aSStephan Aßmus 			if (element == NULL)
2189949213aSStephan Aßmus 				throw (status_t)B_ERROR;
2199949213aSStephan Aßmus 
2209949213aSStephan Aßmus 			element->Parse(c, fStream, last);
2219949213aSStephan Aßmus 			c = last;
2229949213aSStephan Aßmus 		}
2239949213aSStephan Aßmus 	} catch (status_t status) {
2249949213aSStephan Aßmus 		return status;
2259949213aSStephan Aßmus 	}
2269949213aSStephan Aßmus 
2279949213aSStephan Aßmus 	return B_OK;
2289949213aSStephan Aßmus }
2299949213aSStephan Aßmus 
2309949213aSStephan Aßmus 
2319949213aSStephan Aßmus //	#pragma mark -
2329949213aSStephan Aßmus 
2339949213aSStephan Aßmus 
Element()2349949213aSStephan Aßmus Element::Element()
2359949213aSStephan Aßmus 	:
2369949213aSStephan Aßmus 	fParent(NULL)
2379949213aSStephan Aßmus {
2389949213aSStephan Aßmus }
2399949213aSStephan Aßmus 
2409949213aSStephan Aßmus 
~Element()2419949213aSStephan Aßmus Element::~Element()
2429949213aSStephan Aßmus {
2439949213aSStephan Aßmus }
2449949213aSStephan Aßmus 
2459949213aSStephan Aßmus 
2469949213aSStephan Aßmus void
SetParent(Group * parent)2479949213aSStephan Aßmus Element::SetParent(Group *parent)
2489949213aSStephan Aßmus {
2499949213aSStephan Aßmus 	fParent = parent;
2509949213aSStephan Aßmus }
2519949213aSStephan Aßmus 
2529949213aSStephan Aßmus 
2539949213aSStephan Aßmus Group *
Parent() const2549949213aSStephan Aßmus Element::Parent() const
2559949213aSStephan Aßmus {
2569949213aSStephan Aßmus 	return fParent;
2579949213aSStephan Aßmus }
2589949213aSStephan Aßmus 
2599949213aSStephan Aßmus 
2609949213aSStephan Aßmus bool
IsDefinitionDelimiter()2619949213aSStephan Aßmus Element::IsDefinitionDelimiter()
2629949213aSStephan Aßmus {
2639949213aSStephan Aßmus 	return false;
2649949213aSStephan Aßmus }
2659949213aSStephan Aßmus 
2669949213aSStephan Aßmus 
2679949213aSStephan Aßmus void
PrintToStream(int32 level)2689949213aSStephan Aßmus Element::PrintToStream(int32 level)
2699949213aSStephan Aßmus {
2709949213aSStephan Aßmus 	dump(*this, level);
2719949213aSStephan Aßmus }
2729949213aSStephan Aßmus 
2739949213aSStephan Aßmus 
2749949213aSStephan Aßmus //	#pragma mark -
2759949213aSStephan Aßmus 
2769949213aSStephan Aßmus 
Group()2779949213aSStephan Aßmus Group::Group()
2789949213aSStephan Aßmus 	:
2799949213aSStephan Aßmus 	fDestination(TEXT_DESTINATION)
2809949213aSStephan Aßmus {
2819949213aSStephan Aßmus }
2829949213aSStephan Aßmus 
2839949213aSStephan Aßmus 
~Group()2849949213aSStephan Aßmus Group::~Group()
2859949213aSStephan Aßmus {
2869949213aSStephan Aßmus 	Element *element;
2872b861dd2SAlex Smith 	while ((element = (Element *)fElements.RemoveItem((int32)0)) != NULL) {
2889949213aSStephan Aßmus 		delete element;
2899949213aSStephan Aßmus 	}
2909949213aSStephan Aßmus }
2919949213aSStephan Aßmus 
2929949213aSStephan Aßmus 
2939949213aSStephan Aßmus void
Parse(char first,BDataIO & stream,char & last)294*365c228dSAugustin Cavalier Group::Parse(char first, BDataIO &stream, char &last)
2959949213aSStephan Aßmus {
2969949213aSStephan Aßmus 	if (first == '\0')
2979949213aSStephan Aßmus 		first = read_char(stream);
2989949213aSStephan Aßmus 
2999949213aSStephan Aßmus 	if (first != '{')
3009949213aSStephan Aßmus 		throw (status_t)B_BAD_TYPE;
3019949213aSStephan Aßmus 
3029949213aSStephan Aßmus 	last = read_char(stream);
3039949213aSStephan Aßmus }
3049949213aSStephan Aßmus 
3059949213aSStephan Aßmus 
3069949213aSStephan Aßmus status_t
AddElement(Element * element)3079949213aSStephan Aßmus Group::AddElement(Element *element)
3089949213aSStephan Aßmus {
3099949213aSStephan Aßmus 	if (element == NULL)
3109949213aSStephan Aßmus 		return B_BAD_VALUE;
3119949213aSStephan Aßmus 
3129949213aSStephan Aßmus 	if (fElements.AddItem(element)) {
3139949213aSStephan Aßmus 		element->SetParent(this);
3149949213aSStephan Aßmus 		return B_OK;
3159949213aSStephan Aßmus 	}
3169949213aSStephan Aßmus 
3179949213aSStephan Aßmus 	return B_NO_MEMORY;
3189949213aSStephan Aßmus }
3199949213aSStephan Aßmus 
3209949213aSStephan Aßmus 
3219949213aSStephan Aßmus uint32
CountElements() const3229949213aSStephan Aßmus Group::CountElements() const
3239949213aSStephan Aßmus {
3249949213aSStephan Aßmus 	return (uint32)fElements.CountItems();
3259949213aSStephan Aßmus }
3269949213aSStephan Aßmus 
3279949213aSStephan Aßmus 
3289949213aSStephan Aßmus Element *
ElementAt(uint32 index) const3299949213aSStephan Aßmus Group::ElementAt(uint32 index) const
3309949213aSStephan Aßmus {
3319949213aSStephan Aßmus 	return static_cast<Element *>(fElements.ItemAt(index));
3329949213aSStephan Aßmus }
3339949213aSStephan Aßmus 
3349949213aSStephan Aßmus 
3359949213aSStephan Aßmus Element *
FindDefinitionStart(int32 index,int32 * _startIndex) const3369949213aSStephan Aßmus Group::FindDefinitionStart(int32 index, int32 *_startIndex) const
3379949213aSStephan Aßmus {
3389949213aSStephan Aßmus 	if (index < 0)
3399949213aSStephan Aßmus 		return NULL;
3409949213aSStephan Aßmus 
3419949213aSStephan Aßmus 	Element *element;
3429949213aSStephan Aßmus 	int32 number = 0;
3439949213aSStephan Aßmus 	for (uint32 i = 0; (element = ElementAt(i)) != NULL; i++) {
3449949213aSStephan Aßmus 		if (number == index) {
3459949213aSStephan Aßmus 			if (_startIndex)
3469949213aSStephan Aßmus 				*_startIndex = i;
3479949213aSStephan Aßmus 			return element;
3489949213aSStephan Aßmus 		}
3499949213aSStephan Aßmus 
3509949213aSStephan Aßmus 		if (element->IsDefinitionDelimiter())
3519949213aSStephan Aßmus 			number++;
3529949213aSStephan Aßmus 	}
3539949213aSStephan Aßmus 
3549949213aSStephan Aßmus 	return NULL;
3559949213aSStephan Aßmus }
3569949213aSStephan Aßmus 
3579949213aSStephan Aßmus 
3589949213aSStephan Aßmus Command *
FindDefinition(const char * name,int32 index) const3599949213aSStephan Aßmus Group::FindDefinition(const char *name, int32 index) const
3609949213aSStephan Aßmus {
3619949213aSStephan Aßmus 	int32 startIndex;
3629949213aSStephan Aßmus 	Element *element = FindDefinitionStart(index, &startIndex);
3639949213aSStephan Aßmus 	if (element == NULL)
3649949213aSStephan Aßmus 		return NULL;
3659949213aSStephan Aßmus 
3669949213aSStephan Aßmus 	for (uint32 i = startIndex; (element = ElementAt(i)) != NULL; i++) {
3679949213aSStephan Aßmus 		if (element->IsDefinitionDelimiter())
3689949213aSStephan Aßmus 			break;
3699949213aSStephan Aßmus 
3709949213aSStephan Aßmus 		if (Command *command = dynamic_cast<Command *>(element)) {
3719949213aSStephan Aßmus 			if (command != NULL && !strcmp(name, command->Name()))
3729949213aSStephan Aßmus 				return command;
3739949213aSStephan Aßmus 		}
3749949213aSStephan Aßmus 	}
3759949213aSStephan Aßmus 
3769949213aSStephan Aßmus 	return NULL;
3779949213aSStephan Aßmus }
3789949213aSStephan Aßmus 
3799949213aSStephan Aßmus 
3809949213aSStephan Aßmus Group *
FindGroup(const char * name) const3819949213aSStephan Aßmus Group::FindGroup(const char *name) const
3829949213aSStephan Aßmus {
3839949213aSStephan Aßmus 	Element *element;
3849949213aSStephan Aßmus 	for (uint32 i = 0; (element = ElementAt(i)) != NULL; i++) {
3859949213aSStephan Aßmus 		Group *group = dynamic_cast<Group *>(element);
3869949213aSStephan Aßmus 		if (group == NULL)
3879949213aSStephan Aßmus 			continue;
3889949213aSStephan Aßmus 
3899949213aSStephan Aßmus 		Command *command = dynamic_cast<Command *>(group->ElementAt(0));
3909949213aSStephan Aßmus 		if (command != NULL && !strcmp(name, command->Name()))
3919949213aSStephan Aßmus 			return group;
3929949213aSStephan Aßmus 	}
3939949213aSStephan Aßmus 
3949949213aSStephan Aßmus 	return NULL;
3959949213aSStephan Aßmus }
3969949213aSStephan Aßmus 
3979949213aSStephan Aßmus 
3989949213aSStephan Aßmus const char *
Name() const3999949213aSStephan Aßmus Group::Name() const
4009949213aSStephan Aßmus {
4019949213aSStephan Aßmus 	Command *command = dynamic_cast<Command *>(ElementAt(0));
4029949213aSStephan Aßmus 	if (command != NULL)
4039949213aSStephan Aßmus 		return command->Name();
4049949213aSStephan Aßmus 
4059949213aSStephan Aßmus 	return NULL;
4069949213aSStephan Aßmus }
4079949213aSStephan Aßmus 
4089949213aSStephan Aßmus 
4099949213aSStephan Aßmus void
DetermineDestination()4109949213aSStephan Aßmus Group::DetermineDestination()
4119949213aSStephan Aßmus {
4129949213aSStephan Aßmus 	const char *name = Name();
4139949213aSStephan Aßmus 	if (name == NULL)
4149949213aSStephan Aßmus 		return;
4159949213aSStephan Aßmus 
4169949213aSStephan Aßmus 	if (!strcmp(name, "*")) {
4179949213aSStephan Aßmus 		fDestination = COMMENT_DESTINATION;
4189949213aSStephan Aßmus 		return;
4199949213aSStephan Aßmus 	}
4209949213aSStephan Aßmus 
4219949213aSStephan Aßmus 	// binary search for destination control words
4229949213aSStephan Aßmus 
4239949213aSStephan Aßmus 	if (bsearch(name, kDestinationControlWords,
4249949213aSStephan Aßmus 			sizeof(kDestinationControlWords) / sizeof(kDestinationControlWords[0]),
4259949213aSStephan Aßmus 			sizeof(kDestinationControlWords[0]),
4269949213aSStephan Aßmus 			(int (*)(const void *, const void *))string_array_compare) != NULL)
4279949213aSStephan Aßmus 		fDestination = OTHER_DESTINATION;
4289949213aSStephan Aßmus }
4299949213aSStephan Aßmus 
4309949213aSStephan Aßmus 
4319949213aSStephan Aßmus group_destination
Destination() const4329949213aSStephan Aßmus Group::Destination() const
4339949213aSStephan Aßmus {
4349949213aSStephan Aßmus 	return fDestination;
4359949213aSStephan Aßmus }
4369949213aSStephan Aßmus 
4379949213aSStephan Aßmus 
4389949213aSStephan Aßmus //	#pragma mark -
4399949213aSStephan Aßmus 
4409949213aSStephan Aßmus 
Header()4419949213aSStephan Aßmus Header::Header()
4429949213aSStephan Aßmus 	:
4439949213aSStephan Aßmus 	fVersion(0)
4449949213aSStephan Aßmus {
4459949213aSStephan Aßmus }
4469949213aSStephan Aßmus 
4479949213aSStephan Aßmus 
~Header()4489949213aSStephan Aßmus Header::~Header()
4499949213aSStephan Aßmus {
4509949213aSStephan Aßmus }
4519949213aSStephan Aßmus 
4529949213aSStephan Aßmus 
4539949213aSStephan Aßmus void
Parse(char first,BDataIO & stream,char & last)454*365c228dSAugustin Cavalier Header::Parse(char first, BDataIO &stream, char &last)
4559949213aSStephan Aßmus {
4569949213aSStephan Aßmus 	// The stream has been peeked into by the parser already, and
4579949213aSStephan Aßmus 	// only the version follows in the stream -- let's pick it up
4589949213aSStephan Aßmus 
4599949213aSStephan Aßmus 	fVersion = parse_integer(first, stream, last);
4609949213aSStephan Aßmus 
4619949213aSStephan Aßmus 	// recreate "rtf" command to name this group
4629949213aSStephan Aßmus 
4639949213aSStephan Aßmus 	Command *command = new Command();
4649949213aSStephan Aßmus 	command->SetName("rtf");
4659949213aSStephan Aßmus 	command->SetOption(fVersion);
4669949213aSStephan Aßmus 
4679949213aSStephan Aßmus 	AddElement(command);
4689949213aSStephan Aßmus }
4699949213aSStephan Aßmus 
4709949213aSStephan Aßmus 
4719949213aSStephan Aßmus int32
Version() const4729949213aSStephan Aßmus Header::Version() const
4739949213aSStephan Aßmus {
4749949213aSStephan Aßmus 	return fVersion;
4759949213aSStephan Aßmus }
4769949213aSStephan Aßmus 
4779949213aSStephan Aßmus 
4789949213aSStephan Aßmus const char *
Charset() const4799949213aSStephan Aßmus Header::Charset() const
4809949213aSStephan Aßmus {
4819949213aSStephan Aßmus 	Command *command = dynamic_cast<Command *>(ElementAt(1));
4829949213aSStephan Aßmus 	if (command == NULL)
4839949213aSStephan Aßmus 		return NULL;
4849949213aSStephan Aßmus 
4859949213aSStephan Aßmus 	return command->Name();
4869949213aSStephan Aßmus }
4879949213aSStephan Aßmus 
4889949213aSStephan Aßmus 
4899949213aSStephan Aßmus rgb_color
Color(int32 index)4909949213aSStephan Aßmus Header::Color(int32 index)
4919949213aSStephan Aßmus {
4929949213aSStephan Aßmus 	rgb_color color = {0, 0, 0, 255};
4939949213aSStephan Aßmus 
4949949213aSStephan Aßmus 	Group *colorTable = FindGroup("colortbl");
4959949213aSStephan Aßmus 
4969949213aSStephan Aßmus 	if (colorTable != NULL) {
4979949213aSStephan Aßmus 		if (Command *gun = colorTable->FindDefinition("red", index))
4989949213aSStephan Aßmus 			color.red = gun->Option();
4999949213aSStephan Aßmus 		if (Command *gun = colorTable->FindDefinition("green", index))
5009949213aSStephan Aßmus 			color.green = gun->Option();
5019949213aSStephan Aßmus 		if (Command *gun = colorTable->FindDefinition("blue", index))
5029949213aSStephan Aßmus 			color.blue = gun->Option();
5039949213aSStephan Aßmus 	}
5049949213aSStephan Aßmus 
5059949213aSStephan Aßmus 	return color;
5069949213aSStephan Aßmus }
5079949213aSStephan Aßmus 
5089949213aSStephan Aßmus 
5099949213aSStephan Aßmus //	#pragma mark -
5109949213aSStephan Aßmus 
5119949213aSStephan Aßmus 
Text()5129949213aSStephan Aßmus Text::Text()
5139949213aSStephan Aßmus {
5149949213aSStephan Aßmus }
5159949213aSStephan Aßmus 
5169949213aSStephan Aßmus 
~Text()5179949213aSStephan Aßmus Text::~Text()
5189949213aSStephan Aßmus {
5199949213aSStephan Aßmus 	SetTo(NULL);
5209949213aSStephan Aßmus }
5219949213aSStephan Aßmus 
5229949213aSStephan Aßmus 
5239949213aSStephan Aßmus bool
IsDefinitionDelimiter()5249949213aSStephan Aßmus Text::IsDefinitionDelimiter()
5259949213aSStephan Aßmus {
5269949213aSStephan Aßmus 	return fText == ";";
5279949213aSStephan Aßmus }
5289949213aSStephan Aßmus 
5299949213aSStephan Aßmus 
5309949213aSStephan Aßmus void
Parse(char first,BDataIO & stream,char & last)531*365c228dSAugustin Cavalier Text::Parse(char first, BDataIO &stream, char &last)
5329949213aSStephan Aßmus {
5339949213aSStephan Aßmus 	char c = first;
5349949213aSStephan Aßmus 	if (c == '\0')
5359949213aSStephan Aßmus 		c = read_char(stream);
5369949213aSStephan Aßmus 
5379949213aSStephan Aßmus 	if (c == ';') {
5389949213aSStephan Aßmus 		// definition delimiter
5399949213aSStephan Aßmus 		fText.SetTo(";");
5409949213aSStephan Aßmus 		last = read_char(stream);
5419949213aSStephan Aßmus 		return;
5429949213aSStephan Aßmus 	}
5439949213aSStephan Aßmus 
5449949213aSStephan Aßmus 	const size_t kBufferSteps = 1;
5459949213aSStephan Aßmus 	size_t maxSize = kBufferSteps;
5469949213aSStephan Aßmus 	char *text = fText.LockBuffer(maxSize);
5479949213aSStephan Aßmus 	if (text == NULL)
5489949213aSStephan Aßmus 		throw (status_t)B_NO_MEMORY;
5499949213aSStephan Aßmus 
5509949213aSStephan Aßmus 	size_t position = 0;
5519949213aSStephan Aßmus 
5529949213aSStephan Aßmus 	while (true) {
5539949213aSStephan Aßmus 		if (c == '\\' || c == '}' || c == '{' || c == ';' || c == '\n' || c == '\r')
5549949213aSStephan Aßmus 			break;
5559949213aSStephan Aßmus 
5569949213aSStephan Aßmus 		if (position >= maxSize) {
5579949213aSStephan Aßmus 			fText.UnlockBuffer(position);
5589949213aSStephan Aßmus 			text = fText.LockBuffer(maxSize += kBufferSteps);
5599949213aSStephan Aßmus 			if (text == NULL)
5609949213aSStephan Aßmus 				throw (status_t)B_NO_MEMORY;
5619949213aSStephan Aßmus 		}
5629949213aSStephan Aßmus 
5639949213aSStephan Aßmus 		text[position++] = c;
5649949213aSStephan Aßmus 
5659949213aSStephan Aßmus 		c = read_char(stream);
5669949213aSStephan Aßmus 	}
5679949213aSStephan Aßmus 	fText.UnlockBuffer(position);
5689949213aSStephan Aßmus 
5699949213aSStephan Aßmus 	// ToDo: add support for different charsets - right now, only ASCII is supported!
5709949213aSStephan Aßmus 	//	To achieve this, we should just translate everything into UTF-8 here
5719949213aSStephan Aßmus 
5729949213aSStephan Aßmus 	last = c;
5739949213aSStephan Aßmus }
5749949213aSStephan Aßmus 
5759949213aSStephan Aßmus 
5769949213aSStephan Aßmus status_t
SetTo(const char * text)5779949213aSStephan Aßmus Text::SetTo(const char *text)
5789949213aSStephan Aßmus {
5799949213aSStephan Aßmus 	return fText.SetTo(text) != NULL ? B_OK : B_NO_MEMORY;
5809949213aSStephan Aßmus }
5819949213aSStephan Aßmus 
5829949213aSStephan Aßmus 
5839949213aSStephan Aßmus const char *
String() const5849949213aSStephan Aßmus Text::String() const
5859949213aSStephan Aßmus {
5869949213aSStephan Aßmus 	return fText.String();
5879949213aSStephan Aßmus }
5889949213aSStephan Aßmus 
5899949213aSStephan Aßmus 
5909949213aSStephan Aßmus uint32
Length() const5919949213aSStephan Aßmus Text::Length() const
5929949213aSStephan Aßmus {
5939949213aSStephan Aßmus 	return fText.Length();
5949949213aSStephan Aßmus }
5959949213aSStephan Aßmus 
5969949213aSStephan Aßmus 
5979949213aSStephan Aßmus //	#pragma mark -
5989949213aSStephan Aßmus 
5999949213aSStephan Aßmus 
Command()6009949213aSStephan Aßmus Command::Command()
6019949213aSStephan Aßmus 	:
6029949213aSStephan Aßmus 	fName(NULL),
6039949213aSStephan Aßmus 	fHasOption(false),
6049949213aSStephan Aßmus 	fOption(-1)
6059949213aSStephan Aßmus {
6069949213aSStephan Aßmus }
6079949213aSStephan Aßmus 
6089949213aSStephan Aßmus 
~Command()6099949213aSStephan Aßmus Command::~Command()
6109949213aSStephan Aßmus {
6119949213aSStephan Aßmus }
6129949213aSStephan Aßmus 
6139949213aSStephan Aßmus 
6149949213aSStephan Aßmus void
Parse(char first,BDataIO & stream,char & last)615*365c228dSAugustin Cavalier Command::Parse(char first, BDataIO &stream, char &last)
6169949213aSStephan Aßmus {
6179949213aSStephan Aßmus 	if (first == '\0')
6189949213aSStephan Aßmus 		first = read_char(stream);
6199949213aSStephan Aßmus 
6209949213aSStephan Aßmus 	if (first != '\\')
6219949213aSStephan Aßmus 		throw (status_t)B_BAD_TYPE;
6229949213aSStephan Aßmus 
6239949213aSStephan Aßmus 	// get name
6249949213aSStephan Aßmus 	char name[kCommandLength];
6259949213aSStephan Aßmus 	size_t length = 0;
6269949213aSStephan Aßmus 	char c;
6279949213aSStephan Aßmus 	while (isalpha(c = read_char(stream))) {
6289949213aSStephan Aßmus 		name[length++] = c;
6299949213aSStephan Aßmus 		if (length >= kCommandLength - 1)
6309949213aSStephan Aßmus 			throw (status_t)B_BAD_TYPE;
6319949213aSStephan Aßmus 	}
6329949213aSStephan Aßmus 
6339949213aSStephan Aßmus 	if (length == 0) {
6349949213aSStephan Aßmus 		if (c == '\n' || c == '\r') {
6359949213aSStephan Aßmus 			// we're a hard return
6369949213aSStephan Aßmus 			fName.SetTo("par");
6379949213aSStephan Aßmus 		} else
6389949213aSStephan Aßmus 			fName.SetTo(c, 1);
6399949213aSStephan Aßmus 
6409949213aSStephan Aßmus 		// read over character
6419949213aSStephan Aßmus 		c = read_char(stream);
6429949213aSStephan Aßmus 	} else
6439949213aSStephan Aßmus 		fName.SetTo(name, length);
6449949213aSStephan Aßmus 
645b70311a3SAxel Dörfler 	TRACE("command: %s\n", fName.String());
646b70311a3SAxel Dörfler 
6479949213aSStephan Aßmus 	// parse numeric option
6489949213aSStephan Aßmus 
6499949213aSStephan Aßmus 	if (c == '-')
6509949213aSStephan Aßmus 		c = read_char(stream);
6519949213aSStephan Aßmus 
6529949213aSStephan Aßmus 	last = c;
6539949213aSStephan Aßmus 
6549949213aSStephan Aßmus 	if (fName == "'") {
6559949213aSStephan Aßmus 		// hexadecimal
6569949213aSStephan Aßmus 		char bytes[2];
6579949213aSStephan Aßmus 		bytes[0] = read_char(stream);
6589949213aSStephan Aßmus 		bytes[1] = '\0';
6599949213aSStephan Aßmus 		BMemoryIO memory(bytes, 2);
6609949213aSStephan Aßmus 
6619949213aSStephan Aßmus 		SetOption(parse_integer(c, memory, last, 16));
6629949213aSStephan Aßmus 		last = read_char(stream);
6639949213aSStephan Aßmus 	} else {
6649949213aSStephan Aßmus 		// decimal
6659949213aSStephan Aßmus 		if (isdigit(c))
6669949213aSStephan Aßmus 			SetOption(parse_integer(c, stream, last));
6679949213aSStephan Aßmus 
6689949213aSStephan Aßmus 		// a space delimiter is eaten up by the command
6699949213aSStephan Aßmus 		if (isspace(last))
6709949213aSStephan Aßmus 			last = read_char(stream);
6719949213aSStephan Aßmus 	}
672b70311a3SAxel Dörfler 
673b70311a3SAxel Dörfler 	if (HasOption())
674b70311a3SAxel Dörfler 		TRACE("  option: %ld\n", fOption);
6759949213aSStephan Aßmus }
6769949213aSStephan Aßmus 
6779949213aSStephan Aßmus 
6789949213aSStephan Aßmus status_t
SetName(const char * name)6799949213aSStephan Aßmus Command::SetName(const char *name)
6809949213aSStephan Aßmus {
6819949213aSStephan Aßmus 	return fName.SetTo(name) != NULL ? B_OK : B_NO_MEMORY;
6829949213aSStephan Aßmus }
6839949213aSStephan Aßmus 
6849949213aSStephan Aßmus 
6859949213aSStephan Aßmus const char *
Name()6869949213aSStephan Aßmus Command::Name()
6879949213aSStephan Aßmus {
6889949213aSStephan Aßmus 	return fName.String();
6899949213aSStephan Aßmus }
6909949213aSStephan Aßmus 
6919949213aSStephan Aßmus 
6929949213aSStephan Aßmus void
UnsetOption()6939949213aSStephan Aßmus Command::UnsetOption()
6949949213aSStephan Aßmus {
6959949213aSStephan Aßmus 	fHasOption = false;
6969949213aSStephan Aßmus 	fOption = -1;
6979949213aSStephan Aßmus }
6989949213aSStephan Aßmus 
6999949213aSStephan Aßmus 
7009949213aSStephan Aßmus void
SetOption(int32 option)7019949213aSStephan Aßmus Command::SetOption(int32 option)
7029949213aSStephan Aßmus {
7039949213aSStephan Aßmus 	fOption = option;
7049949213aSStephan Aßmus 	fHasOption = true;
7059949213aSStephan Aßmus }
7069949213aSStephan Aßmus 
7079949213aSStephan Aßmus 
7089949213aSStephan Aßmus bool
HasOption() const7099949213aSStephan Aßmus Command::HasOption() const
7109949213aSStephan Aßmus {
7119949213aSStephan Aßmus 	return fHasOption;
7129949213aSStephan Aßmus }
7139949213aSStephan Aßmus 
7149949213aSStephan Aßmus 
7159949213aSStephan Aßmus int32
Option() const7169949213aSStephan Aßmus Command::Option() const
7179949213aSStephan Aßmus {
7189949213aSStephan Aßmus 	return fOption;
7199949213aSStephan Aßmus }
7209949213aSStephan Aßmus 
7219949213aSStephan Aßmus 
7229949213aSStephan Aßmus //	#pragma mark -
7239949213aSStephan Aßmus 
7249949213aSStephan Aßmus 
Iterator(Element & start,group_destination destination)7259949213aSStephan Aßmus Iterator::Iterator(Element &start, group_destination destination)
7269949213aSStephan Aßmus {
7279949213aSStephan Aßmus 	SetTo(start, destination);
7289949213aSStephan Aßmus }
7299949213aSStephan Aßmus 
7309949213aSStephan Aßmus 
7319949213aSStephan Aßmus void
SetTo(Element & start,group_destination destination)7329949213aSStephan Aßmus Iterator::SetTo(Element &start, group_destination destination)
7339949213aSStephan Aßmus {
7349949213aSStephan Aßmus 	fStart = &start;
7359949213aSStephan Aßmus 	fDestination = destination;
7369949213aSStephan Aßmus 
7379949213aSStephan Aßmus 	Rewind();
7389949213aSStephan Aßmus }
7399949213aSStephan Aßmus 
7409949213aSStephan Aßmus 
7419949213aSStephan Aßmus void
Rewind()7429949213aSStephan Aßmus Iterator::Rewind()
7439949213aSStephan Aßmus {
7449949213aSStephan Aßmus 	fStack.MakeEmpty();
7459949213aSStephan Aßmus 	fStack.Push(fStart);
7469949213aSStephan Aßmus }
7479949213aSStephan Aßmus 
7489949213aSStephan Aßmus 
7499949213aSStephan Aßmus bool
HasNext() const7509949213aSStephan Aßmus Iterator::HasNext() const
7519949213aSStephan Aßmus {
7529949213aSStephan Aßmus 	return !fStack.IsEmpty();
7539949213aSStephan Aßmus }
7549949213aSStephan Aßmus 
7559949213aSStephan Aßmus 
7569949213aSStephan Aßmus Element *
Next()7579949213aSStephan Aßmus Iterator::Next()
7589949213aSStephan Aßmus {
7599949213aSStephan Aßmus 	Element *element;
7609949213aSStephan Aßmus 
7619949213aSStephan Aßmus 	if (!fStack.Pop(&element))
7629949213aSStephan Aßmus 		return NULL;
7639949213aSStephan Aßmus 
7649949213aSStephan Aßmus 	Group *group = dynamic_cast<Group *>(element);
7659949213aSStephan Aßmus 	if (group != NULL
7669949213aSStephan Aßmus 		&& (fDestination == ALL_DESTINATIONS
7679949213aSStephan Aßmus 			|| fDestination == group->Destination())) {
7689949213aSStephan Aßmus 		// put this group's children on the stack in
7699949213aSStephan Aßmus 		// reverse order, so that we iterate over
7709949213aSStephan Aßmus 		// the tree in in-order
7719949213aSStephan Aßmus 
7729949213aSStephan Aßmus 		for (int32 i = group->CountElements(); i-- > 0;) {
7739949213aSStephan Aßmus 			fStack.Push(group->ElementAt(i));
7749949213aSStephan Aßmus 		}
7759949213aSStephan Aßmus 	}
7769949213aSStephan Aßmus 
7779949213aSStephan Aßmus 	return element;
7789949213aSStephan Aßmus }
7799949213aSStephan Aßmus 
7809949213aSStephan Aßmus 
7819949213aSStephan Aßmus //	#pragma mark -
7829949213aSStephan Aßmus 
7839949213aSStephan Aßmus 
Worker(RTF::Header & start)7849949213aSStephan Aßmus Worker::Worker(RTF::Header &start)
7859949213aSStephan Aßmus 	:
7869949213aSStephan Aßmus 	fStart(start)
7879949213aSStephan Aßmus {
7889949213aSStephan Aßmus }
7899949213aSStephan Aßmus 
7909949213aSStephan Aßmus 
~Worker()7919949213aSStephan Aßmus Worker::~Worker()
7929949213aSStephan Aßmus {
7939949213aSStephan Aßmus }
7949949213aSStephan Aßmus 
7959949213aSStephan Aßmus 
7969949213aSStephan Aßmus void
Dispatch(Element * element)7979949213aSStephan Aßmus Worker::Dispatch(Element *element)
7989949213aSStephan Aßmus {
7999949213aSStephan Aßmus 	if (RTF::Group *group = dynamic_cast<RTF::Group *>(element)) {
8009949213aSStephan Aßmus 		fSkip = false;
8019949213aSStephan Aßmus 		Group(group);
8029949213aSStephan Aßmus 
8039949213aSStephan Aßmus 		if (fSkip)
8049949213aSStephan Aßmus 			return;
8059949213aSStephan Aßmus 
8069949213aSStephan Aßmus 		for (int32 i = 0; (element = group->ElementAt(i)) != NULL; i++)
8079949213aSStephan Aßmus 			Dispatch(element);
8089949213aSStephan Aßmus 
8099949213aSStephan Aßmus 		GroupEnd(group);
8109949213aSStephan Aßmus 	} else if (RTF::Command *command = dynamic_cast<RTF::Command *>(element)) {
8119949213aSStephan Aßmus 		Command(command);
8129949213aSStephan Aßmus 	} else if (RTF::Text *text = dynamic_cast<RTF::Text *>(element)) {
8139949213aSStephan Aßmus 		Text(text);
8149949213aSStephan Aßmus 	}
8159949213aSStephan Aßmus }
8169949213aSStephan Aßmus 
8179949213aSStephan Aßmus 
8189949213aSStephan Aßmus void
Work()819*365c228dSAugustin Cavalier Worker::Work()
8209949213aSStephan Aßmus {
8219949213aSStephan Aßmus 	Dispatch(&fStart);
8229949213aSStephan Aßmus }
8239949213aSStephan Aßmus 
8249949213aSStephan Aßmus 
8259949213aSStephan Aßmus void
Group(RTF::Group * group)8269949213aSStephan Aßmus Worker::Group(RTF::Group *group)
8279949213aSStephan Aßmus {
8289949213aSStephan Aßmus }
8299949213aSStephan Aßmus 
8309949213aSStephan Aßmus 
8319949213aSStephan Aßmus void
GroupEnd(RTF::Group * group)8329949213aSStephan Aßmus Worker::GroupEnd(RTF::Group *group)
8339949213aSStephan Aßmus {
8349949213aSStephan Aßmus }
8359949213aSStephan Aßmus 
8369949213aSStephan Aßmus 
8379949213aSStephan Aßmus void
Command(RTF::Command * command)8389949213aSStephan Aßmus Worker::Command(RTF::Command *command)
8399949213aSStephan Aßmus {
8409949213aSStephan Aßmus }
8419949213aSStephan Aßmus 
8429949213aSStephan Aßmus 
8439949213aSStephan Aßmus void
Text(RTF::Text * text)8449949213aSStephan Aßmus Worker::Text(RTF::Text *text)
8459949213aSStephan Aßmus {
8469949213aSStephan Aßmus }
8479949213aSStephan Aßmus 
8489949213aSStephan Aßmus 
8499949213aSStephan Aßmus RTF::Header &
Start()8509949213aSStephan Aßmus Worker::Start()
8519949213aSStephan Aßmus {
8529949213aSStephan Aßmus 	return fStart;
8539949213aSStephan Aßmus }
8549949213aSStephan Aßmus 
8559949213aSStephan Aßmus 
8569949213aSStephan Aßmus void
Skip()8579949213aSStephan Aßmus Worker::Skip()
8589949213aSStephan Aßmus {
8599949213aSStephan Aßmus 	fSkip = true;
8609949213aSStephan Aßmus }
8619949213aSStephan Aßmus 
8629949213aSStephan Aßmus 
8639949213aSStephan Aßmus void
Abort(status_t status)8649949213aSStephan Aßmus Worker::Abort(status_t status)
8659949213aSStephan Aßmus {
8669949213aSStephan Aßmus 	throw status;
8679949213aSStephan Aßmus }
8689949213aSStephan Aßmus 
869