xref: /plan9/sys/src/cmd/aux/antiword/xml.c (revision 25b329d522281a8cdd35da0dcc08c3fc621059a9)
1 /*
2  * xml.c
3  * Copyright (C) 2002-2005 A.J. van Os; Released under GNU GPL
4  *
5  * Description:
6  * Functions to deal with the XML/DocBook format
7  *
8  */
9 
10 #include <string.h>
11 #include "antiword.h"
12 
13 
14 #define vAddEndTagsUntil1(p,t)	vAddEndTagsUntil2(p,t,TAG_NOTAG)
15 
16 #if defined(DEBUG)
17 #define vStackTrace()	__vStackTrace(__LINE__)
18 #else
19 #define vStackTrace()	/* EMPTY */
20 #endif /* DEBUG */
21 
22 /* The character set */
23 static encoding_type	eEncoding = encoding_neutral;
24 /* Word version */
25 static int	iWordVersion = -1;
26 /* Special treatment for files from Word 4/5/6 on an Apple Macintosh */
27 static BOOL	bOldMacFile = FALSE;
28 /* Text is emphasised */
29 static BOOL	bEmphasisOpen = FALSE;
30 /* Text is superscript */
31 static BOOL	bSuperscriptOpen = FALSE;
32 /* Text is subscript */
33 static BOOL	bSubscriptOpen = FALSE;
34 /* Title is open */
35 static BOOL	bTitleOpen = FALSE;
36 /* Table is open */
37 static BOOL	bTableOpen = FALSE;
38 /* Footnote is open */
39 static BOOL	bFootnoteOpen = FALSE;
40 /* Current paragraph level */
41 static UINT	uiParagraphLevel = 0;
42 /* Current list level */
43 static UINT	uiListLevel = 0;
44 /* Current list level is still empty */
45 static BOOL	bEmptyListLevel = TRUE;
46 /* Current header level */
47 static USHORT	usHeaderLevelCurrent = 0;
48 /* Current header level is still empty */
49 static BOOL	bEmptyHeaderLevel = TRUE;
50 /* Number of columns in the current table */
51 static int	iTableColumnsCurrent = 0;
52 /* Footnote number */
53 static UINT	uiFootnoteNumber = 0;
54 
55 /* Constants for the stack */
56 #define INITIAL_STACK_SIZE	10
57 #if defined(DEBUG)
58 #define EXTENSION_STACK_SIZE	 2
59 #else
60 #define EXTENSION_STACK_SIZE	10
61 #endif /* DEBUG */
62 
63 /* Variables for the stack */
64 static UCHAR	*aucStack = NULL;
65 static size_t	tStacksize = 0;
66 static size_t	tStackNextFree = 0;
67 
68 /* Constants for the tags */
69 #define TAG_NOTAG		(UCHAR)0
70 #define TAG_AUTHOR		(UCHAR)1
71 #define TAG_BEGINPAGE		(UCHAR)2
72 #define TAG_BOOK		(UCHAR)3
73 #define TAG_BOOKINFO		(UCHAR)4
74 #define TAG_CHAPTER		(UCHAR)5
75 #define TAG_COLSPEC		(UCHAR)6
76 #define TAG_CORPNAME		(UCHAR)7
77 #define TAG_DATE		(UCHAR)8
78 #define TAG_EMPHASIS		(UCHAR)9
79 #define TAG_ENTRY		(UCHAR)10
80 #define TAG_FILENAME		(UCHAR)11
81 #define TAG_FOOTNOTE		(UCHAR)12
82 #define TAG_INFORMALTABLE	(UCHAR)13
83 #define TAG_ITEMIZEDLIST	(UCHAR)14
84 #define TAG_LISTITEM		(UCHAR)15
85 #define TAG_ORDEREDLIST		(UCHAR)16
86 #define TAG_PARA		(UCHAR)17
87 #define TAG_ROW			(UCHAR)18
88 #define TAG_SECT1		(UCHAR)19
89 #define TAG_SECT2		(UCHAR)20
90 #define TAG_SECT3		(UCHAR)21
91 #define TAG_SECT4		(UCHAR)22
92 #define TAG_SECT5		(UCHAR)23
93 #define TAG_SUBSCRIPT		(UCHAR)24
94 #define TAG_SUBTITLE		(UCHAR)25
95 #define TAG_SUPERSCRIPT		(UCHAR)26
96 #define TAG_SURNAME		(UCHAR)27
97 #define TAG_TBODY		(UCHAR)28
98 #define TAG_TGROUP		(UCHAR)29
99 #define TAG_TITLE		(UCHAR)30
100 
101 typedef struct docbooktags_tag {
102 	UCHAR	ucTagnumber;
103 	char	szTagname[15];
104 	BOOL	bAddNewlineStart;
105 	BOOL	bAddNewlineEnd;
106 } docbooktags_type;
107 
108 static const docbooktags_type atDocBookTags[] = {
109 	{	TAG_NOTAG, 		"!ERROR!",	TRUE,	TRUE	},
110 	{	TAG_AUTHOR,		"author",	TRUE,	TRUE	},
111 	{	TAG_BEGINPAGE,		"beginpage",	TRUE,	TRUE	},
112 	{	TAG_BOOK, 		"book",		TRUE,	TRUE	},
113 	{	TAG_BOOKINFO, 		"bookinfo",	TRUE,	TRUE	},
114 	{	TAG_CHAPTER, 		"chapter",	TRUE,	TRUE	},
115 	{	TAG_COLSPEC,		"colspec",	TRUE,	TRUE	},
116 	{	TAG_CORPNAME,		"corpname",	FALSE,	FALSE	},
117 	{	TAG_DATE,		"date",		FALSE,	FALSE	},
118 	{	TAG_EMPHASIS,		"emphasis",	FALSE,	FALSE	},
119 	{	TAG_ENTRY,		"entry",	TRUE,	TRUE	},
120 	{	TAG_FILENAME,		"filename",	FALSE,	FALSE	},
121 	{	TAG_FOOTNOTE,		"footnote",	FALSE,	FALSE	},
122 	{	TAG_INFORMALTABLE,	"informaltable",TRUE,	TRUE	},
123 	{	TAG_ITEMIZEDLIST,	"itemizedlist",	TRUE,	TRUE	},
124 	{	TAG_LISTITEM,		"listitem",	TRUE,	TRUE	},
125 	{	TAG_ORDEREDLIST,	"orderedlist",	TRUE,	TRUE	},
126 	{	TAG_PARA, 		"para",		TRUE,	TRUE	},
127 	{	TAG_ROW,		"row",		TRUE,	TRUE	},
128 	{	TAG_SECT1, 		"sect1",	TRUE,	TRUE	},
129 	{	TAG_SECT2, 		"sect2",	TRUE,	TRUE	},
130 	{	TAG_SECT3, 		"sect3",	TRUE,	TRUE	},
131 	{	TAG_SECT4, 		"sect4",	TRUE,	TRUE	},
132 	{	TAG_SECT5, 		"sect5",	TRUE,	TRUE	},
133 	{	TAG_SUBSCRIPT,		"subscript",	FALSE,	FALSE	},
134 	{	TAG_SUBTITLE,		"subtitle",	FALSE,	FALSE	},
135 	{	TAG_SUPERSCRIPT,	"superscript",	FALSE,	FALSE	},
136 	{	TAG_SURNAME,		"surname",	FALSE,	FALSE	},
137 	{	TAG_TBODY,		"tbody",	TRUE,	TRUE	},
138 	{	TAG_TGROUP,		"tgroup",	TRUE,	TRUE	},
139 	{	TAG_TITLE, 		"title",	FALSE,	FALSE	},
140 };
141 
142 static void	vAddStartTag(diagram_type *, UCHAR, const char *);
143 static void	vAddEndTag(diagram_type *, UCHAR);
144 static void	vAddCombinedTag(diagram_type *, UCHAR, const char *);
145 static void	vPrintChar(diagram_type *, char);
146 
147 
148 #if defined(DEBUG)
149 /*
150  * vCheckTagTable - check the tag table
151  */
152 static void
vCheckTagTable(void)153 vCheckTagTable(void)
154 {
155 	size_t	tIndex;
156 
157 	for (tIndex = 0; tIndex < elementsof(atDocBookTags); tIndex++) {
158 		if (tIndex != (size_t)atDocBookTags[tIndex].ucTagnumber) {
159 			DBG_DEC(tIndex);
160 			werr(1, "Array atDocBookTags is broken");
161 		}
162 	}
163 } /* end of vCheckTagTable */
164 
165 /*
166  * __vStackTrace - show a stack trace
167  */
168 static void
__vStackTrace(int iLine)169 __vStackTrace(int iLine)
170 {
171 	int	iIndex;
172 
173 	fprintf(stderr, "%s[%3d]:\n", __FILE__, iLine);
174 
175 	if (tStackNextFree == 0) {
176 		fprintf(stderr, "The stack is empty\n");
177 		return;
178 	}
179 	for (iIndex = (int)tStackNextFree - 1; iIndex >= 0; iIndex--) {
180 		fprintf(stderr, "%2d: %2d: '%s'\n",
181 			iIndex,
182 			(int)atDocBookTags[(UINT)aucStack[iIndex]].ucTagnumber,
183 			atDocBookTags[(UINT)aucStack[iIndex]].szTagname);
184 	}
185 } /* end of __vStackTrace */
186 #endif /* DEBUG */
187 
188 /*
189  * vPushStack - push a tag onto the stack
190  */
191 static void
vPushStack(UCHAR ucTag)192 vPushStack(UCHAR ucTag)
193 {
194 	fail(tStackNextFree > tStacksize);
195 
196 	if (tStackNextFree == tStacksize) {
197 		/* The stack is full; enlarge the stack */
198 		tStacksize += EXTENSION_STACK_SIZE;
199 		aucStack = xrealloc(aucStack, tStacksize * sizeof(UCHAR));
200 		DBG_DEC(tStacksize);
201 	}
202 
203 	fail(tStackNextFree >= tStacksize);
204 
205 	aucStack[tStackNextFree++] = ucTag;
206 } /* end of vPushStack */
207 
208 /*
209  * vPopStack - pop a tag from the stack
210  */
211 static UCHAR
ucPopStack(void)212 ucPopStack(void)
213 {
214 	DBG_DEC_C(tStackNextFree > tStacksize, tStackNextFree);
215 	DBG_DEC_C(tStackNextFree > tStacksize, tStacksize);
216 	fail(tStackNextFree > tStacksize);
217 	fail(tStackNextFree == 0);
218 
219 	if (tStackNextFree == 0) {
220 		werr(1, "The stack is empty, unable to continue");
221 		return TAG_NOTAG;
222 	}
223 	return aucStack[--tStackNextFree];
224 } /* end of ucPopStack */
225 
226 /*
227  * vReadStack - read a tag from the top of the stack
228  */
229 static UCHAR
ucReadStack(void)230 ucReadStack(void)
231 {
232 	DBG_DEC_C(tStackNextFree > tStacksize, tStackNextFree);
233 	DBG_DEC_C(tStackNextFree > tStacksize, tStacksize);
234 	fail(tStackNextFree > tStacksize);
235 
236 	if (tStackNextFree == 0) {
237 		/* The stack is empty */
238 		return TAG_NOTAG;
239 	}
240 	return aucStack[tStackNextFree - 1];
241 } /* end of ucReadStack */
242 
243 /*
244  * vPrintLevel - print the tag level
245  */
246 static void
vPrintLevel(FILE * pOutFile)247 vPrintLevel(FILE *pOutFile)
248 {
249 	size_t	tIndex;
250 
251 	fail(pOutFile == NULL);
252 
253 	for (tIndex = 0; tIndex < tStackNextFree; tIndex++) {
254 		(void)putc(' ', pOutFile);
255 	}
256 } /* end of vPrintLevel */
257 
258 /*
259  * vPrintFootnote - print a footnote
260  */
261 static void
vPrintFootnote(diagram_type * pDiag,UINT uiFootnoteIndex)262 vPrintFootnote(diagram_type *pDiag, UINT uiFootnoteIndex)
263 {
264 	const char	*szText, *pcTmp;
265 	BOOL	bSuScript;
266 	UCHAR	ucTopTag;
267 
268 	TRACE_MSG("vPrintFootnote");
269 
270 	szText = szGetFootnootText(uiFootnoteIndex);
271 
272 	if (szText == NULL) {
273 		szText = "";
274 	}
275 
276 	/* Remove the subscript/superscript (if any) */
277 	ucTopTag = ucReadStack();
278 	bSuScript = ucTopTag == TAG_SUBSCRIPT || ucTopTag == TAG_SUPERSCRIPT;
279 	if (bSuScript) {
280 		vAddEndTag(pDiag, ucTopTag);
281 	}
282 
283 	/* Start a footnote */
284 	vAddStartTag(pDiag, TAG_FOOTNOTE, NULL);
285 	vAddStartTag(pDiag, TAG_PARA, NULL);
286 
287 	/* Print a footnote */
288 	for (pcTmp = szText; *pcTmp != '\0'; pcTmp++) {
289 		if (*pcTmp == PAR_END) {
290 			if (*(pcTmp + 1) != PAR_END && *(pcTmp + 1) != '\0') {
291 				/* PAR_END is not empty and not last */
292 				vAddEndTag(pDiag, TAG_PARA);
293 				vAddStartTag(pDiag, TAG_PARA, NULL);
294 			}
295 		} else {
296 			vPrintChar(pDiag, *pcTmp);
297 		}
298 	}
299 
300 	/* End a footnote */
301 	vAddEndTag(pDiag, TAG_PARA);
302 	vAddEndTag(pDiag, TAG_FOOTNOTE);
303 
304 	/* Repair the subscript/superscript (if any) */
305 	if (bSuScript) {
306 		vAddStartTag(pDiag, ucTopTag, NULL);
307 	}
308 } /* end of vPrintFootnote */
309 
310 /*
311  * vPrintChar - print a character with XML encoding
312  */
313 static void
vPrintChar(diagram_type * pDiag,char cChar)314 vPrintChar(diagram_type *pDiag, char cChar)
315 {
316 	fail(pDiag == NULL);
317 	fail(pDiag->pOutFile == NULL);
318 
319 	switch (cChar) {
320 	case FOOTNOTE_OR_ENDNOTE:
321 		uiFootnoteNumber++;
322 		vPrintFootnote(pDiag, uiFootnoteNumber - 1);
323 		break;
324 	case '<':
325 		fprintf(pDiag->pOutFile, "%s", "&lt;");
326 		break;
327 	case '>':
328 		fprintf(pDiag->pOutFile, "%s", "&gt;");
329 		break;
330 	case '&':
331 		fprintf(pDiag->pOutFile, "%s", "&amp;");
332 		break;
333 	default:
334 		(void)putc(cChar, pDiag->pOutFile);
335 		break;
336 	}
337 } /* end of vPrintChar */
338 
339 /*
340  * vPrintSpecialChar - convert and print a character
341  */
342 static void
vPrintSpecialChar(diagram_type * pDiag,USHORT usChar)343 vPrintSpecialChar(diagram_type *pDiag, USHORT usChar)
344 {
345 	ULONG   ulChar;
346 	size_t  tLen, tIndex;
347 	char    szResult[4];
348 
349 	fail(pDiag == NULL);
350 	fail(pDiag->pOutFile == NULL);
351 	fail(iWordVersion < 0);
352 	fail(eEncoding == encoding_neutral);
353 
354 	ulChar = ulTranslateCharacters(usChar, 0, iWordVersion,
355 				conversion_xml, eEncoding, bOldMacFile);
356 	tLen = tUcs2Utf8(ulChar, szResult, sizeof(szResult));
357 	if (tLen == 1) {
358 		vPrintChar(pDiag, szResult[0]);
359 	} else {
360 		for (tIndex = 0; tIndex < tLen; tIndex++) {
361 			(void)putc(szResult[tIndex], pDiag->pOutFile);
362 		}
363 	}
364 } /* end of vPrintSpecialChar */
365 
366 /*
367  * vPrintSpecialString - convert and print a string
368  */
369 static void
vPrintSpecialString(diagram_type * pDiag,const char * szString)370 vPrintSpecialString(diagram_type *pDiag, const char *szString)
371 {
372 	int	iIndex;
373 	USHORT	usChar;
374 
375 	fail(pDiag == NULL);
376 	fail(pDiag->pOutFile == NULL);
377 	fail(szString == NULL);
378 
379 	for (iIndex = 0; szString[iIndex] != '\0'; iIndex++) {
380 		usChar = (USHORT)(UCHAR)szString[iIndex];
381 		vPrintSpecialChar(pDiag, usChar);
382 	}
383 } /* end of vPrintSpecialString */
384 
385 /*
386  * vAddStartTag - add the specified start tag to the file
387  */
388 static void
vAddStartTag(diagram_type * pDiag,UCHAR ucTag,const char * szAttribute)389 vAddStartTag(diagram_type *pDiag, UCHAR ucTag, const char *szAttribute)
390 {
391 	fail(pDiag == NULL);
392 	fail(pDiag->pOutFile == NULL);
393 	fail((size_t)ucTag >= elementsof(atDocBookTags));
394 
395 	if (atDocBookTags[(UINT)ucTag].bAddNewlineStart) {
396 		fprintf(pDiag->pOutFile, "\n");
397 		vPrintLevel(pDiag->pOutFile);
398 	}
399 
400 	if (szAttribute == NULL || szAttribute[0] == '\0') {
401 		fprintf(pDiag->pOutFile, "<%s>",
402 			atDocBookTags[(UINT)ucTag].szTagname);
403 	} else {
404 		fprintf(pDiag->pOutFile, "<%s %s>",
405 			atDocBookTags[(UINT)ucTag].szTagname, szAttribute);
406 	}
407 
408 	if (atDocBookTags[(UINT)ucTag].bAddNewlineEnd) {
409 		fprintf(pDiag->pOutFile, "\n");
410 		pDiag->lXleft = 0;
411 	}
412 
413 	vPushStack(ucTag);
414 
415 	/* Set global variables */
416 	switch (ucTag) {
417 	case TAG_CHAPTER:
418 		usHeaderLevelCurrent = 1;
419 		bEmptyHeaderLevel = TRUE;
420 		break;
421 	case TAG_SECT1:
422 		usHeaderLevelCurrent = 2;
423 		bEmptyHeaderLevel = TRUE;
424 		break;
425 	case TAG_SECT2:
426 		usHeaderLevelCurrent = 3;
427 		bEmptyHeaderLevel = TRUE;
428 		break;
429 	case TAG_SECT3:
430 		usHeaderLevelCurrent = 4;
431 		bEmptyHeaderLevel = TRUE;
432 		break;
433 	case TAG_SECT4:
434 		usHeaderLevelCurrent = 5;
435 		bEmptyHeaderLevel = TRUE;
436 		break;
437 	case TAG_SECT5:
438 		usHeaderLevelCurrent = 6;
439 		bEmptyHeaderLevel = TRUE;
440 		break;
441 	case TAG_TITLE:
442 		fail(uiParagraphLevel != 0);
443 		bTitleOpen = TRUE;
444 		break;
445 	case TAG_FOOTNOTE:
446 		bFootnoteOpen = TRUE;
447 		break;
448 	case TAG_PARA:
449 		fail(bTitleOpen && !bFootnoteOpen);
450 		uiParagraphLevel++;
451 		bEmptyHeaderLevel = FALSE;
452 		break;
453 	case TAG_EMPHASIS:
454 		bEmphasisOpen = TRUE;
455 		break;
456 	case TAG_ITEMIZEDLIST:
457 	case TAG_ORDEREDLIST:
458 		uiListLevel++;
459 		bEmptyListLevel = TRUE;
460 		bEmptyHeaderLevel = FALSE;
461 		break;
462 	case TAG_LISTITEM:
463 		bEmptyListLevel = FALSE;
464 		break;
465 	case TAG_SUPERSCRIPT:
466 		bSuperscriptOpen = TRUE;
467 		break;
468 	case TAG_SUBSCRIPT:
469 		bSubscriptOpen = TRUE;
470 		break;
471 	case TAG_INFORMALTABLE:
472 		bTableOpen = TRUE;
473 		bEmptyHeaderLevel = FALSE;
474 		break;
475 	default:
476 		break;
477 	}
478 } /* end of vAddStartTag */
479 
480 /*
481  * vAddEndTag - add the specified end tag to the file
482  */
483 static void
vAddEndTag(diagram_type * pDiag,UCHAR ucTag)484 vAddEndTag(diagram_type *pDiag, UCHAR ucTag)
485 {
486 	UCHAR	ucTopTag;
487 
488 	fail(pDiag == NULL);
489 	fail(pDiag->pOutFile == NULL);
490 	fail((size_t)ucTag >= elementsof(atDocBookTags));
491 
492 #if defined(DEBUG)
493 	ucTopTag = ucReadStack();
494 	if (ucTag != ucTopTag) {
495 		DBG_DEC(ucTag);
496 		DBG_MSG(atDocBookTags[(UINT)ucTag].szTagname);
497 		vStackTrace();
498 	}
499 #endif /* DEBUG */
500 
501 	ucTopTag = ucPopStack();
502 	fail((size_t)ucTopTag >= elementsof(atDocBookTags));
503 	if (ucTag != ucTopTag) {
504 		DBG_DEC(ucTag);
505 		DBG_DEC(ucTopTag);
506 		DBG_FIXME();
507 		werr(1, "Impossible tag sequence, unable to continue");
508 	}
509 
510 	if (atDocBookTags[(UINT)ucTag].bAddNewlineEnd) {
511 		fprintf(pDiag->pOutFile, "\n");
512 		vPrintLevel(pDiag->pOutFile);
513 	}
514 
515 	fprintf(pDiag->pOutFile, "</%s>", atDocBookTags[(UINT)ucTag].szTagname);
516 
517 	if (atDocBookTags[(UINT)ucTag].bAddNewlineStart) {
518 		fprintf(pDiag->pOutFile, "\n");
519 		pDiag->lXleft = 0;
520 	}
521 
522 	/* Set global variables */
523 	switch (ucTag) {
524 	case TAG_CHAPTER:
525 		usHeaderLevelCurrent = 0;
526 		break;
527 	case TAG_SECT1:
528 		usHeaderLevelCurrent = 1;
529 		break;
530 	case TAG_SECT2:
531 		usHeaderLevelCurrent = 2;
532 		break;
533 	case TAG_SECT3:
534 		usHeaderLevelCurrent = 3;
535 		break;
536 	case TAG_SECT4:
537 		usHeaderLevelCurrent = 4;
538 		break;
539 	case TAG_SECT5:
540 		usHeaderLevelCurrent = 5;
541 		break;
542 	case TAG_TITLE:
543 		bTitleOpen = FALSE;
544 		break;
545 	case TAG_FOOTNOTE:
546 		bFootnoteOpen = FALSE;
547 		break;
548 	case TAG_PARA:
549 		uiParagraphLevel--;
550 		break;
551 	case TAG_EMPHASIS:
552 		bEmphasisOpen = FALSE;
553 		break;
554 	case TAG_SUPERSCRIPT:
555 		bSuperscriptOpen = FALSE;
556 		break;
557 	case TAG_ITEMIZEDLIST:
558 	case TAG_ORDEREDLIST:
559 		uiListLevel--;
560 		break;
561 	case TAG_SUBSCRIPT:
562 		bSubscriptOpen = FALSE;
563 		break;
564 	case TAG_INFORMALTABLE:
565 		bTableOpen = FALSE;
566 		iTableColumnsCurrent = 0;
567 		break;
568 	default:
569 		break;
570 	}
571 } /* end of vAddEndTag */
572 
573 /*
574  * vAddEndTagOptional - add the specified end tag to the file if needed
575  */
576 static void
vAddEndTagOptional(diagram_type * pDiag,UCHAR ucTag)577 vAddEndTagOptional(diagram_type *pDiag, UCHAR ucTag)
578 {
579 	UCHAR	ucTopTag;
580 
581 	ucTopTag = ucReadStack();
582 	if (ucTag == ucTopTag) {
583 		vAddEndTag(pDiag, ucTag);
584 	}
585 } /* end of vAddEndTagOptional */
586 
587 /*
588  * vAddCombinedTag - add the specified start and end tag to the file
589  */
590 static void
vAddCombinedTag(diagram_type * pDiag,UCHAR ucTag,const char * szAttribute)591 vAddCombinedTag(diagram_type *pDiag, UCHAR ucTag, const char *szAttribute)
592 {
593 	fail(pDiag == NULL);
594 	fail(pDiag->pOutFile == NULL);
595 	fail((size_t)ucTag >= elementsof(atDocBookTags));
596 
597 	if (atDocBookTags[(UINT)ucTag].bAddNewlineStart) {
598 		fprintf(pDiag->pOutFile, "\n");
599 		vPrintLevel(pDiag->pOutFile);
600 	}
601 
602 	if (szAttribute == NULL || szAttribute[0] == '\0') {
603 		fprintf(pDiag->pOutFile, "<%s/>",
604 			atDocBookTags[(UINT)ucTag].szTagname);
605 	} else {
606 		fprintf(pDiag->pOutFile, "<%s %s/>",
607 			atDocBookTags[(UINT)ucTag].szTagname, szAttribute);
608 	}
609 
610 	if (atDocBookTags[(UINT)ucTag].bAddNewlineStart) {
611 		fprintf(pDiag->pOutFile, "\n");
612 		pDiag->lXleft = 0;
613 	}
614 } /* end of vAddCombinedTag */
615 
616 /*
617  * vAddEndTagsUntil2 - add end tags until one the specified tags is seen
618  */
619 static void
vAddEndTagsUntil2(diagram_type * pDiag,UCHAR ucTag1,UCHAR ucTag2)620 vAddEndTagsUntil2(diagram_type *pDiag, UCHAR ucTag1, UCHAR ucTag2)
621 {
622 	UCHAR	ucTopTag;
623 
624 	do {
625 		ucTopTag = ucReadStack();
626 		switch (ucTopTag) {
627                 case TAG_CHAPTER:
628                 case TAG_SECT1:
629                 case TAG_SECT2:
630                 case TAG_SECT3:
631                 case TAG_SECT4:
632                 case TAG_SECT5:
633 			if (bEmptyHeaderLevel) {
634 				/*
635 				 * An empty chapter is legal in Word,
636 				 * but not in DocBook.
637 				 */
638 				vAddCombinedTag(pDiag, TAG_PARA, NULL);
639 				bEmptyHeaderLevel = FALSE;
640 			}
641 			break;
642 		case TAG_ITEMIZEDLIST:
643 		case TAG_ORDEREDLIST:
644 			if (bEmptyListLevel) {
645 				/*
646 				 * A list without items is legal in Word,
647 				 * but not in DocBook. (Nor are empty items)
648 				 */
649 				vAddStartTag(pDiag, TAG_LISTITEM, NULL);
650 				vAddCombinedTag(pDiag, TAG_PARA, NULL);
651 				vAddEndTag(pDiag, TAG_LISTITEM);
652 				bEmptyListLevel = FALSE;
653 			}
654 			break;
655 		default:
656 			break;
657 		}
658 		vAddEndTag(pDiag, ucTopTag);
659 	} while (ucTopTag != ucTag1 && ucTopTag != ucTag2);
660 } /* end of vAddEndTagsUntil2 */
661 
662 /*
663  * vCreateBookIntro - create title and bookinfo
664  */
665 void
vCreateBookIntro(diagram_type * pDiag,int iVersion)666 vCreateBookIntro(diagram_type *pDiag, int iVersion)
667 {
668 	const char	*szTitle, *szSubject, *szAuthor;
669 	const char	*szLastSaveDtm, *szCompany;
670 	const char	*szLanguage;
671 	char		szTmp[13];
672 
673 	fail(pDiag == NULL);
674 	fail(pDiag->pOutFile == NULL);
675 	fail(iVersion < 0);
676 	fail(eEncoding == encoding_neutral);
677 
678 	iWordVersion = iVersion;
679 	bOldMacFile = bIsOldMacFile();
680 	szTitle = szGetTitle();
681 	szSubject = szGetSubject();
682 	szAuthor = szGetAuthor();
683 	szLastSaveDtm = szGetLastSaveDtm();
684 	szCompany = szGetCompany();
685 
686 	/* Start Book */
687 	szLanguage = szGetLanguage();
688 	if (szLanguage != NULL) {
689 		DBG_MSG(szLanguage);
690 		sprintf(szTmp, "lang='%.5s'", szLanguage);
691 		szLanguage = szTmp;
692 	}
693 	vAddStartTag(pDiag, TAG_BOOK, szLanguage);
694 
695 	/* Book title */
696 	if (szTitle != NULL && szTitle[0] != '\0') {
697 		vAddStartTag(pDiag, TAG_TITLE, NULL);
698 		vPrintSpecialString(pDiag, szTitle);
699 		vAddEndTag(pDiag, TAG_TITLE);
700 	}
701 	/* Bookinfo */
702 	if ((szTitle != NULL && szTitle[0] != '\0') ||
703 	    (szSubject != NULL && szSubject[0] != '\0') ||
704 	    (szAuthor != NULL && szAuthor[0] != '\0') ||
705 	    (szLastSaveDtm != NULL && szLastSaveDtm[0] != '\0') ||
706 	    (szCompany != NULL && szCompany[0] != '\0')) {
707 		vAddStartTag(pDiag, TAG_BOOKINFO, NULL);
708 		if (szTitle != NULL && szTitle[0] != '\0') {
709 			vAddStartTag(pDiag, TAG_TITLE, NULL);
710 			vPrintSpecialString(pDiag, szTitle);
711 			vAddEndTag(pDiag, TAG_TITLE);
712 		}
713 		if (szSubject != NULL && szSubject[0] != '\0') {
714 			vAddStartTag(pDiag, TAG_SUBTITLE, NULL);
715 			vPrintSpecialString(pDiag, szSubject);
716 			vAddEndTag(pDiag, TAG_SUBTITLE);
717 		}
718 		if (szAuthor != NULL && szAuthor[0] != '\0') {
719 			vAddStartTag(pDiag, TAG_AUTHOR, NULL);
720 			vAddStartTag(pDiag, TAG_SURNAME, NULL);
721 			vPrintSpecialString(pDiag, szAuthor);
722 			vAddEndTag(pDiag, TAG_SURNAME);
723 			vAddEndTag(pDiag, TAG_AUTHOR);
724 		}
725 		if (szLastSaveDtm != NULL && szLastSaveDtm[0] != '\0') {
726 			vAddStartTag(pDiag, TAG_DATE, NULL);
727 			vPrintSpecialString(pDiag, szLastSaveDtm);
728 			vAddEndTag(pDiag, TAG_DATE);
729 		}
730 		if (szCompany != NULL && szCompany[0] != '\0') {
731 			vAddStartTag(pDiag, TAG_CORPNAME, NULL);
732 			vPrintSpecialString(pDiag, szCompany);
733 			vAddEndTag(pDiag, TAG_CORPNAME);
734 		}
735 		vAddEndTag(pDiag, TAG_BOOKINFO);
736 	}
737 } /* end of vCreateBookIntro */
738 
739 /*
740  * vPrologueXML - perform the XML initialization
741  */
742 void
vPrologueXML(diagram_type * pDiag,const options_type * pOptions)743 vPrologueXML(diagram_type *pDiag, const options_type *pOptions)
744 {
745 
746 	fail(pDiag == NULL);
747 	fail(pDiag->pOutFile == NULL);
748 	fail(pOptions == NULL);
749 
750 #if defined(DEBUG)
751 	vCheckTagTable();
752 #endif /* DEBUG */
753 
754 	/* Set global variables to their start values */
755 	eEncoding = pOptions->eEncoding;
756 	bEmphasisOpen = FALSE;
757 	bSuperscriptOpen = FALSE;
758 	bSubscriptOpen = FALSE;
759 	bTitleOpen = FALSE;
760 	bTableOpen = FALSE;
761 	bFootnoteOpen = FALSE;
762 	uiParagraphLevel = 0;
763 	uiListLevel = 0;
764 	bEmptyListLevel = TRUE;
765 	usHeaderLevelCurrent = 0;
766 	bEmptyHeaderLevel = TRUE;
767 	iTableColumnsCurrent = 0;
768 	uiFootnoteNumber = 0;
769 
770 	pDiag->lXleft = 0;
771 	pDiag->lYtop = 0;
772 
773 	/* Create an empty stack */
774 	tStacksize = INITIAL_STACK_SIZE;
775 	aucStack = xcalloc(tStacksize, sizeof(UCHAR));
776 	tStackNextFree = 0;
777 } /* end of vPrologueXML */
778 
779 /*
780  * vEpilogueXML - clean up after everything is done
781  */
782 void
vEpilogueXML(diagram_type * pDiag)783 vEpilogueXML(diagram_type *pDiag)
784 {
785 	vStackTrace();
786 
787 	vAddEndTagsUntil1(pDiag, TAG_BOOK);
788 
789 	vStackTrace();
790 
791 	/* Destroy the stack */
792 	fail(tStackNextFree != 0);
793 	tStacksize = 0;
794 	aucStack = xfree(aucStack);
795 	tStackNextFree = 0;
796 } /* end of vEpilogueXML */
797 
798 /*
799  * vPrintXML - print a XML string
800  */
801 static void
vPrintXML(diagram_type * pDiag,const char * szString,size_t tStringLength,USHORT usFontstyle)802 vPrintXML(diagram_type *pDiag, const char *szString, size_t tStringLength,
803 		USHORT usFontstyle)
804 {
805 	const char	*szAttr;
806 	int	iCount;
807 	size_t	tNextFree;
808 	BOOL	bNotReady, bEmphasisNew, bSuperscriptNew, bSubscriptNew;
809 	UCHAR	ucTopTag, aucStorage[3];
810 
811 	fail(szString == NULL);
812 
813 	if (szString == NULL || szString[0] == '\0' || tStringLength == 0) {
814 		return;
815 	}
816 
817 	if (tStringLength == 1 && szString[0] == FOOTNOTE_OR_ENDNOTE) {
818 		/* Don't do anything special for just a single footnote */
819 		bEmphasisNew = FALSE;
820 		bSuperscriptNew = FALSE;
821 		bSubscriptNew = FALSE;
822 	} else {
823 		/* Situation normal */
824 		bEmphasisNew = bIsBold(usFontstyle) ||
825 				bIsItalic(usFontstyle) ||
826 				bIsUnderline(usFontstyle) ||
827 				bIsStrike(usFontstyle);
828 		bSuperscriptNew = bIsSuperscript(usFontstyle);
829 		bSubscriptNew = bIsSubscript(usFontstyle);
830 	}
831 
832 	/* End what has to be ended (or more to keep the stack happy) */
833 	tNextFree = 0;
834 	bNotReady = TRUE;
835 	do {
836 		ucTopTag = ucReadStack();
837 		switch (ucTopTag) {
838 		case TAG_EMPHASIS:
839 			fail(!bEmphasisOpen);
840 			if (bEmphasisNew) {
841 				aucStorage[tNextFree++] = ucTopTag;
842 			}
843 			vAddEndTag(pDiag, ucTopTag);
844 			break;
845 		case TAG_SUPERSCRIPT:
846 			fail(!bSuperscriptOpen);
847 			if (bSuperscriptNew) {
848 				aucStorage[tNextFree++] = ucTopTag;
849 			}
850 			vAddEndTag(pDiag, ucTopTag);
851 			break;
852 		case TAG_SUBSCRIPT:
853 			fail(!bSubscriptOpen);
854 			if (bSubscriptNew) {
855 				aucStorage[tNextFree++] = ucTopTag;
856 			}
857 			vAddEndTag(pDiag, ucTopTag);
858 			break;
859 		default:
860 			bNotReady = FALSE;
861 			break;
862 		}
863 		fail(tNextFree > elementsof(aucStorage));
864 		fail(bNotReady && tNextFree == elementsof(aucStorage));
865 	} while (bNotReady);
866 
867 	/* Just te make sure */
868 	vStartOfParagraphXML(pDiag, 1);
869 
870 	/* Restart to keep the stack happy */
871 	for (iCount = (int)tNextFree - 1; iCount > 0; iCount--) {
872 		vAddStartTag(pDiag, aucStorage[iCount], NULL);
873 	}
874 
875 	/* Start what has to be started */
876 	if (bEmphasisNew && !bEmphasisOpen) {
877 		if (bIsBold(usFontstyle)) {
878 			szAttr = "role='bold'";
879 		} else if (bIsItalic(usFontstyle)) {
880 			szAttr = NULL;
881 		} else if (bIsUnderline(usFontstyle)) {
882 			szAttr = "role='underline'";
883 		} else if (bIsStrike(usFontstyle)) {
884 			szAttr = "role='strikethrough'";
885 		} else {
886 			szAttr = NULL;
887 		}
888 		vAddStartTag(pDiag, TAG_EMPHASIS, szAttr);
889 	}
890 	if (bSuperscriptNew && !bSuperscriptOpen) {
891 		vAddStartTag(pDiag, TAG_SUPERSCRIPT, NULL);
892 	}
893 	if (bSubscriptNew && !bSubscriptOpen) {
894 		vAddStartTag(pDiag, TAG_SUBSCRIPT, NULL);
895 	}
896 
897 	/* The print the string */
898 	for (iCount = 0; iCount < (int)tStringLength; iCount++) {
899 		vPrintChar(pDiag, szString[iCount]);
900 	}
901 } /* end of vPrintXML */
902 
903 /*
904  * vMove2NextLineXML - move to the next line
905  */
906 void
vMove2NextLineXML(diagram_type * pDiag)907 vMove2NextLineXML(diagram_type *pDiag)
908 {
909 	fail(pDiag == NULL);
910 
911 	/*
912 	if (uiParagraphLevel != 0) {
913 		We need something like HTML's <BR> tag
914 	}
915 	*/
916 } /* end of vMove2NextLineXML */
917 
918 /*
919  * vSubstringXML - put a sub string into a diagram
920  */
921 void
vSubstringXML(diagram_type * pDiag,const char * szString,size_t tStringLength,long lStringWidth,USHORT usFontstyle)922 vSubstringXML(diagram_type *pDiag,
923 	const char *szString, size_t tStringLength, long lStringWidth,
924 	USHORT usFontstyle)
925 {
926 	fail(pDiag == NULL || szString == NULL);
927 	fail(pDiag->pOutFile == NULL);
928 	fail(pDiag->lXleft < 0);
929 	fail(tStringLength != strlen(szString));
930 
931 	if (szString[0] == '\0' || tStringLength == 0) {
932 		return;
933 	}
934 
935 	vPrintXML(pDiag, szString, tStringLength, usFontstyle);
936 	pDiag->lXleft += lStringWidth;
937 } /* end of vSubstringXML */
938 
939 /*
940  * Create an start of a paragraph
941  * Only works on paragraph level one, because Word doesn't allow paragraphs
942  * in paragraphs. Other paragraph levels result from DocBooks special needs.
943  */
944 void
vStartOfParagraphXML(diagram_type * pDiag,UINT uiMaxLevel)945 vStartOfParagraphXML(diagram_type *pDiag, UINT uiMaxLevel)
946 {
947 	fail(pDiag == NULL);
948 
949 	if (uiParagraphLevel >= uiMaxLevel || bTitleOpen) {
950 		/* In Word a title is just a paragraph */
951 		return;
952 	}
953 	if (uiListLevel != 0 && bEmptyListLevel) {
954 		/* No paragraphs in a list before the first listitem */
955 		return;
956 	}
957 	if (usHeaderLevelCurrent == 0) {
958 		/* No paragraphs without an open header */
959 		vAddStartTag(pDiag, TAG_CHAPTER, NULL);
960 		/* Dummy title */
961 		vAddCombinedTag(pDiag, TAG_TITLE, NULL);
962 	}
963 	vAddStartTag(pDiag, TAG_PARA, NULL);
964 } /* end of vStartOfParagraphXML */
965 
966 /*
967  * Create an end of a paragraph
968  * Only for paragraph level one and for titles
969  */
970 void
vEndOfParagraphXML(diagram_type * pDiag,UINT uiMaxLevel)971 vEndOfParagraphXML(diagram_type *pDiag, UINT uiMaxLevel)
972 {
973 	UCHAR	ucTopTag;
974 
975 	fail(pDiag == NULL);
976 
977 	if (uiParagraphLevel > uiMaxLevel) {
978 		DBG_DEC(uiParagraphLevel);
979 		return;
980 	}
981 
982 	for(;;) {
983 		ucTopTag = ucReadStack();
984 		switch (ucTopTag) {
985 		case TAG_EMPHASIS:
986 			fail(!bEmphasisOpen);
987 			vAddEndTag(pDiag, TAG_EMPHASIS);
988 			break;
989 		case TAG_SUPERSCRIPT:
990 			fail(!bSuperscriptOpen);
991 			vAddEndTag(pDiag, TAG_SUPERSCRIPT);
992 			break;
993 		case TAG_SUBSCRIPT:
994 			fail(!bSubscriptOpen);
995 			vAddEndTag(pDiag, TAG_SUBSCRIPT);
996 			break;
997 		case TAG_TITLE:
998 			fail(!bTitleOpen);
999 			vAddEndTag(pDiag, TAG_TITLE);
1000 			return;
1001 		case TAG_PARA:
1002 			fail(uiParagraphLevel == 0);
1003 			vAddEndTag(pDiag, TAG_PARA);
1004 			return;
1005 		case TAG_TBODY:
1006 		case TAG_TGROUP:
1007 		case TAG_INFORMALTABLE:
1008 			fail(!bTableOpen);
1009 			vAddEndTag(pDiag, ucTopTag);
1010 			break;
1011 		case TAG_NOTAG:
1012 			DBG_FIXME();
1013 			werr(1, "Impossible tag sequence, unable to continue");
1014 			break;
1015 		default:
1016 			DBG_DEC(ucTopTag);
1017 			DBG_MSG_C((size_t)ucTopTag < elementsof(atDocBookTags),
1018 				atDocBookTags[(UINT)ucTopTag].szTagname);
1019 			return;
1020 		}
1021 	}
1022 } /* end of vEndOfParagraphXML */
1023 
1024 /*
1025  * Create an end of a page
1026  */
1027 void
vEndOfPageXML(diagram_type * pDiag)1028 vEndOfPageXML(diagram_type *pDiag)
1029 {
1030 	if (bTableOpen || usHeaderLevelCurrent == 0) {
1031 		/* No beginpage in a table or outside a chapter */
1032 		return;
1033 	}
1034 	if (bTitleOpen) {
1035 		/* A beginpage is not allowed when in a title */
1036 		/* So start a new paragraph */
1037 		vEndOfParagraphXML(pDiag, UINT_MAX);
1038 		vStartOfParagraphXML(pDiag, UINT_MAX);
1039 		return;
1040 	}
1041 	vAddCombinedTag(pDiag, TAG_BEGINPAGE, NULL);
1042 } /* end of vEndOfPageXML */
1043 
1044 /*
1045  * vCloseHeaderLevels - close the specified header levels
1046  */
1047 static void
vCloseHeaderLevels(diagram_type * pDiag,USHORT usIstd)1048 vCloseHeaderLevels(diagram_type *pDiag, USHORT usIstd)
1049 {
1050 	BOOL	bNotReady;
1051 	UCHAR	ucTopTag;
1052 
1053 	DBG_MSG("vCloseHeaderLevels");
1054 	DBG_DEC(usIstd);
1055 	DBG_DEC(usHeaderLevelCurrent);
1056 
1057 	vStackTrace();
1058 
1059 	bNotReady = TRUE;
1060 	do {
1061 		ucTopTag = ucReadStack();
1062 		switch (ucTopTag) {
1063 		case TAG_TITLE:
1064 		case TAG_PARA:
1065 			vAddEndTag(pDiag, ucTopTag);
1066 			break;
1067 		default:
1068 			bNotReady = FALSE;
1069 			break;
1070 		}
1071 	} while (bNotReady);
1072 
1073 	vStackTrace();
1074 
1075 	while (usHeaderLevelCurrent >= usIstd) {
1076 		if (bEmptyHeaderLevel) {
1077 			vAddCombinedTag(pDiag, TAG_PARA, NULL);
1078 			bEmptyHeaderLevel = FALSE;
1079 		}
1080 		switch (usHeaderLevelCurrent) {
1081 		case 1: vAddEndTag(pDiag, TAG_CHAPTER); break;
1082 		case 2: vAddEndTag(pDiag, TAG_SECT1); break;
1083 		case 3: vAddEndTag(pDiag, TAG_SECT2); break;
1084 		case 4: vAddEndTag(pDiag, TAG_SECT3); break;
1085 		case 5: vAddEndTag(pDiag, TAG_SECT4); break;
1086 		case 6: vAddEndTag(pDiag, TAG_SECT5); break;
1087 		default:
1088 			DBG_DEC(usHeaderLevelCurrent);
1089 			DBG_FIXME();
1090 			return;
1091 		}
1092 	}
1093 
1094 	DBG_DEC(usHeaderLevelCurrent);
1095 
1096 	vStackTrace();
1097 } /* end of vCloseHeaderLevels */
1098 
1099 /*
1100  * vSetHeadersXML - set the headers
1101  */
1102 void
vSetHeadersXML(diagram_type * pDiag,USHORT usIstd)1103 vSetHeadersXML(diagram_type *pDiag, USHORT usIstd)
1104 {
1105 	fail(pDiag == NULL);
1106 
1107 	if (usIstd == 0 || usIstd > 6) {
1108 		DBG_DEC_C(usIstd != 0 && usIstd <= 9, usIstd);
1109 		return;
1110 	}
1111 	DBG_DEC(usIstd);
1112 
1113 	if (bTableOpen || uiListLevel != 0) {
1114 		/* No headers when you're in a table or in a list */
1115 		return;
1116 	}
1117 
1118 	/* Close levels */
1119 	vCloseHeaderLevels(pDiag, usIstd);
1120 
1121 	DBG_DEC(usHeaderLevelCurrent);
1122 
1123 	/* Open levels */
1124 	while (usHeaderLevelCurrent < usIstd) {
1125 		switch (usHeaderLevelCurrent) {
1126 		case 0: vAddStartTag(pDiag, TAG_CHAPTER, NULL); break;
1127 		case 1: vAddStartTag(pDiag, TAG_SECT1, NULL); break;
1128 		case 2: vAddStartTag(pDiag, TAG_SECT2, NULL); break;
1129 		case 3: vAddStartTag(pDiag, TAG_SECT3, NULL); break;
1130 		case 4: vAddStartTag(pDiag, TAG_SECT4, NULL); break;
1131 		case 5: vAddStartTag(pDiag, TAG_SECT5, NULL); break;
1132 		default:
1133 			DBG_DEC(usHeaderLevelCurrent);
1134 			DBG_FIXME();
1135 			return;
1136 		}
1137 		fail(usIstd == 0);
1138 		/* The next paragraph should be a title */
1139 		if (usHeaderLevelCurrent < usIstd) {
1140 			/* This chapter level is not in the Word document */
1141 			vAddCombinedTag(pDiag, TAG_TITLE, NULL);
1142 		} else {
1143 			vAddStartTag(pDiag, TAG_TITLE, NULL);
1144 		}
1145 	}
1146 } /* end of vSetHeadersXML */
1147 
1148 /*
1149  * Create a start of a list
1150  */
1151 void
vStartOfListXML(diagram_type * pDiag,UCHAR ucNFC,BOOL bIsEndOfTable)1152 vStartOfListXML(diagram_type *pDiag, UCHAR ucNFC, BOOL bIsEndOfTable)
1153 {
1154 	const char	*szAttr;
1155 	UCHAR		ucTag;
1156 
1157 	fail(pDiag == NULL);
1158 
1159 	if (bIsEndOfTable) {
1160 		/* FIXME: until a list in a table is allowed */
1161 		vEndOfTableXML(pDiag);
1162 	}
1163 
1164 	if (bTableOpen) {
1165 		/* FIXME: a list in a table should be allowed */
1166 		return;
1167 	}
1168 
1169 	if (usHeaderLevelCurrent == 0) {
1170 		/* No list without an open header */
1171 		vAddStartTag(pDiag, TAG_CHAPTER, NULL);
1172 		/* Dummy title */
1173 		vAddCombinedTag(pDiag, TAG_TITLE, NULL);
1174 	}
1175 
1176 	switch (ucNFC) {
1177 	case LIST_ARABIC_NUM:
1178 	case LIST_ORDINAL_NUM:
1179 	case LIST_NUMBER_TXT:
1180 	case LIST_ORDINAL_TXT:
1181 	case LIST_OUTLINE_NUM:
1182 		ucTag = TAG_ORDEREDLIST;
1183 		szAttr = "numeration='arabic'";
1184 		break;
1185 	case LIST_UPPER_ROMAN:
1186 		ucTag = TAG_ORDEREDLIST;
1187 		szAttr = "numeration='upperroman'";
1188 		break;
1189 	case LIST_LOWER_ROMAN:
1190 		ucTag = TAG_ORDEREDLIST;
1191 		szAttr = "numeration='lowerroman'";
1192 		break;
1193 	case LIST_UPPER_ALPHA:
1194 		ucTag = TAG_ORDEREDLIST;
1195 		szAttr = "numeration='upperalpha'";
1196 		break;
1197 	case LIST_LOWER_ALPHA:
1198 		ucTag = TAG_ORDEREDLIST;
1199 		szAttr = "numeration='loweralpha'";
1200 		break;
1201 	case LIST_SPECIAL:
1202 	case LIST_SPECIAL2:
1203 	case LIST_BULLETS:
1204 		ucTag = TAG_ITEMIZEDLIST;
1205 		szAttr = "mark='bullet'";
1206 		break;
1207 	default:
1208 		ucTag = TAG_ORDEREDLIST;
1209 		szAttr = "numeration='arabic'";
1210 		DBG_HEX(ucNFC);
1211 		DBG_FIXME();
1212 		break;
1213 	}
1214 	vAddStartTag(pDiag, ucTag, szAttr);
1215 } /* end of vStartOfListXML */
1216 
1217 /*
1218  * Create an end of a list
1219  */
1220 void
vEndOfListXML(diagram_type * pDiag)1221 vEndOfListXML(diagram_type *pDiag)
1222 {
1223 	fail(pDiag == NULL);
1224 
1225 	if (bTableOpen) {
1226 		/* FIXME: a list in a table should be allowed */
1227 		return;
1228 	}
1229 
1230 	if (uiListLevel != 0) {
1231 		vStackTrace();
1232 		vAddEndTagsUntil2(pDiag, TAG_ITEMIZEDLIST, TAG_ORDEREDLIST);
1233 		vStackTrace();
1234 	}
1235 } /* end of vEndOfListXML */
1236 
1237 /*
1238  * Create a start of a list item
1239  */
1240 void
vStartOfListItemXML(diagram_type * pDiag,BOOL bNoMarks)1241 vStartOfListItemXML(diagram_type *pDiag, BOOL bNoMarks)
1242 {
1243 	const char	*szAttr;
1244 	UCHAR	ucTopTag;
1245 
1246 	fail(pDiag == NULL);
1247 
1248 	if (bTableOpen) {
1249 		/* FIXME: a list in a table should be allowed */
1250 		return;
1251 	}
1252 
1253 	ucTopTag = ucReadStack();
1254 	if (ucTopTag != TAG_ITEMIZEDLIST && ucTopTag != TAG_ORDEREDLIST) {
1255 		/* Must end a previous list item first */
1256 		vAddEndTagsUntil1(pDiag, TAG_LISTITEM);
1257 	}
1258 
1259 	DBG_DEC_C(ucReadStack() != TAG_ITEMIZEDLIST &&
1260 		ucReadStack() != TAG_ORDEREDLIST, ucReadStack());
1261 
1262 	/* Start a new list item */
1263 	szAttr = bNoMarks ? "override='none'" : NULL;
1264 	vAddStartTag(pDiag, TAG_LISTITEM, szAttr);
1265 	/* Start a new paragraph (independant of level) */
1266 	vAddStartTag(pDiag, TAG_PARA, NULL);
1267 } /* end of vStartOfListItemXML */
1268 
1269 /*
1270  * Create a start of a table
1271  */
1272 static void
vStartOfTable(diagram_type * pDiag,UCHAR ucBorderInfo)1273 vStartOfTable(diagram_type *pDiag, UCHAR ucBorderInfo)
1274 {
1275 	const char	*szFrame;
1276 	BOOL	bNotReady;
1277 	UCHAR	ucTopTag;
1278 	char	cColSep, cRowSep;
1279 	char	szAttr[40];
1280 
1281 	fail(pDiag == NULL);
1282 
1283 	/* Close elements that cannot contain a table */
1284 	bNotReady = TRUE;
1285 	do {
1286 		ucTopTag = ucReadStack();
1287 		switch (ucTopTag) {
1288 		case TAG_TITLE:
1289 			fail(!bTitleOpen);
1290 			vAddEndTag(pDiag, TAG_TITLE);
1291 			break;
1292 		case TAG_EMPHASIS:
1293 			fail(!bEmphasisOpen);
1294 			vAddEndTag(pDiag, TAG_EMPHASIS);
1295 			break;
1296 		case TAG_SUPERSCRIPT:
1297 			fail(!bSuperscriptOpen);
1298 			vAddEndTag(pDiag, TAG_SUPERSCRIPT);
1299 			break;
1300 		case TAG_SUBSCRIPT:
1301 			fail(!bSubscriptOpen);
1302 			vAddEndTag(pDiag, TAG_SUBSCRIPT);
1303 			break;
1304 		default:
1305 			bNotReady = FALSE;
1306 			break;
1307 		}
1308 	} while (bNotReady);
1309 
1310 	/* Create table attributes */
1311 	switch (ucBorderInfo) {
1312 	case TABLE_BORDER_TOP:
1313 		szFrame = "top";
1314 		break;
1315 	case TABLE_BORDER_LEFT|TABLE_BORDER_RIGHT:
1316 		szFrame = "sides";
1317 		break;
1318 	case TABLE_BORDER_TOP|TABLE_BORDER_BOTTOM:
1319 		szFrame = "topbot";
1320 		break;
1321 	case TABLE_BORDER_BOTTOM:
1322 		szFrame = "bottom";
1323 		break;
1324 	case TABLE_BORDER_TOP|TABLE_BORDER_LEFT|
1325 	     TABLE_BORDER_BOTTOM|TABLE_BORDER_RIGHT:
1326 		szFrame = "all";
1327 		break;
1328 	default:
1329 		szFrame = "none";
1330 		break;
1331 	}
1332 	cColSep = bIsTableBorderLeft(ucBorderInfo) ||
1333 		  bIsTableBorderRight(ucBorderInfo) ? '1' : '0';
1334 	cRowSep = bIsTableBorderTop(ucBorderInfo) ||
1335 		  bIsTableBorderBottom(ucBorderInfo) ? '1' : '0';
1336 
1337 	sprintf(szAttr, "frame='%.6s' colsep='%c' rowsep='%c'",
1338 			szFrame, cColSep, cRowSep);
1339 
1340 	if (usHeaderLevelCurrent == 0) {
1341 		/* No table without an open header */
1342 		vAddStartTag(pDiag, TAG_CHAPTER, NULL);
1343 		/* Dummy title */
1344 		vAddCombinedTag(pDiag, TAG_TITLE, NULL);
1345 	}
1346 	vAddStartTag(pDiag, TAG_INFORMALTABLE, szAttr);
1347 } /* end of vStartOfTable */
1348 
1349 /*
1350  * Create a start of a table group
1351  */
1352 static void
vStartOfTableGroup(diagram_type * pDiag,int iNbrOfColumns,const short * asColumnWidth)1353 vStartOfTableGroup(diagram_type *pDiag,
1354 	int iNbrOfColumns, const short *asColumnWidth)
1355 {
1356 	double	dWidth;
1357 	int	iIndex;
1358 	char	szCols[6 + 3 * sizeof(int) + 1 + 1];
1359 	char	szColWidth[10 + 3 * sizeof(short) + 3 + 3 + 1];
1360 
1361 	fail(iNbrOfColumns < 1);
1362 	fail(asColumnWidth == NULL);
1363 
1364 	sprintf(szCols, "cols='%d'", iNbrOfColumns);
1365 	vAddStartTag(pDiag, TAG_TGROUP, szCols);
1366 
1367 	for (iIndex= 0; iIndex < iNbrOfColumns; iIndex++) {
1368 		fail(asColumnWidth[iIndex] < 0);
1369 		dWidth = dTwips2Points(asColumnWidth[iIndex]);
1370 		if (dWidth <= 1.0) {
1371 			strcpy(szColWidth, "colwidth='1.00pt'");
1372 		} else {
1373 			sprintf(szColWidth, "colwidth='%.2fpt'", dWidth);
1374 		}
1375 		vAddCombinedTag(pDiag, TAG_COLSPEC, szColWidth);
1376 	}
1377 } /* end of vStartOfTableGroup */
1378 
1379 /*
1380  * Create an end of a table
1381  */
1382 void
vEndOfTableXML(diagram_type * pDiag)1383 vEndOfTableXML(diagram_type *pDiag)
1384 {
1385 	fail(pDiag == NULL);
1386 
1387 	if (bTableOpen) {
1388 		vAddEndTag(pDiag, TAG_TBODY);
1389 		vAddEndTag(pDiag, TAG_TGROUP);
1390 		vAddEndTag(pDiag, TAG_INFORMALTABLE);
1391 	}
1392 } /* end of vEndOfTableXML */
1393 
1394 /*
1395  * Add a table row
1396  */
1397 void
vAddTableRowXML(diagram_type * pDiag,char ** aszColTxt,int iNbrOfColumns,const short * asColumnWidth,UCHAR ucBorderInfo)1398 vAddTableRowXML(diagram_type *pDiag, char **aszColTxt,
1399 	int iNbrOfColumns, const short *asColumnWidth, UCHAR ucBorderInfo)
1400 {
1401 	size_t	tCount, tStringLength;
1402 	int	iIndex;
1403 
1404 	fail(pDiag == NULL);
1405 	fail(pDiag->pOutFile == NULL);
1406 	fail(aszColTxt == NULL);
1407 	fail(iNbrOfColumns < 1);
1408 	fail(asColumnWidth == NULL);
1409 
1410 	if (iNbrOfColumns != iTableColumnsCurrent) {
1411 		/* A new number of columns */
1412 		/* End the old table body and table group (if they exist) */
1413 		vAddEndTagOptional(pDiag, TAG_TBODY);
1414 		vAddEndTagOptional(pDiag, TAG_TGROUP);
1415 		if (!bTableOpen) {
1416 			/* No table yet. Start a new table */
1417 			vStartOfTable(pDiag, ucBorderInfo);
1418 		}
1419 		/* Start a new table group and a new table body */
1420 		vStartOfTableGroup(pDiag, iNbrOfColumns, asColumnWidth);
1421 		vAddStartTag(pDiag, TAG_TBODY, NULL);
1422 		iTableColumnsCurrent = iNbrOfColumns;
1423 	}
1424 
1425 	/* Add the table row */
1426 	vAddStartTag(pDiag, TAG_ROW, NULL);
1427 	for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
1428 		/* Add a table cell */
1429 		fail(aszColTxt[iIndex] == NULL);
1430 		vAddStartTag(pDiag, TAG_ENTRY, NULL);
1431 		tStringLength = strlen(aszColTxt[iIndex]);
1432 		for (tCount = 0; tCount < tStringLength; tCount++) {
1433 			vPrintChar(pDiag, aszColTxt[iIndex][tCount]);
1434 		}
1435 		vAddEndTag(pDiag, TAG_ENTRY);
1436 	}
1437 	vAddEndTag(pDiag, TAG_ROW);
1438 } /* end of vAddTableRowXML */
1439