#help_index "DolDoc"

I64 ParseDocFlagSingle(CCompCtrl *cc, I64 *_de_flags, U32 *_type, Bool turn_on)
{
	I64				 res = -1;
	CHashGeneric	*tmph;

	if (cc->token == TK_IDENT && (tmph = HashFind(cc->cur_str, doldoc.hash, DHT_DOC_FLAG)))
	{
		res = tmph->user_data0;
		if (res < 64)
		{
			BEqual(_de_flags, res, turn_on);
			switch (res)
			{
				case DOCEf_BLINK:
				case DOCEf_INVERT:
				case DOCEf_UNDERLINE:
				case DOCEf_SEL:
					BEqual(_type, res, turn_on);
					break;
			}
		}
		Lex(cc);		//skip flag
	}

	return res;
}

I64 ParseDocFlags(CCompCtrl *cc, I64 *_de_flags, U32 *_type)
{
	I64  res = -1;
	Bool turn_on;

	while (TRUE)
	{
		if (cc->token == '+')
			turn_on = TRUE;
		else if (cc->token == '-')
			turn_on = FALSE;
		else
			break;
		Lex(cc);
		res = ParseDocFlagSingle(cc, _de_flags, _type, turn_on);
	}

	return res;
}

U8 *Doc2PlainText(CDoc *doc, CDocEntry *doc_e)
{//TODO: break strs
	I64 i, j, attr = doc_e->attr, t1, f1, de_flags, type;
	U8 *buf, *buf2;

	if (doc_e->type_u8 == DOCT_FOREGROUND && doc->flags & DOCF_COLOR_NAMES && 0 <= attr < COLORS_NUM)
	{
		buf = StrNew(DefineSub(attr, "ST_COLORS"));
		attr = DOC_DEFAULT;
	}
	else
		buf = StrNew(DefineSub(doc_e->type_u8, "ST_DOC_CMDS"));
	if (doc_e->type_u8 != DOCT_ERROR)
	{
		f1 = doldoc.default_de_flags[doc_e->type_u8];
		t1 = doc_e->type_u8 | doldoc.default_type_flags[doc_e->type_u8];

		de_flags = doc_e->de_flags & ~(DOCG_BL_IV_UL | DOCEF_SEL | DOCEF_HIGHLIGHT |
									   DOCEF_WORD_WRAP | DOCEF_SKIP | DOCEF_FILTER_SKIP);

		for (i = 0; i < DOCEf_FLAGS_NUM;i++)
			if (Bt(&f1, i) != Bt(&de_flags, i))
		{
				if (Bt(&de_flags, i))
				{
					if (!(1 << i & DOCEG_HAS_ARG))
					{
						buf2 =MStrPrint("%s+%Z", buf, i, "ST_DOC_FLAGS");
						Free(buf);
						buf = buf2;
					}
				}
				else
				{
					buf2 = MStrPrint("%s-%Z", buf, i, "ST_DOC_FLAGS");
					Free(buf);
					buf = buf2;
				}
			}
		type = doc_e->type & ~DOCET_SEL;
		for (i = DOCEt_BLINK; i <= DOCEt_UNDERLINE; i++)
			if (Bt(&t1, i) != Bt(&type, i))
			{
				if (Bt(&type, i))
					buf2 = MStrPrint("%s+%Z", buf, i, "ST_DOC_FLAGS");
				else
					buf2 = MStrPrint("%s-%Z", buf, i, "ST_DOC_FLAGS");
				Free(buf);
				buf = buf2;
			}
		buf2 = MStrPrint("%s,", buf);
		Free(buf);
		buf = buf2;
		switch [doc_e->type_u8]
		{
			case DOCT_HEX_ED:
				buf2 = MStrPrint("%s%d,", buf, doc_e->len);
				Free(buf);
				buf  =buf2;
				buf2 = MStrPrint("%s%d,", buf, doc_e->hex_ed_width);
				Free(buf);
				buf = buf2;
				break;

			case DOCT_FOREGROUND:
			case DOCT_BACKGROUND:
			case DOCT_DEFAULT_FOREGROUND:
			case DOCT_DEFAULT_BACKGROUND:
				if (doc->flags & DOCF_COLOR_NAMES && 0 <= attr < COLORS_NUM)
				{
					buf2 = MStrPrint("%s%Z,", buf, doc_e->attr, "ST_COLORS");
					Free(buf);
					buf = buf2;
					break;
				}
			case DOCT_PAGE_LEN:
			case DOCT_LEFT_MARGIN:
			case DOCT_RIGHT_MARGIN:
			case DOCT_HEADER:
			case DOCT_FOOTER:
			case DOCT_INDENT:
			case DOCT_WORD_WRAP:
			case DOCT_HIGHLIGHT:
			case DOCT_BLINK:
			case DOCT_INVERT:
			case DOCT_UNDERLINE:
			case DOCT_SHIFTED_X:
			case DOCT_SHIFTED_Y:
				if (attr != DOC_DEFAULT)
				{
					buf2 = MStrPrint("%s%d,", buf, doc_e->attr);
					Free(buf);
					buf = buf2;
				}
			case DOCT_TYPES_NUM - 1: //nobound switch
				break;
		}
		de_flags = doc_e->de_flags & DOCEG_HAS_ARG;
		while (de_flags)
		{
			j = Bsf(de_flags);
			Btr(&de_flags, j);
			switch [j]
			{
				case DOCEf_TAG:
					if (doc_e->type_u8 == DOCT_DATA || doc_e->type_u8 == DOCT_MACRO &&
						(doc_e->de_flags & DOCEF_LEFT_MACRO &&
						 !StrCompare(doc_e->tag, doc_e->left_macro) ||
						 doc_e->de_flags & DOCEF_RIGHT_MACRO &&
						 !StrCompare(doc_e->tag, doc_e->right_macro)) ||
						doc_e->de_flags & DOCEF_LIST && !StrCompare(doc_e->tag, "[]") && doc_e->de_flags & DOCEF_DEFINE)
					{
						buf2 = buf;
						buf = NULL;
					}
					else
					{
						if (doc_e->type_u8 == DOCT_CHECK_BOX)
						{
							if (StrLen(doc_e->tag) >= 4)
								buf2 = doc_e->tag + 4;
							else
								buf2 = "";
						}
						else if (doc_e->de_flags & DOCEF_TREE)
						{
							if (StrLen(doc_e->tag) >= 3)
								buf2 = doc_e->tag + 3;
							else
								buf2 = "";
						}
						else
							buf2 = doc_e->tag;
						if (Bt(&doldoc.default_de_flags[doc_e->type_u8], DOCEf_TAG))
							buf2 = MStrPrint("%s\"%$$Q\",", buf, buf2);
						else
							buf2 = MStrPrint("%sT=\"%$$Q\",", buf, buf2);
					}
					break;
				case DOCEf_LEN:
					buf2 = MStrPrint("%sLEN=%d,", buf, doc_e->len);
					break;

				case DOCEf_AUX_STR:
					buf2 = MStrPrint("%sA=\"%$$Q\",", buf, doc_e->aux_str);
					break;

				case DOCEf_DEFINE:
					buf2 = MStrPrint("%sD=\"%$$Q\",", buf, doc_e->define_str);
					break;

				case DOCEf_HTML_LINK:
					buf2 = MStrPrint("%sHTML=\"%$$Q\",", buf, doc_e->html_link);
					break;

				case DOCEf_LEFT_EXP:
					buf2 = MStrPrint("%sLE=%d,", buf, doc_e->left_exp);
					break;

				case DOCEf_LEFT_MACRO:
					buf2 = MStrPrint("%sLM=\"%$$Q\",", buf, doc_e->left_macro);
					break;

				case DOCEf_RIGHT_EXP:
					buf2 = MStrPrint("%sRE=%d,", buf, doc_e->right_exp);
					break;

				case DOCEf_RIGHT_MACRO:
					buf2 = MStrPrint("%sRM=\"%$$Q\",", buf, doc_e->right_macro);
					break;

				case DOCEf_HAS_BIN:
					buf2 = MStrPrint("%sBI=%d,", buf, doc_e->bin_num);
					break;

				case DOCEf_BIN_PTR_LINK:
					buf2 = MStrPrint("%sBP=\"%$$Q\",", buf, doc_e->bin_ptr_link);
					break;

				case DOCEf_RAW_TYPE:
					if (doc_e->type_u8 == DOCT_CHECK_BOX && doc_e->raw_type != RT_I8 ||
							doc_e->type_u8 != DOCT_CHECK_BOX && doc_e->raw_type != RT_I64)

						buf2 = MStrPrint("%sRT=%Z,", buf, doc_e->raw_type, "ST_RAW_TYPES");
					break;

				case DOCEf_SHIFTED_X:
					j = doc_e->type.u16[1] & 0x1F;
					if (j & 0x10)
						j |= 0xFFFFFFF0;
					buf2 = MStrPrint("%sSX=%d,", buf, j);
					break;

				case DOCEf_SHIFTED_Y:
					j = doc_e->type >> 21 & 0x1F;
					if (j & 0x10)
						j |= 0xFFFFFFF0;
					buf2 = MStrPrint("%sSY=%d,", buf, j);
					break;

				case DOCEf_SCROLLING_X:
					buf2 = MStrPrint("%sSCX=%d,", buf, doc_e->scroll_len);
					break;

				case DOCEf_USER_DATA:
					buf2 = MStrPrint("%sU=0x%X,", buf, doc_e->user_data);
					break;

				case DOCEf_FLAGS_NUM - 1: //nobound switch
					break;
			}
			Free(buf);
			buf = buf2;
		}
		buf[StrLen(buf) - 1] = 0;  //Kill last comma
	}
	buf2 = StrNew(buf, doc->mem_task); //exact allocation
	Free(buf);

	return buf2;
}

CDocEntry *ParseDollarCmd(CDoc *doc, U8 *st)
{//Uses $LK-UL,"Lex",A="MN:Lex"$() to parse a string and make Doc entries.
	I64				 i, j, de_flags, processed_flags, attr = DOC_DEFAULT;
	U8				*ptr, *st2;
	CDocEntry		*doc_e = NULL;
	CHashGeneric	*tmph;
	CCompCtrl		*cc = CompCtrlNew(st, CCF_DONT_FREE_BUF);
	CHashTable		*old_hash_table_list = cc->htc.hash_table_list;

	try
	{
		cc->htc.hash_table_list = NULL;
		if (Lex(cc) == TK_IDENT)
		{
			if (tmph = HashFind(cc->cur_str, doldoc.hash, DHT_DOC_CMD | DHT_COLOR))
			{
				if (tmph->type & DHT_DOC_CMD)
					i = tmph->user_data0;
				else
				{//DHT_COLOR
					i = DOCT_FOREGROUND;
					attr = tmph->user_data0;
				}
			}
			else
				goto pd_err;
			Lex(cc); //skip cmd code
			doc_e = CAlloc(sizeof(CDocEntry), doc->mem_task);
			doc_e->type		=  i;
			doc_e->de_flags	=  doldoc.default_de_flags[i];
			doc_e->type		|= doldoc.default_type_flags[i];
			doc_e->raw_type	=  RT_I64;
			doc_e->len		=  DOCE_LEN_DEFAULT;
			j=ParseDocFlags(cc, &doc_e->de_flags, &doc_e->type);
			cc->htc.hash_table_list = old_hash_table_list;
			switch [i]
			{
				case DOCT_CHECK_BOX:
					doc_e->raw_type = RT_I8;
					break;

				case DOCT_HEX_ED:
					while (cc->token == ',')
						Lex(cc);
					if (cc->token)
						doc_e->len = LexExpressionI64(cc);
					else
						goto pd_err;
					while (cc->token == ',')
						Lex(cc);
					if (cc->token)
						doc_e->hex_ed_width = LexExpressionI64(cc);
					else
						goto pd_err;
					break;

				case DOCT_PAGE_LEN:
				case DOCT_LEFT_MARGIN:
				case DOCT_RIGHT_MARGIN:
				case DOCT_HEADER:
				case DOCT_FOOTER:
				case DOCT_INDENT:
				case DOCT_FOREGROUND:
				case DOCT_BACKGROUND:
				case DOCT_DEFAULT_FOREGROUND:
				case DOCT_DEFAULT_BACKGROUND:
				case DOCT_WORD_WRAP:
				case DOCT_HIGHLIGHT:
				case DOCT_BLINK:
				case DOCT_INVERT:
				case DOCT_UNDERLINE:
				case DOCT_SHIFTED_X:
				case DOCT_SHIFTED_Y:
					while (cc->token == ',')
						Lex(cc);
					if (cc->token)
						doc_e->attr = LexExpressionI64(cc);
					else
						doc_e->attr = attr;
					break;
#assert DOCT_ERROR == DOCT_TYPES_NUM - 1

				case DOCT_ERROR:
					goto pd_err;
			}

			processed_flags = 0;
			while (TRUE)
			{
				cc->htc.hash_table_list = NULL;
				while (cc->token == ',')
					Lex(cc);
				cc->htc.hash_table_list = old_hash_table_list;
				j=ParseDocFlagSingle(cc, &doc_e->de_flags, &doc_e->type, TRUE);
				if (!(de_flags = ~processed_flags & doc_e->de_flags & DOCEG_HAS_ARG))
					break;
				if (cc->token == '=')
					Lex(cc);
				else
					j = Bsf(de_flags);
				if (j < 0 || Bts(&processed_flags, j))
					goto pd_err;
				switch [j]
				{//TODO: Might check for expression errors
					case DOCEf_TAG:
						if (!doc_e->tag)
						{
//If a $$MA,LM=""$$, Tag is filled when the LM is processed.
							//if doc_e->df_flags&DOCEF_LIST,
							// Tag is filled when the Define is processed.
							//(The default_flag1.tag calls this after.)
							if (cc->token == TK_STR)
							{
								st2 = LexExtStr(cc);
								if (i == DOCT_CHECK_BOX)
								{
									st = MStrPrint("[X] %s", st2);
									Free(st2);
									doc_e->min_col = 1;
								}
								else if (doc_e->de_flags & DOCEF_LIST)
								{
									if (*st2 != '[')
									{
										st = MStrPrint("[%s]", st2);
										Free(st2);
									}
									else
										st = st2;
									doc_e->min_col = 1;
								}
								else if (doc_e->de_flags & DOCEF_TREE)
								{
									st = MStrPrint("+] %s", st2);
									Free(st2);
									doc_e->min_col = 1;
								}
								else
									st = st2;
								doc_e->tag = StrNew(st, doc->mem_task);
								Free(st);
							}
							else
								goto pd_err;
						}
						break;

					case DOCEf_LEN:
						if (cc->token)
						{
							doc_e->len		=  LexExpression(cc);
							doc_e->de_flags	&= ~DOCEF_DEFAULT_LEN;
						}
						else
							goto pd_err;
						break;

					case DOCEf_AUX_STR:
						if (cc->token == TK_STR)
						{
							st2 = LexExtStr(cc);
							doc_e->aux_str = StrNew(st2, doc->mem_task);
							Free(st2);
//$AN,"Anchor",A="DataTagWidth"$
							if (i == DOCT_DATA)
							{ //See $LK,"DocForm",A="MN:DocForm"$()
								if (ptr = StrMatch(":", doc_e->aux_str))
									doc_e->min_col = ptr - doc_e->aux_str + 1;

								doc_e->tag = MAlloc(doc_e->len + doc_e->min_col + 2, doc->mem_task); //+2 because "_\0"
							}
						}
						else
							goto pd_err;
						break;

					case DOCEf_DEFINE:
						if (cc->token == TK_STR)
						{
							st2 = LexExtStr(cc);
							doc_e->define_str = StrNew(st2, doc->mem_task);
							Free(st2);
							if (doc_e->de_flags & DOCEF_LIST && !doc_e->tag)
								doc_e->tag = StrNew("[]", doc->mem_task);
						}
						else
							goto pd_err;
						break;

					case DOCEf_HTML_LINK:
						if (cc->token == TK_STR)
						{
							st2 = LexExtStr(cc);
							doc_e->html_link = StrNew(st2, doc->mem_task);
							Free(st2);
						}
						else
							goto pd_err;
						break;

					case DOCEf_LEFT_EXP:
						if (cc->token)
							doc_e->left_exp = LexExpression(cc);
						else
							goto pd_err;
						break;

					case DOCEf_LEFT_MACRO:
						if (cc->token == TK_STR)
						{
							st2 = LexExtStr(cc);
							doc_e->left_macro = StrNew(st2, doc->mem_task);
							Free(st2);
							if (i == DOCT_MACRO && !doc_e->tag)
								doc_e->tag = StrNew(doc_e->left_macro, doc->mem_task);
						}
						else
							goto pd_err;
						break;

					case DOCEf_RIGHT_EXP:
						if (cc->token)
							doc_e->right_exp = LexExpression(cc);
						else
							goto pd_err;
						break;

					case DOCEf_RIGHT_MACRO:
						if (cc->token == TK_STR)
						{
							st2 = LexExtStr(cc);
							doc_e->right_macro = StrNew(st2, doc->mem_task);
							Free(st2);
							if (i == DOCT_MACRO && !doc_e->tag)
								doc_e->tag = StrNew(doc_e->right_macro, doc->mem_task);
						}
						else
							goto pd_err;
						break;

					case DOCEf_HAS_BIN:
						if (cc->token)
							doc_e->bin_num = LexExpressionI64(cc);
						else
							goto pd_err;
						break;

					case DOCEf_BIN_PTR_LINK:
						if (cc->token == TK_STR)
						{
							st2 = LexExtStr(cc);
							doc_e->bin_ptr_link = StrNew(st2, doc->mem_task);
							Free(st2);
							if (!DocBinPtrReset(doc, doc_e))
								doc_e->type = DOCT_ERROR;
						}
						else
							goto pd_err;
						break;

					case DOCEf_RAW_TYPE:
						if (cc->token == TK_IDENT)
						{
							j = DefineMatch(cc->cur_str, "ST_RAW_TYPES");
							if (j < 0)
								goto pd_err;
							doc_e->raw_type	=  j;
							doc_e->de_flags	&= ~DOCEF_DEFAULT_RAW_TYPE;
							Lex(cc);
						}
						else
							goto pd_err;
						break;

					case DOCEf_SHIFTED_X:
						if (cc->token)
							doc_e->type |= (LexExpressionI64(cc) & 0x1F) << 16;
						else
							goto pd_err;
						break;

					case DOCEf_SHIFTED_Y:
						if (cc->token)
							doc_e->type |= (LexExpressionI64(cc) & 0x1F) << 21;
						else
							goto pd_err;
						break;

					case DOCEf_SCROLLING_X:
						if (cc->token)
							doc_e->scroll_len = LexExpressionI64(cc);
						else
							goto pd_err;
						break;

					case DOCEf_USER_DATA:
						if (cc->token)
							doc_e->user_data = LexExpression(cc);
						else
							goto pd_err;
						break;

					case DOCEf_FLAGS_NUM - 1: //nobound switch
						break;
				}
			}
		}
		else
		{
pd_err:
			if (!doc_e)
				doc_e = CAlloc(sizeof(CDocEntry), doc->mem_task);
			doc_e->type		= DOCT_ERROR;
			doc_e->de_flags	= 0;
		}
		if (doc_e->de_flags & DOCEF_LIST && (doc_e->de_flags & DOCEF_REMALLOC_DATA || !(doc_e->de_flags & DOCEF_DEREF_DATA)))
		{
			DocDataScan(doc, doc_e);
			DocDataFormat(doc, doc_e);
		}
		CompCtrlDel(cc);
	}
	catch
	{
		Fs->catch_except = TRUE;
		if (!doc_e)
			doc_e = CAlloc(sizeof(CDocEntry), doc->mem_task);
		doc_e->type		= DOCT_ERROR;
		doc_e->de_flags	= 0;
	}

	return doc_e;
}

U0 DocEntryToggle(CDoc *doc)
{
	Bool 		 unlock = DocLock(doc), old_color_names;
	CDocEntry	*doc_ce = doc->cur_entry, *cl1, *doc_ce2;
	U8			 ch, *st, *st2;
	I64			 i, j, k;

	if (doc_ce != doc && !(doc->flags & (DOCF_PLAIN_TEXT | DOCF_PLAIN_TEXT_TABS)))
	{
		if (doc_ce->type_u8 == DOCT_TEXT &&
			!(doc_ce->de_flags &
			 ~(DOCEF_TAG | DOCG_BL_IV_UL | DOCEF_WORD_WRAP | DOCEF_HIGHLIGHT | DOCEF_SKIP | DOCEF_FILTER_SKIP)) &&
			!(doc_ce->type&DOCG_BL_IV_UL))
		{
			doc_ce2 = doc_ce->last;
			for (k = 0; k < 20; k++)
			{
				if (doc_ce2 != doc)
				{
					cl1 = doc_ce2->last;
					if (doc_ce2->type_u8 == DOCT_TEXT &&
						doc_ce->de_flags == doc_ce2->de_flags &&
						doc_ce->type == doc_ce2->type)
					{
						i = StrLen(doc_ce2->tag);
						j = StrLen(doc_ce->tag);
						st = MAlloc(i + j + 1, doc->mem_task);
						MemCopy(st, doc_ce2->tag, i);
						MemCopy(st + i, doc_ce->tag, j + 1);
						Free(doc_ce->tag);
						doc_ce->tag		=  st;
						doc_ce->max_col	=  i + j;
						doc->cur_col	+= i;
						DocEntryDel(doc, doc_ce2);
					}
					else if (doc_ce2->type_u8 == DOCT_SOFT_NEW_LINE)
						DocEntryDel(doc, doc_ce2);
					else
						break;
					doc_ce2 = cl1;
				}
				else
					break;
			}
			doc_ce2 = doc_ce->next;
			for (k = 0; k < 20; k++)
			{
				if (doc_ce2 != doc)
				{
					cl1 = doc_ce2->next;
					if (doc_ce2->type_u8 == DOCT_TEXT &&
						doc_ce->de_flags == doc_ce2->de_flags &&
						doc_ce->type == doc_ce2->type)
					{
						i = StrLen(doc_ce->tag);
						j = StrLen(doc_ce2->tag);
						st = MAlloc(i + j + 1, doc->mem_task);
						MemCopy(st, doc_ce->tag, i);
						MemCopy(st + i, doc_ce2->tag, j + 1);
						Free(doc_ce->tag);
						doc_ce->tag		= st;
						doc_ce->max_col	= i + j;
						DocEntryDel(doc, doc_ce2);
					}
					else if (doc_ce2->type_u8 == DOCT_SOFT_NEW_LINE)
						DocEntryDel(doc, doc_ce2);
					else
						break;
					doc_ce2 = cl1;
				}
				else
					break;
			}
			i = doc->cur_col;
			while (i > doc_ce->min_col && doc_ce->tag[i] != '$$')
				i--;
			j = doc->cur_col + 1;
			while (j < doc_ce->max_col && doc_ce->tag[j] != '$$')
				j++;
			if (i < j - 1 && doc_ce->min_col <= i < j < doc_ce->max_col && doc_ce->tag[i] == '$$' && doc_ce->tag[j] == '$$')
			{
				ch = doc_ce->tag[j + 1];
				doc_ce->tag[j + 1] = 0;
				st = StrNew(doc_ce->tag + i);
				doc_ce->tag[j + 1] = ch;
				StrCopy(doc_ce->tag + i, doc_ce->tag + j + 1);
				doc->cur_col = i;
				st2 = MStrPrint("%q", st);
				if (doc_ce = DocPrint(doc, st2))
				{
					doc->cur_entry	= doc_ce;
					doc->cur_col	= doc_ce->min_col;
				}
				Free(st);
				Free(st2);
			}
		}
		else
		{
			old_color_names = LBts(&doc->flags, DOCf_COLOR_NAMES);
			st = Doc2PlainText(doc, doc_ce);
			LBEqual(&doc->flags, DOCf_COLOR_NAMES, old_color_names);
			DocEntryDel(doc, doc_ce);
			DocPrint(doc, "$$$$%$$Q$$$$", st);
		}
		DocRecalc(doc);
	}
	if (unlock)
		DocUnlock(doc);
}

U0 DocFlagsToggle(CDoc *doc, I64 tog_flags)
{
	Bool		 unlock = DocLock(doc);
	I64			 size, flags = doc->flags ^ tog_flags;
	U8			*st;
	CDocUndo	*u_next, *u_last;

	doc->flags = doc->flags & ~DOCF_NO_CURSOR | DOCF_COLOR_NAMES;
	st = DocSave(doc, &size);

	u_next = doc->undo_head.next;
	u_last = doc->undo_head.last;
	doc->undo_head.next = doc->undo_head.last = &doc->undo_head;

	DocReset(doc, TRUE);
	doc->undo_head.next = u_next;
	doc->undo_head.last = u_last;
	DocUndoCountSet(doc);
	doc->flags = flags & ~(DOCF_NO_CURSOR | DOCG_BL_IV_UL | DOCF_WORD_WRAP);
	DocLoad(doc, st, size);
	doc->flags |= flags & DOCF_NO_CURSOR;
	DocCenter(doc);
	if (unlock)
		DocUnlock(doc);
	Free(st);
}
