1 #ifndef production
2 static char rcsId[]="$Header$";
3 #endif
4 /*****
5 * format.c : XmHTML formatting routines: translates parsed HTML to 	info
6 *			required for displaying a HTML page.
7 *
8 * This file Version	$Revision$
9 *
10 * Creation date:		Tue Nov 26 17:03:09 GMT+0100 1996
11 * Last modification: 	$Date$
12 * By:					$Author$
13 * Current State:		$State$
14 *
15 * Author:				newt
16 * (C)Copyright 1995-1996 Ripley Software Development
17 * All Rights Reserved
18 *
19 * This file is part of the XmHTML Widget Library.
20 *
21 * This library is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU Library General Public
23 * License as published by the Free Software Foundation; either
24 * version 2 of the License, or (at your option) any later version.
25 *
26 * This library is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
29 * Library General Public License for more details.
30 *
31 * You should have received a copy of the GNU Library General Public
32 * License along with this library; if not, write to the Free
33 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 *
35 *****/
36 /*****
37 * ChangeLog
38 * $Log$
39 * Revision 1.1  2011/06/30 16:10:38  rwcox
40 * Cadd
41 *
42 * Revision 1.19  1998/04/27 06:59:23  newt
43 * tka stuff
44 *
45 * Revision 1.18  1998/04/04 06:28:07  newt
46 * XmHTML Beta 1.1.3
47 *
48 * Revision 1.17  1997/10/23 00:24:56  newt
49 * XmHTML Beta 1.1.0 release
50 *
51 * Revision 1.16  1997/08/31 17:34:24  newt
52 * renamed _rec structures to Rec.
53 *
54 * Revision 1.15  1997/08/30 00:55:24  newt
55 * Completed <form></form> support.
56 * Bugfix in _XmHTMLInitializeFontSizeLists, the default font is now properly
57 * changed.
58 * Made the font loading routines again robuster.
59 * ParseBodyTags now always attempts to load a body image.
60 * Made XmHTMLGetURLType a bit stricter.
61 *
62 * Revision 1.14  1997/08/01 13:00:21  newt
63 * Bugfixes in font switching (<b>...<font><i>...</i></font>...</b>) now
64 * properly handled. Enhanced form support.
65 *
66 * Revision 1.13  1997/05/28 01:46:35  newt
67 * Added support for the XmNbodyImage resource: it's now used but only if no
68 * bgcolor resource has been set.
69 *
70 * Revision 1.12  1997/04/29 14:26:18  newt
71 * HTML forms changes
72 *
73 * Revision 1.11  1997/04/03 05:34:25  newt
74 * _XmHTMLLoadBodyImage added.
75 * Placed a large number of warnings between a #ifdef PEDANTIC/#endif
76 *
77 * Revision 1.10  1997/03/28 07:12:43  newt
78 * Fixed buffer overrun in TexToPre.
79 * Fixed font resolution: x and y resolution are now always equal.
80 * XmHTML now ignores the ending body tag.
81 *
82 * Revision 1.9  1997/03/20 08:10:04  newt
83 * Split font cache in a true cache and a font stack.
84 * Added stack checks when document has been formatted.
85 *
86 * Revision 1.8  1997/03/11 19:52:17  newt
87 * added ImageToWord
88 *
89 * Revision 1.7  1997/03/04 00:59:26  newt
90 * ?
91 *
92 * Revision 1.6  1997/03/02 23:17:46  newt
93 * Way too many changes. Most important: font loading/switching scheme; anchor
94 * treatment; image/imagemap treatment
95 *
96 * Revision 1.5  1997/02/11 02:08:44  newt
97 * Way to many. Anchor treatment has been changed completely.
98 * Bugfixes in anchor parsing. Potential buffer overruns eliminated.
99 *
100 * Revision 1.4  1997/02/04 02:56:49  newt
101 * Bugfix in LoadQueryFont.
102 * Added code to deal with the basefont element.
103 * Changed the font element handling.
104 *
105 * Revision 1.3  1997/01/09 06:55:39  newt
106 * expanded copyright marker
107 *
108 * Revision 1.2  1997/01/09 06:44:42  newt
109 * lots of changes: linebreaking and changes related to changed XmHTMLWord
110 *
111 * Revision 1.1  1996/12/19 02:17:10  newt
112 * Initial Revision
113 *
114 *****/
115 #ifdef HAVE_CONFIG_H
116 #include "config.h"
117 #endif
118 
119 #include <stdlib.h>
120 #include <stdio.h>
121 #include <string.h>
122 #include <ctype.h>	/* isspace, tolower */
123 
124 /* Local includes */
125 #include "toolkit.h"
126 #include XmHTMLPrivateHeader
127 #include "stack.h"
128 
129 /*** External Function Prototype Declarations ***/
130 
131 /*** Public Variable Declarations ***/
132 
133 /*** Private Datatype Declarations ****/
134 
135 typedef struct{
136 	String name;
137 	Marker type;	/* Marker is an enumeration type defined in XmHTMLP.h */
138 }listMarkers;
139 
140 /* for nesting of ordered and unordered lists */
141 typedef struct{
142 	Boolean isindex;	/* propagate index numbers? */
143 	int level;			/* item number */
144 	htmlEnum type;		/* ol or ul, used for custom markers */
145 	Marker marker;		/* marker to use */
146 }listStack;
147 
148 /*** Private Function Prototype Declarations ****/
149 
150 /****
151 * Formatting routines
152 *****/
153 
154 /* Release the formatted element table */
155 static void FreeObjectTable(XmHTMLObjectTable *list);
156 
157 /* Free the given list of tables */
158 static void freeTables(XmHTMLTable *table);
159 
160 /* Initialize the formatted element table */
161 static void InitObjectTable(XmHTMLObjectTable *list, XmHTMLAnchor *anchors);
162 
163 /* copy given text into an internal buffer */
164 static String CopyText(XmHTMLWidget html, String text, Boolean formatted,
165 	Byte *text_data, Boolean expand_escapes, Boolean *i18n);
166 
167 /* collapse all consecutive whitespace into a single space */
168 static void CollapseWhiteSpace(String text);
169 
170 /* Split raw text into an array of words */
171 static XmHTMLWord* TextToWords(String text, int *num_words, Dimension *height,
172 	XmHTMLfont *font, Byte line_data, Byte text_data,
173 	XmHTMLObjectTableElement owner, ToolkitAbstraction *tka);
174 
175 /* Split an image into an array of words ;-) */
176 static XmHTMLWord *ImageToWord(XmHTMLWidget html, String attributes,
177 	int *num_words, Dimension *height, XmHTMLObjectTableElement owner,
178 	Boolean formatted, ToolkitAbstraction *tka, Boolean is_anchor,
179 	Byte text_data);
180 
181 static XmHTMLWord *allocFormWord(XmHTMLWidget html, XmHTMLForm *form,
182 	Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
183 	Boolean formatted);
184 
185 static XmHTMLWord *InputToWord(XmHTMLWidget html, String attributes,
186 	int *num_words, Dimension *width, Dimension *height,
187 	XmHTMLObjectTableElement owner, Boolean formatted);
188 
189 static XmHTMLWord *SelectToWord(XmHTMLWidget html, XmHTMLObject *start,
190 	int *num_words, Dimension *width, Dimension *height,
191 	XmHTMLObjectTableElement owner, Boolean formatted);
192 
193 static XmHTMLWord *TextAreaToWord(XmHTMLWidget html, XmHTMLObject *start,
194 	int *num_words, Dimension *width, Dimension *height,
195 	XmHTMLObjectTableElement owner, Boolean formatted);
196 
197 static XmHTMLWord *BreakToWord(Dimension *height, XmHTMLfont *font,
198 	int linefeed, XmHTMLObjectTableElement owner);
199 
200 static XmHTMLWord *MakeDummyWord(Dimension *height, XmHTMLfont *font,
201 	Byte line_data, XmHTMLObjectTableElement owner);
202 
203 /* Split raw text into a chunk of preformatted lines */
204 static XmHTMLWord *TextToPre(String text, int *num_words, XmHTMLfont *font,
205 	Byte line_data, XmHTMLObjectTableElement owner, ToolkitAbstraction *tka,
206 	int tabwidth);
207 
208 /* Insert a horizontal tab */
209 static XmHTMLWord* SetTab(int size, Dimension *height, XmHTMLfont *font,
210 	XmHTMLObjectTableElement owner, ToolkitAbstraction *tka);
211 
212 /* Initialize a bullet (a real bullet or some number) */
213 static void FillBullet(XmHTMLWidget html, XmHTMLObjectTableElement owner,
214 	ToolkitAbstraction *tka);
215 
216 /* get properties of a table element */
217 static TableProperties *tableCheckProperties(XmHTMLWidget html,
218 	String attributes, TableProperties *parent, Alignment halign, Pixel bg);
219 
220 /* open a new table */
221 static XmHTMLTable *tableOpen(XmHTMLWidget html, XmHTMLTable *parent,
222 	XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
223 	Pixel *bg);
224 
225 /* close the current table */
226 static XmHTMLTable *tableClose(XmHTMLWidget html, XmHTMLTable *parent,
227 	XmHTMLObjectTableElement end);
228 
229 /* open a caption in the current table */
230 static void tableOpenCaption(XmHTMLWidget html, XmHTMLTable *parent,
231 	XmHTMLObjectTableElement start, XmHTMLObject *obj,
232 	Pixel *bg);
233 
234 /* close the current caption */
235 static void tableCloseCaption(XmHTMLWidget html, XmHTMLTable *parent,
236 	XmHTMLObjectTableElement end);
237 
238 /* open a row in the current table */
239 static void tableOpenRow(XmHTMLWidget html, XmHTMLTable *parent,
240 	XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
241 	Pixel *bg);
242 
243 /* close the current row */
244 static void tableCloseRow(XmHTMLWidget html, XmHTMLTable *parent,
245 	XmHTMLObjectTableElement end);
246 
247 /* open a cell in the current row */
248 static void tableOpenCell(XmHTMLWidget html, XmHTMLTable *parent,
249 	XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
250 	Pixel *bg);
251 
252 /* close the current cell in the current row */
253 static void tableCloseCell(XmHTMLWidget html, XmHTMLTable *parent,
254 	XmHTMLObjectTableElement end);
255 
256 /* Parse body tags and update the widget */
257 static void ParseBodyTags(XmHTMLWidget html, XmHTMLObject *data);
258 
259 /* Check whether a linefeed is required or not */
260 static int CheckLineFeed(int op, Boolean force, Byte *text_data);
261 
262 /* split the given anchor spec into a href, target and other stuff */
263 static void parseHref(String text, XmHTMLAnchor *anchor);
264 
265 /*** Private Variable Declarations ***/
266 /* Element data bits */
267 #define ELE_ANCHOR				(1<<1)	/* an anchor						*/
268 #define ELE_ANCHOR_TARGET		(1<<2)	/* with a target attribute			*/
269 #define ELE_ANCHOR_VISITED		(1<<3)	/* it's a previously visited one	*/
270 #define ELE_ANCHOR_INTERN		(1<<4)	/* with a name attribute			*/
271 #define ELE_UNDERLINE			(1<<5)	/* underlined object				*/
272 #define ELE_UNDERLINE_TEXT		(1<<6)	/* underlined text					*/
273 #define ELE_STRIKEOUT			(1<<7)	/* strikeout object					*/
274 #define ELE_STRIKEOUT_TEXT		(1<<8)	/* strikeout text					*/
275 
276 /* Private formatted element table data */
277 static struct{
278 #ifdef DEBUG
279 	unsigned long num_elements;
280 	unsigned long num_anchors;
281 #endif
282 	XmHTMLObjectTableElement head;
283 	XmHTMLObjectTableElement current;
284 	XmHTMLAnchor *anchor_head;
285 	XmHTMLAnchor *anchor_current;
286 }list_data;
287 
288 /* Marker information for HTML lists, ordered list. */
289 #define OL_ARRAYSIZE	5
290 static listMarkers ol_markers[OL_ARRAYSIZE] = {
291 	{"1", XmMARKER_ARABIC},
292 	{"a", XmMARKER_ALPHA_LOWER},
293 	{"A", XmMARKER_ALPHA_UPPER},
294 	{"i", XmMARKER_ROMAN_LOWER},
295 	{"I", XmMARKER_ROMAN_UPPER},
296 };
297 
298 /* Unordered list. */
299 #define UL_ARRAYSIZE	3
300 static listMarkers ul_markers[UL_ARRAYSIZE] = {
301 	{"disc", XmMARKER_DISC},
302 	{"square", XmMARKER_SQUARE},
303 	{"circle", XmMARKER_CIRCLE},
304 };
305 
306 #ifdef DEBUG
307 static int allocated;
308 #endif
309 
310 /*****
311 * Name: 		NewTableElement
312 * Return Type: 	MACRO
313 * Description: 	creates a XmHTMLObjectTableElement and fills it.
314 *				Macro for obvious perfomance reasons.
315 * In:
316 *	ELEMENT:	element to be allocated
317 *	data:		raw data for this element.
318 * Returns:
319 *	the newly created element.
320 *****/
321 #ifdef DEBUG
322 
323 #define NewTableElement(ELEMENT,DATA) do{ \
324 	(ELEMENT) = (XmHTMLObjectTableElement)malloc(sizeof(XmHTMLObjectTable)); \
325 	(void)memset((ELEMENT), 0, sizeof(XmHTMLObjectTable)); \
326 	(ELEMENT)->object = DATA; \
327 	allocated++; \
328 }while(0)
329 
330 #else
331 
332 #define NewTableElement(ELEMENT,DATA) do{ \
333 	ELEMENT = (XmHTMLObjectTableElement)malloc(sizeof(XmHTMLObjectTable)); \
334 	(void)memset(ELEMENT, 0, sizeof(XmHTMLObjectTable)); \
335 	ELEMENT->object = DATA; \
336 }while(0)
337 
338 #endif /* DEBUG */
339 
340 /*****
341 * Name: 		InsertTableElement
342 * Return Type: 	MACRO
343 * Description: 	inserts a given formatted element in the list of elements.
344 *				Macro for obvious perfomance reasons.
345 * In:
346 *	element:	element to add
347 *	is_anchor:	true if this element is an anchor.
348 * Returns:
349 *	NA
350 *****/
351 #ifdef DEBUG
352 
353 #define InsertTableElement(ELEMENT,IS_ANCHOR) do { \
354 	ELEMENT->prev = list_data.current; \
355 	list_data.current->next = ELEMENT; \
356 	list_data.current = ELEMENT; \
357 	list_data.num_elements++; \
358 	if((IS_ANCHOR)) list_data.num_anchors++; \
359 }while(0)
360 
361 #else
362 
363 #define InsertTableElement(ELEMENT,IS_ANCHOR) do { \
364 	ELEMENT->prev = list_data.current; \
365 	list_data.current->next = ELEMENT; \
366 	list_data.current = ELEMENT; \
367 }while(0)
368 
369 #endif /* DEBUG */
370 
371 /*****
372 * Name: 		parseHref
373 * Return Type: 	void
374 * Description: 	returns the url specification found in the given anchor.
375 * In:
376 *	text:		full anchor spec.
377 *	href:		url found in given anchor. Filled upon return.
378 *	target:		any target attribute found. Filled upon return.
379 *	extra:		any additional attributes found. Filled upon return.
380 * Returns:
381 *	nothing.
382 *****/
383 static void
parseHref(String text,XmHTMLAnchor * anchor)384 parseHref(String text, XmHTMLAnchor *anchor)
385 {
386 	if(text == NULL ||
387 		(anchor->href = _XmHTMLTagGetValue(text, "href")) == NULL)
388 	{
389 		/* allocate empty href field so later strcmps won't explode */
390 		anchor->href = (char *)malloc(1);
391 		anchor->href[0] = '\0'; /* fix 02/03/97-05, kdh */
392 		/*
393 		* Could be a named anchor with a target spec. Rather impossible but
394 		* allow for it anyway (I can imagine this to be true for a
395 		* split-screen display).
396 		*/
397 		if(text == NULL)
398 			return;
399 	}
400 
401 	/* Check if there is a target specification */
402 	anchor->target= _XmHTMLTagGetValue(text, "target");
403 
404 	/* Also check for rel, rev and title */
405 	anchor->rel = _XmHTMLTagGetValue(text, "rel");
406 	anchor->rev = _XmHTMLTagGetValue(text, "rev");
407 	anchor->title  = _XmHTMLTagGetValue(text, "title");
408 }
409 
410 /*****
411 * Name: 		FreeObjectTable
412 * Return Type: 	void
413 * Description: 	releases all memory occupied by the formatted list of elements.
414 * In:
415 *	list:		previous list to be released.
416 * Returns:
417 *	nothing.
418 * Note:
419 *	Images are freed in XmHTML.c, which calls XmHTMLFreeAllImages to do the
420 *	job.
421 *****/
422 static void
FreeObjectTable(XmHTMLObjectTable * list)423 FreeObjectTable(XmHTMLObjectTable *list)
424 {
425 	XmHTMLObjectTableElement temp;
426 
427 #ifdef DEBUG
428 	int i = 0, j = 0;
429 #endif
430 
431 	/* free all parsed objects */
432 	while(list != NULL)
433 	{
434 		temp = list->next;
435 		if(list->text)	/* space occupied by text to display */
436 			free(list->text);
437 
438 		/* free list of words. Can't be done above, <pre> doesn't have this! */
439 		if(list->n_words)
440 		{
441 			/*
442 			* only the first word contains a valid ptr, all others point to
443 			* some char in this buffer, so freeing them *will* cause a
444 			* segmentation fault eventually.
445 			*/
446 			free(list->words[0].word);
447 			/* Free raw word data */
448 			free(list->words);
449 		}
450 		free(list);
451 		list = temp;
452 #ifdef DEBUG
453 		i++;
454 #endif
455 	}
456 	_XmHTMLDebug(2, ("format.c: FreeObjectTable End, freed %i elements and "
457 		"%i anchors.\n", i, j));
458 }
459 
460 /*****
461 * Name:			freeTables
462 * Return Type: 	void
463 * Description: 	frees all data allocated for HTML table support.
464 * In:
465 *	table:		list of tables to be freed.
466 * Returns:
467 *	nothing.
468 *****/
469 static void
freeTables(XmHTMLTable * table)470 freeTables(XmHTMLTable *table)
471 {
472 	XmHTMLTable *tab, *tmp = table;
473 	TableRow *row;
474 	int i, j, k;
475 
476 	while(table)
477 	{
478 		tmp = table->next;
479 
480 		/*****
481 		* Free all child tables (first table in the childs array is the
482 		* table itself)
483 		*****/
484 		for(i = 0; i < table->nchilds; i++)
485 		{
486 			tab = &table->childs[i];
487 
488 			/* free all rows */
489 			for(j = 0; j < tab->nrows; j++)
490 			{
491 				row = &tab->rows[j];
492 				/* free all cells in this row */
493 				for(k = 0; k < row->ncells; k++)
494 				{
495 					free(row->cells[k].props);
496 				}
497 				free(row->cells);
498 				free(row->props);
499 			}
500 			free(tab->rows);
501 			free(tab->props);
502 		}
503 		free(table->childs);
504 		free(table);
505 		table = tmp;
506 	}
507 }
508 
509 /*****
510 * Name: 		FreeAnchors
511 * Return Type: 	void
512 * Description: 	frees the memory occupied by the anchor data
513 * In:
514 *	anchors:	list of anchors to be freed
515 * Returns:
516 *	nothing.
517 *****/
518 static void
FreeAnchors(XmHTMLAnchor * anchors)519 FreeAnchors(XmHTMLAnchor *anchors)
520 {
521 	XmHTMLAnchor *tmp;
522 	int i = 0;
523 
524 	while(anchors)
525 	{
526 		tmp = anchors->next;
527 		/* href field is always allocated */
528 		free(anchors->href);
529 		if(anchors->target)
530 			free(anchors->target);
531 		if(anchors->rel)
532 			free(anchors->rel);
533 		if(anchors->rev)
534 			free(anchors->rev);
535 		if(anchors->title)
536 			free(anchors->title);
537 		if(anchors->name)		/* fix 07/09/97-01, kdh */
538 			free(anchors->name);
539 		if(anchors->events)
540 			free(anchors->events);
541 		free(anchors);
542 		anchors = NULL;
543 		anchors = tmp;
544 		i++;
545 	}
546 	_XmHTMLDebug(2, ("format.c: FreeAnchors, freed %i XmHTMLAnchor objects\n",
547 		i));
548 }
549 
550 /*****
551 * Name: 		InitObjectTable
552 * Return Type: 	void
553 * Description: 	initializes the list of formatted elements.
554 * In:
555 *	list:		previous list to be released.
556 * Returns:
557 *	nothing
558 * Note:
559 *	The list head is a dummy element and is never used. It is done to gain
560 *	some performance (a test on an empty head is not required now in the
561 *	InsertTableElement routine).
562 *****/
563 static void
InitObjectTable(XmHTMLObjectTable * list,XmHTMLAnchor * anchors)564 InitObjectTable(XmHTMLObjectTable *list, XmHTMLAnchor *anchors)
565 {
566 	if(list != NULL)
567 	{
568 		FreeObjectTable(list);
569 		list = NULL;
570 	}
571 
572 	if(anchors != NULL)
573 	{
574 		FreeAnchors(anchors);
575 		anchors = NULL;
576 	}
577 	if(list_data.head)
578 		free(list_data.head);
579 	NewTableElement(list_data.head,NULL);
580 	list_data.current = list_data.head;
581 	list_data.anchor_head = (XmHTMLAnchor*)NULL;
582 	list_data.anchor_current = (XmHTMLAnchor*)NULL;
583 #ifdef DEBUG
584 	list_data.num_elements = 1;
585 	list_data.num_anchors  = 0;
586 #endif
587 }
588 
589 /*****
590 * Name: 		CollapseWhiteSpace
591 * Return Type: 	void
592 * Description: 	collapses whitespace in the given text
593 * In:
594 *	text:		text for which multiple whitespace has to be collapsed.
595 * Returns:
596 *	nothing, but text is updated when this function returns.
597 *****/
598 static void
CollapseWhiteSpace(String text)599 CollapseWhiteSpace(String text)
600 {
601 	register char *outPtr = text;
602 #ifdef I18N
603 	int n = 1;
604 #endif	/* I18N */
605 
606 	/*
607 	* We only collapse valid text and text that contains more than whitespace
608 	* only. This should never be true since CopyText will filter these
609 	* things out. It's just here for sanity.
610 	*/
611 	if(*text == '\0' || !strlen(text))
612 		return;
613 
614 	_XmHTMLDebug(2, ("format.c: CollapseWhiteSpace, text in is:\n%s\n", text));
615 
616 	/*
617 	* Now collapse each occurance of multiple whitespaces.
618 	* This may produce different results on different systems since
619 	* isspace() might not produce the same on each and every platform.
620 	*/
621 	while(True)
622 	{
623 
624 #ifdef I18N
625 		if((n = mblen((char*)text, (size_t)(strlen(text)))) == 1)
626 		{
627 #endif	/* I18N */
628 		switch(*text)
629 		{
630 			case '\f':
631 			case '\n':
632 			case '\r':
633 			case '\t':
634 			case '\v':
635 				*text = ' ';	/* replace by a single space */
636 				/* fall through */
637 			case ' ':
638 				/* skip past first space */
639 				*(outPtr++) = *(text++);
640 #ifdef I18N
641 				n = mblen((char*)text, (size_t)(strlen(text)));
642 				while(n == 1 && *text != '\0' && isspace(*text))
643 					*text++ = '\0';
644 #else
645 				/* collapse every space following */
646 				while(*text != '\0' && isspace(*text))
647 					*text++ = '\0';
648 #endif /* I18N */
649 				break;
650 			default:
651 				*(outPtr++) = *(text++);
652 				break;
653 		}
654 		if(*text == 0)
655 		{
656 			*outPtr = '\0';
657 			return;
658 		}
659 #ifdef I18N
660 		}
661 		else if(n > 1)
662 		{
663 			/*****
664 			* Multibyte char, copy it.
665 			*****/
666 			int i;
667 			for(i = 0; i < n; i++)
668 				*(outPtr++) = *(text++);
669 		}
670 		else
671 			break;
672 #endif	/* I18N */
673 	}
674 }
675 
676 /*****
677 * Name: 		TextToWords
678 * Return Type: 	XmHTMLWord*
679 * Description: 	splits the given text into an array of words.
680 * In:
681 *	text:		text to split
682 *	num_words:	number of words in the given text. Filled upon return;
683 *	font:		font to use for this text.
684 * Returns:
685 *	an array of words. When allocation fails, this routine exits.
686 *****/
687 static XmHTMLWord*
TextToWords(String text,int * num_words,Dimension * height,XmHTMLfont * font,Byte line_data,Byte text_data,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka)688 TextToWords(String text, int *num_words, Dimension *height, XmHTMLfont *font,
689 	Byte line_data, Byte text_data, XmHTMLObjectTableElement owner,
690 	ToolkitAbstraction *tka)
691 {
692 	int n_words, len, i;
693 	char *start;
694 	static XmHTMLWord *words;
695 	static char *raw;
696 	register int j;
697 	register char *chPtr;
698 
699 	/* sanity check */
700 	if(text == NULL)
701 	{
702 		*height = *num_words = 0;
703 		return(NULL);
704 	}
705 
706 	_XmHTMLFullDebug(2, ("format.c: TextToWords, text in is:\n%s\n", text));
707 
708 	/* compute how many words we have */
709 	n_words = 0;
710 	for(chPtr = text; *chPtr != '\0'; chPtr++)
711 		if(*chPtr == ' ')
712 			n_words++;
713 	/* also pick up the last word */
714 	n_words++;
715 
716 	/* copy text */
717 	raw = strdup(text);
718 
719 	/* allocate memory for all words */
720 	words = (XmHTMLWord*)calloc(n_words, sizeof(XmHTMLWord));
721 
722 	/* Split the text in words and fill in the appropriate fields */
723 	*height = font->height;
724 
725 	chPtr = start = raw;
726 
727 	for(i = 0, j = 0, len = 0; ; chPtr++, len++, j++)
728 	{
729 		/* also pick up the last word! */
730 		if(*chPtr == ' ' || *chPtr == '\0')
731 		{
732 			if(*chPtr)
733 			{
734 				chPtr++;			/* nuke the space */
735 				raw[j++] = '\0';
736 			}
737 			/* fill in required fields */
738 			words[i].self      = &words[i];
739 			words[i].word      = start;
740 			words[i].len       = len;
741 #ifdef I18N
742 
743 			/*****
744 			* Possible multibyte input, use multibyte functions to
745 			* determine correct size of this word.
746 			*****/
747 			if(font->type == XmHTML_FONTSET)
748 			{
749 				int nbytes = 0;
750 				Byte *mbPtr = start;
751 				int n = mblen((char*)mbPtr, (size_t)len);
752 
753 				/*****
754 				* Compute number of multibyte characters this word
755 				* occupies.
756 				*****/
757 				while(n > 0)
758 				{
759 					nbytes += n;
760 					mbPtr  += n;
761 					len    -= n;
762 					n       = mblen((char*)mbPtr, (size_t)len);
763 				}
764 				len = nbytes;	/* used by TextWidth computation */
765 				words[i].len = len;
766 			}
767 			else
768 				words[i].len   = len;
769 #else
770 			words[i].len       = len;
771 #endif	/* I18N */
772 
773 			words[i].height    = *height;
774 			words[i].width     = tka->TextWidth(font, words[i].word, len);
775 			words[i].owner     = owner;
776 			words[i].font      = font;
777 			words[i].spacing   = TEXT_SPACE_LEAD | TEXT_SPACE_TRAIL;
778 			words[i].type      = OBJ_TEXT;
779 			words[i].line_data = line_data;
780 
781 			_XmHTMLFullDebug(2, ("format.c: TextToWords, word is %s, len is "
782 				"%i, width is %i, height is %i\n", words[i].word, words[i].len,
783 				words[i].width, words[i].height));
784 
785 			start = chPtr;
786 			i++;
787 			len = 0;
788 		}
789 		if(*chPtr == '\0')
790 			break;
791 	}
792 	/*
793 	* when there is more than one word in this block, the first word
794 	* _always_ has a trailing space.
795 	* Likewise, the last word always has a leading space.
796 	*/
797 	if(n_words > 1)
798 	{
799 		/* unset nospace bit */
800 		Byte spacing = text_data & ~TEXT_SPACE_NONE;
801 		words[0].spacing = spacing | TEXT_SPACE_TRAIL;
802 		words[n_words-1].spacing = spacing | TEXT_SPACE_LEAD;
803 	}
804 	else
805 		words[0].spacing = text_data;
806 
807 	_XmHTMLFullDebug(2, ("format.c: TextToWords counted %i words\n", n_words));
808 
809 	*num_words = i; /* n_words */;
810 	return(words);
811 }
812 
813 /*****
814 * Name: 		ImageToWord
815 * Return Type: 	XmHTMLWord*
816 * Description: 	converts an image to a word
817 * In:
818 *	w:			XmHTMLWidget id
819 *	attributes:	raw <img> specification
820 *	height:		object height, updated upon return
821 *	owner:		owning object
822 *	formatted:	True when this image is part of a block of <pre></pre> text.
823 *	ToolkitAbstraction:	renderer to be used;
824 *	is_anchor:	True when image is part of an anchor, False if not.
825 * Returns:
826 *	a word representing the image
827 *****/
828 static XmHTMLWord*
ImageToWord(XmHTMLWidget html,String attributes,int * num_words,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted,ToolkitAbstraction * tka,Boolean is_anchor,Byte text_data)829 ImageToWord(XmHTMLWidget html, String attributes, int *num_words,
830 	Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted,
831 	ToolkitAbstraction *tka, Boolean is_anchor, Byte text_data)
832 {
833 	static XmHTMLWord *word;
834 	static XmHTMLImage *image;
835 	Dimension width = 0;
836 
837 	*num_words = 0;
838 
839 	/* sanity check */
840 	if(attributes == NULL ||
841 		(image = _XmHTMLNewImage(html, attributes, &width, height)) == NULL)
842 	{
843 		*height = 0;
844 		return(NULL);
845 	}
846 
847 	/*****
848 	* Check for border, isn't done in the image loading routines since
849 	* the default border is context-dependent: if it's an anchor the default
850 	* is to add a border, if it's plain text default is no border.
851 	*****/
852 	image->border = _XmHTMLTagGetNumber(attributes, "border", (int)is_anchor);
853 
854 	_XmHTMLFullDebug(2, ("format.c: ImageToWord, image in is: %s\n",
855 		image->url));
856 
857 	word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
858 
859 	/* required for image anchoring/replace/update */
860 	image->owner = owner;
861 
862 	/* fill in required fields */
863 	word->self   = word;
864 	word->word   = strdup(image->alt);	/* we always have this */
865 
866 #ifdef I18N
867 	if((HTML_ATTR(default_font))->type == XmHTML_FONTSET)
868 		_XmHTMLWarning(__WFUNC__(html, "ImageToWord"),
869 			"I18N not (yet) supported for <IMG> alt attribute.\n");
870 #else
871 	word->len    = strlen(image->alt);
872 #endif	/* I18N */
873 
874 	word->width  = width + 2*image->hspace + 2*image->border;
875 	word->height = *height + 2*image->vspace + 2*image->border;
876 	word->owner  = owner;
877 	word->font   = HTML_ATTR(default_font); /* always use the default font */
878 	/*****
879 	* if image support is disabled, add width of the alt text to the
880 	* image width (either from default image or specified in the doc).
881 	* This is required for proper exposure handling when images are disabled.
882 	*****/
883 	if(!HTML_ATTR(images_enabled))
884 		word->width += tka->TextWidth(word->font, word->word, word->len);
885 
886 	/*****
887 	* No spacing if part of a chunk of <pre></pre> text
888 	* Fix 07/24/97, kdh
889 	*****/
890 	word->spacing = formatted ? 0 : text_data;
891 	word->type = OBJ_IMG;
892 	word->line_data = NO_LINE;	/* no underlining for images */
893 	word->image = image;
894 
895 	_XmHTMLFullDebug(2, ("format.c: TextToWords, word is %s, len is %i, "
896 		"width is %i, height is %i\n", word->word, word->len,
897 		word->width, word->height));
898 
899 	*num_words = 1;
900 	return(word);
901 }
902 
903 /*****
904 * Name:			allocFormWord
905 * Return Type: 	XmHTMLWord*
906 * Description: 	allocates a default XmHTMLWord for use within a HTML form.
907 * In:
908 *	html:		XmHTMLWidget id
909 *	form:		form entry for which this word should be allocated;
910 *	*width:		object's width, updated upon return;
911 *	*height:	object's height, updated upon return;
912 *	owner:		owning object.
913 *	formatted:	true when allocating a form component present in <pre></pre>
914 * Returns:
915 *	a newly allocated word.
916 *****/
917 static XmHTMLWord*
allocFormWord(XmHTMLWidget html,XmHTMLForm * form,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)918 allocFormWord(XmHTMLWidget html, XmHTMLForm *form, Dimension *width,
919 	Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted)
920 {
921 	static XmHTMLWord *word;
922 
923 	/* allocate new entry */
924 	word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
925 
926 	/* fill in required fields */
927 	word->self    = word;
928 	word->word    = strdup(form->name); 	/* we always have this */
929 	word->len     = strlen(form->name);
930 	word->height  = *height = form->height;
931 	word->width   = *width  = form->width;
932 	word->owner   = owner;
933 	word->font    = HTML_ATTR(default_font); 	/* always use default font */
934 	word->spacing = formatted ? 0 : TEXT_SPACE_LEAD | TEXT_SPACE_TRAIL;
935 	word->type    = OBJ_FORM;
936 	word->form    = form;
937 
938 	return(word);
939 }
940 
941 /*****
942 * Name: 		InputToWord
943 * Return Type: 	XmHTMLWord*
944 * Description: 	converts a HTML form <input> element to a word
945 * In:
946 *	w:			XmHTMLWidget id
947 *	attributes:	raw form element specification
948 *	width:		object width, updated upon return
949 *	height:		object height, updated upon return
950 *	owner:		owning object
951 *	formatted:	true when this form component is placed in a <pre></pre> tag.
952 * Returns:
953 *	a word representing the image
954 *****/
955 static XmHTMLWord*
InputToWord(XmHTMLWidget html,String attributes,int * num_words,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)956 InputToWord(XmHTMLWidget html, String attributes, int *num_words,
957 	Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
958 	Boolean formatted)
959 {
960 	static XmHTMLForm *form_entry;
961 	XmHTMLWord *word;
962 
963 	*num_words = 0;
964 
965 	/* sanity check */
966 	if(attributes == NULL ||
967 		(form_entry = _XmHTMLFormAddInput(html, attributes)) == NULL)
968 		return(NULL);
969 
970 	/* save owner, we need it in the paint routines */
971 	form_entry->data = owner;
972 
973 	/* image buttons are treated as anchored images */
974 	if(form_entry->type == FORM_IMAGE)
975 	{
976 		word = ImageToWord(html, attributes, num_words, height, owner,
977 				formatted, HTML_ATTR(tka), True,
978 				TEXT_SPACE_LEAD|TEXT_SPACE_TRAIL);
979 		/* remove alt text */
980 		free(word->word);
981 		/* use form member name instead */
982 		word->word = strdup(form_entry->name);
983 		word->len  = strlen(form_entry->name);
984 		word->form = form_entry;
985 
986 		_XmHTMLFullDebug(2, ("format.c: InputToWord, word is %s, len is %i, "
987 			"width is %i, height is %i (type = image)\n", word->word,
988 			word->len, word->width, word->height));
989 
990 		return(word);
991 	}
992 
993 	/* allocate new word for this form member */
994 	word = allocFormWord(html, form_entry, width, height, owner, formatted);
995 
996 	_XmHTMLFullDebug(2, ("format.c: InputToWord, word is %s, len is %i, "
997 		"width is %i, height is %i\n", word->word, word->len,
998 		word->width, word->height));
999 
1000 	*num_words = 1;
1001 	return(word);
1002 }
1003 
1004 /*****
1005 * Name:			SelectToWord
1006 * Return Type: 	XmHTMLWord*
1007 * Description:	converts a HTML form <select></select> to a HTMLWord.
1008 *				Also processes any <option></option> items within this select.
1009 * In:
1010 *	html:		XmHTMLWidget id;
1011 *	start:		object at which <select> starts;
1012 *	*num_words:	no of words allocated. Updated upon return;
1013 *	*width:		width of returned object. Updated upon return;
1014 *	*height:	height of returned object. Updated upon return;
1015 *	owner:		owning element.
1016 *	formatted:	true when this form component is placed in a <pre></pre> tag.
1017 * Returns:
1018 *	a newly allocated word upon success. NULL on failure.
1019 *****/
1020 static XmHTMLWord*
SelectToWord(XmHTMLWidget html,XmHTMLObject * start,int * num_words,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)1021 SelectToWord(XmHTMLWidget html, XmHTMLObject *start, int *num_words,
1022 	Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
1023 	Boolean formatted)
1024 {
1025 	static XmHTMLForm *form_entry;
1026 	XmHTMLWord *word;
1027 	XmHTMLObject *tmp = start;
1028 	Boolean i18n = False;
1029 
1030 	*num_words = 0;
1031 
1032 	/* sanity check */
1033 	if(start->attributes == NULL ||
1034 		(form_entry = _XmHTMLFormAddSelect(html, start->attributes)) == NULL)
1035 		return(NULL);
1036 
1037 	/* save owner */
1038 	form_entry->data = owner;
1039 
1040 	/* move to next element */
1041 	tmp = tmp->next;
1042 
1043 	/* add all option tags */
1044 	for(; tmp != NULL && tmp->id != HT_SELECT; tmp = tmp->next)
1045 	{
1046 		if(tmp->id == HT_OPTION && !tmp->is_end)
1047 		{
1048 			XmHTMLObject *sel_start = tmp;
1049 			Byte foo;
1050 			String text = NULL;
1051 
1052 			/*
1053 			* The next object should be plain text, if not it's an
1054 			* error and we should ignore it
1055 			*/
1056 			tmp = tmp->next;
1057 			if(tmp->id != HT_ZTEXT)
1058 			{
1059 				if(HTML_ATTR(bad_html_warnings))
1060 				{
1061 					/* empty option tag, ignore it */
1062 					if(tmp->id == HT_OPTION)
1063 						_XmHTMLWarning(__WFUNC__(html, "SelectToWord"),
1064 							XMHTML_MSG_40, tmp->line);
1065 					else
1066 						_XmHTMLWarning(__WFUNC__(html, "SelectToWord"),
1067 							XMHTML_MSG_41, html_tokens[tmp->id],
1068 							html_tokens[HT_OPTION], tmp->line);
1069 				}
1070 				continue;
1071 			}
1072 			/* get text */
1073 			if((text = CopyText(html, tmp->element, False, &foo, True,
1074 				&i18n)) == NULL)
1075 				continue;
1076 
1077 			CollapseWhiteSpace(text);
1078 			if(strlen(text))
1079 			{
1080 				_XmHTMLFormSelectAddOption(html, form_entry,
1081 					sel_start->attributes, text);
1082 				/* no longer needed */
1083 				free(text);
1084 			}
1085 		}
1086 	}
1087 	/* close this selection and get width and height */
1088 	_XmHTMLFormSelectClose(html, form_entry);
1089 
1090 	/* allocate new word for this form member */
1091 	word = allocFormWord(html, form_entry, width, height, owner, formatted);
1092 
1093 	_XmHTMLFullDebug(2, ("format.c: SelectToWord, word is %s, len is %i, "
1094 		"width is %i, height is %i\n", word->word, word->len,
1095 		word->width, word->height));
1096 
1097 	*num_words = 1;
1098 	return(word);
1099 }
1100 
1101 /*****
1102 * Name:			TextAreaToWord
1103 * Return Type: 	XmHTMLWord*
1104 * Description:	converts a HTML form <textarea> to a HTMLWord.
1105 * In:
1106 *	html:		XmHTMLWidget id;
1107 *	start:		object at which <textarea> starts;
1108 *	*num_words:	no of words allocated. Updated upon return;
1109 *	*width:		width of returned object. Updated upon return;
1110 *	*height:	height of returned object. Updated upon return;
1111 *	owner:		owning element.
1112 *	formatted:	true when this form component is placed in a <pre></pre> tag.
1113 * Returns:
1114 *	a newly allocated word upon success. NULL on failure.
1115 *****/
1116 static XmHTMLWord*
TextAreaToWord(XmHTMLWidget html,XmHTMLObject * start,int * num_words,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)1117 TextAreaToWord(XmHTMLWidget html, XmHTMLObject *start, int *num_words,
1118 	Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
1119 	Boolean formatted)
1120 {
1121 	static XmHTMLForm *form_entry;
1122 	XmHTMLWord *word;
1123 	String text = NULL;
1124 	Byte foo;
1125 	Boolean i18n;
1126 
1127 	*num_words = 0;
1128 	*height = *width = 0;
1129 
1130 	/* sanity check */
1131 	if(start->attributes == NULL)
1132 		return(NULL);
1133 
1134 	/* get text between opening and closing <textarea>, if any */
1135 	if(start->next->id == HT_ZTEXT)
1136 		text = CopyText(html, start->next->element, True, &foo, False, &i18n);
1137 
1138 	/* create new form entry. text will serve as the default content */
1139 	if((form_entry = _XmHTMLFormAddTextArea(html, start->attributes,
1140 		text)) == NULL)
1141 	{
1142 		if(text)
1143 			free(text);
1144 		return(NULL);
1145 	}
1146 	form_entry->data = owner;
1147 
1148 	/* allocate new word for this form member */
1149 	word = allocFormWord(html, form_entry, width, height, owner, formatted);
1150 
1151 	_XmHTMLFullDebug(2, ("format.c: TextAreaToWord, word is %s, len is %i, "
1152 		"width is %i, height is %i\n", word->word, word->len,
1153 		word->width, word->height));
1154 
1155 	*num_words = 1;
1156 	return(word);
1157 }
1158 
1159 /*****
1160 * Name:			indexToWord
1161 * Return Type: 	XmHTMLWord
1162 * Description: 	creates a prefix for numbered lists with the ISINDEX
1163 *				attribute set.
1164 * In:
1165 *	html:		XmHTMLWidget id;
1166 *	list_stack:	stack of all lists;
1167 *	current...:	current list id;
1168 *	owner:		owning element.
1169 *	formatted:	true when this form component is placed in a <pre></pre> tag.
1170 * Returns:
1171 *	a newly allocated word.
1172 * Note:
1173 *	This routine creates the prefix based on the type and depth of the
1174 *	current list. All types can be intermixed, so this routine is capable
1175 *	of returning something like 1.A.IV.c.iii for a list nested five levels,
1176 *	the first with type `1', second with type `A', third with type `I',
1177 *	fourth with type `a' and fifth with type `i'.
1178 *****/
1179 static XmHTMLWord*
indexToWord(XmHTMLWidget html,listStack list_stack[],int current_list,XmHTMLObjectTableElement owner,Boolean formatted)1180 indexToWord(XmHTMLWidget html, listStack list_stack[], int current_list,
1181 	XmHTMLObjectTableElement owner, Boolean formatted)
1182 {
1183 	static XmHTMLWord *word;
1184 	int i;
1185 	char index[128], number[128];	/* enough for a zillion numbers & depths */
1186 
1187 	word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1188 
1189 	(void)memset(&index, '\0', 128);
1190 	for(i = 0; i < current_list; i++)
1191 	{
1192 		if(list_stack[i].type == HT_OL)
1193 		{
1194 			switch(list_stack[i].marker)
1195 			{
1196 				case XmMARKER_ALPHA_LOWER:
1197 					sprintf(number, "%s.", ToAsciiLower(list_stack[i].level));
1198 					break;
1199 				case XmMARKER_ALPHA_UPPER:
1200 					sprintf(number, "%s.", ToAsciiUpper(list_stack[i].level));
1201 					break;
1202 				case XmMARKER_ROMAN_LOWER:
1203 					sprintf(number, "%s.", ToRomanLower(list_stack[i].level));
1204 					break;
1205 				case XmMARKER_ROMAN_UPPER:
1206 					sprintf(number, "%s.", ToRomanUpper(list_stack[i].level));
1207 					break;
1208 				case XmMARKER_ARABIC:
1209 				default:
1210 					sprintf(number, "%i.", list_stack[i].level);
1211 					break;
1212 			}
1213 			/* no buffer overflow */
1214 			if(strlen(index) + strlen(number) > 128)
1215 				break;
1216 			strcat(index, number);
1217 		}
1218 	}
1219 
1220 	/* fill in required fields */
1221 	word->word = strdup(index);
1222 	word->len  = strlen(index);
1223 	word->self      = word;							/* unused */
1224 	word->owner     = owner;						/* unused */
1225 	word->font      = HTML_ATTR(default_font);		/* unused */
1226 	word->spacing   = formatted ? 0 : TEXT_SPACE_NONE;
1227 	word->type      = OBJ_TEXT;						/* unused */
1228 	word->line_data = NO_LINE;						/* unused */
1229 
1230 	return(word);
1231 }
1232 
1233 /*****
1234 * Name: 		TextToPre
1235 * Return Type: 	XmHTMLWord*
1236 * Description: 	splits the given text into an array of preformatted lines
1237 * In:
1238 *	text:		text to split
1239 *	num_words:	number of words in the given text. Filled upon return;
1240 *	font:		font to use for this text.
1241 * Returns:
1242 *	an array of words. When allocation fails, this routine exits.
1243 * Note:
1244 *	the static var nchars is used to propagate the tab index to another
1245 *	chunk of preformatted text if the current text is a block of preformatted
1246 *	text with whatever formatting. It is only reset if an explicit newline
1247 *	is encountered.
1248 *****/
1249 static XmHTMLWord*
TextToPre(String text,int * num_words,XmHTMLfont * font,Byte line_data,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka,int tabwidth)1250 TextToPre(String text, int *num_words, XmHTMLfont *font, Byte line_data,
1251 	XmHTMLObjectTableElement owner, ToolkitAbstraction *tka, int tabwidth)
1252 {
1253 	int nwords, len, i, j, ntabs, max_width, in_word, size, nfeeds;
1254 	static char *raw;
1255 	static XmHTMLWord *words;
1256 	static int nchars = 0;
1257 	register char *chPtr, *start, *end;
1258 #ifdef DEBUG
1259 	int used;
1260 #endif
1261 
1262 	/* sanity check */
1263 	if(text == NULL)
1264 	{
1265 		*num_words = 0;
1266 		return(NULL);
1267 	}
1268 
1269 	_XmHTMLFullDebug(2, ("format.c: TextToPre, text in is:\n%s\n", text));
1270 
1271 	chPtr = text;
1272 	raw = NULL;
1273 
1274 	/*****
1275 	* compute how many words we have. A preformatted word is started
1276 	* with a printing char and is terminated by either a newline or a
1277 	* sequence of whitespaces. Multiple newlines are collapsed into a
1278 	* single word where the height of the word indicates the number of
1279 	* newlines to insert.
1280 	*****/
1281 	in_word = nwords = ntabs = 1;	/* fix 01/30/97-02, kdh */
1282 	while(True)
1283 	{
1284 		switch(*chPtr)
1285 		{
1286 			/* tabs and single spaces are collapsed */
1287 			case '\t':	/* horizontal tab */
1288 			case ' ':
1289 				if(in_word)
1290 				{
1291 					while(*chPtr != '\0' && (*chPtr == ' ' || *chPtr == '\t'))
1292 					{
1293 						if(*chPtr == '\t')
1294 							ntabs++;	/* need to know how many to expand */
1295 						chPtr++;
1296 					}
1297 					nwords++;
1298 					in_word = False;
1299 				}
1300 				else
1301 				{
1302 					/* fix 03/23/97-01, kdh */
1303 					if(*chPtr == '\t')
1304 						ntabs++;	/* need to know how many to expand */
1305 					chPtr++;
1306 				}
1307 				break;
1308 			/* newlines reset the tab index and are collapsed */
1309 			case '\n':
1310 				while(*chPtr != '\0' && *chPtr == '\n')
1311 					chPtr++;
1312 				nwords++;	/* current word is terminated */
1313 				nchars = 1;
1314 				break;
1315 			default:
1316 				chPtr++;
1317 				in_word = True;
1318 				break;
1319 		}
1320 		if(*chPtr == '\0')
1321 			break;
1322 	}
1323 
1324 	/* sanity check */
1325 	if(nwords == 0)
1326 	{
1327 		*num_words = 0;
1328 		return(NULL);
1329 	}
1330 
1331 	/* add an extra word and tab for safety */
1332 	nwords++;	/* preformatted text with other formatting *needs* this */
1333 	ntabs++;
1334 
1335 	/* compute amount of memory to allocate */
1336 	size = ((ntabs*tabwidth)+strlen(text)+1)*sizeof(char);
1337 
1338 	raw = (char*)malloc(size);
1339 
1340 	_XmHTMLDebug(2, ("format.c: TextToPre, allocated %i bytes\n", size));
1341 
1342 	/* allocate memory for all words */
1343 	words = (XmHTMLWord*)calloc(nwords, sizeof(XmHTMLWord));
1344 
1345 	chPtr = text;
1346 	end = raw;
1347 
1348 	/*****
1349 	* If the previous element is also part of a pre-formatted object
1350 	* that doesn't contain a newline, we need to get it's length.
1351 	* This is required for proper continuation of horizontal tabs.
1352 	* If we do not do this, tabulation will be incorrect.
1353 	*****/
1354 	if(owner->prev->object_type == OBJ_PRE_TEXT)
1355 	{
1356 		XmHTMLWord lastw = owner->prev->words[owner->prev->n_words-1];
1357 
1358 		if(!lastw.spacing)
1359 		{
1360 			/* prevent divide by zero */
1361 			int sw = lastw.font->isp ? lastw.font->isp : 3;
1362 
1363 			if(lastw.type == OBJ_IMG && lastw.image && lastw.image->width)
1364 					nchars = lastw.image->width / sw;
1365 			else if(lastw.type == OBJ_FORM && lastw.form && lastw.form->width)
1366 					nchars = lastw.form->width / sw;
1367 			else
1368 				nchars = lastw.len;
1369 		}
1370 	}
1371 
1372 #ifdef DEBUG
1373 	used = 0;
1374 #endif
1375 	/* first filter out all whitespace and other non-printing characters */
1376 	while(True)
1377 	{
1378 		switch(*chPtr)
1379 		{
1380 			case '\f':	/* formfeed, ignore */
1381 			case '\r':	/* carriage return, ignore */
1382 			case '\v':	/* vertical tab, ignore */
1383 				chPtr++;
1384 #ifdef DEBUG
1385 				used++;
1386 #endif
1387 				break;
1388 			case '\t':	/* horizontal tab */
1389 				/* no of ``floating spaces'' to emulate a tab */
1390 				len = ((nchars / tabwidth) + 1) * tabwidth;
1391 				for(j = 0; j < (len - nchars); j++)
1392 				{
1393 					*end++ = ' ';		/* insert a tab */
1394 #ifdef DEBUG
1395 					used++;
1396 #endif
1397 				}
1398 				nchars = len;
1399 #ifdef DEBUG
1400 				used++;
1401 #endif
1402 				chPtr++;
1403 				break;
1404 			/* newlines reset the tab index */
1405 			case '\n':
1406 				nchars = 0;	/* reset tab spacing index */
1407 				/* fall thru */
1408 			default:
1409 				nchars++;
1410 				*end++ = *chPtr++;
1411 #ifdef DEBUG
1412 				used++;
1413 #endif
1414 				break;
1415 		}
1416 		if(*chPtr == '\0')	/* terminate loop */
1417 		{
1418 			*end = '\0';
1419 			break;
1420 		}
1421 	}
1422 	_XmHTMLDebug(2, ("format.c: TextToPre, %i bytes actually used\n", used));
1423 
1424 	/* Now go and fill all words */
1425 	start = end = raw;
1426 	max_width = i = len = 0;
1427 	nfeeds = 0;
1428 
1429 	while(True)
1430 	{
1431 		/* also pick up the last word! */
1432 		if(*end == ' ' || *end == '\n' || *end == '\0')
1433 		{
1434 			if(*end)
1435 			{
1436 				/* skip past all spaces */
1437 				while(*end != '\0' && *end != '\n' && *end == ' ')
1438 				{
1439 					end++;
1440 					len++;
1441 				}
1442 
1443 				/*****
1444 				* if this word is ended by a newline, remove the newline.
1445 				* X doesn't know how to interpret them.
1446 				* We also want to recognize multiple newlines, so we must
1447 				* skip past them.
1448 				*****/
1449 				if(*end == '\n')
1450 				{
1451 					while(*end != '\0' && *end == '\n')
1452 					{
1453 						nfeeds++;
1454 						*end++ = '\0';
1455 					}
1456 					/*****
1457 					* Since the no of newlines to add is stored in a
1458 					* Byte, we need to limit the no of newlines to the
1459 					* max. value a Byte can have: 255 (= 2^8)
1460 					*****/
1461 					if(nfeeds > 255)
1462 						nfeeds = 255;
1463 				}
1464 			}
1465 
1466 			words[i].type      = OBJ_TEXT;
1467 			words[i].self      = &words[i];
1468 			words[i].word      = start;
1469 			words[i].height    = font->height;
1470 			words[i].owner     = owner;
1471 			words[i].spacing   = (Byte)nfeeds;	/* no of newlines */
1472 			words[i].font      = font;
1473 			words[i].line_data = line_data;
1474 			words[i].len       = len;
1475 			words[i].width     = tka->TextWidth(font, words[i].word, len);
1476 			start = end;
1477 			i++;
1478 			len = 0;
1479 			nfeeds = 0;
1480 		}
1481 		if(*end == '\0')	/* terminate loop */
1482 			break;
1483 		end++;	/* move to the next char */
1484 		len++;
1485 	}
1486 
1487 	_XmHTMLDebug(2, ("format.c: TexToPre, allocated %i words, %i actually "
1488 		"used\n", nwords, i));
1489 
1490 	/* total no of words */
1491 	*num_words = i;
1492 	return(words);
1493 }
1494 
1495 static XmHTMLWord*
BreakToWord(Dimension * height,XmHTMLfont * font,int linefeed,XmHTMLObjectTableElement owner)1496 BreakToWord(Dimension *height, XmHTMLfont *font, int linefeed,
1497 	XmHTMLObjectTableElement owner)
1498 {
1499 	static XmHTMLWord *word;
1500 
1501 	word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1502 
1503 	word->type      = OBJ_BLOCK;
1504 	word->self      = word;
1505 	word->word      = (String)malloc(1);	/* needs an empty word */
1506 	word->word[0]   = '\0';
1507 	word->len       = 0;
1508 	word->height    = font->height;			/* height of current font */
1509 	word->owner     = owner;
1510 	word->spacing   = TEXT_SPACE_NONE;		/* obviously no spacing */
1511 	word->line_data = linefeed+1;	/* how much lines to break */
1512 	word->font      = font;
1513 
1514 	return(word);
1515 }
1516 
1517 static XmHTMLWord*
MakeDummyWord(Dimension * height,XmHTMLfont * font,Byte line_data,XmHTMLObjectTableElement owner)1518 MakeDummyWord(Dimension *height, XmHTMLfont *font, Byte line_data,
1519 	XmHTMLObjectTableElement owner)
1520 {
1521 	static XmHTMLWord *word;
1522 
1523 	word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1524 
1525 	word->type      = OBJ_TEXT;
1526 	word->self      = word;
1527 	word->word      = (String)malloc(1);		/* needs an empty word */
1528 	word->word[0]   = '\0';
1529 	word->len       = 0;
1530 	word->height    = *height = font->height;	/* height of current font */
1531 	word->owner     = owner;
1532 	word->line_data = line_data;
1533 	word->font      = font;
1534 	/* an empty word acts as a single space */
1535 	word->spacing   = TEXT_SPACE_LEAD|TEXT_SPACE_TRAIL;
1536 
1537 	return(word);
1538 }
1539 
1540 
1541 /*****
1542 * Name: 		SetTab
1543 * Return Type: 	XmHTMLWord*
1544 * Description: 	returns a XmHTMLWord with spaces required for a tab.
1545 * In:
1546 * Returns:
1547 *	the tab.
1548 *****/
1549 static XmHTMLWord*
SetTab(int size,Dimension * height,XmHTMLfont * font,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka)1550 SetTab(int size, Dimension *height, XmHTMLfont *font,
1551 	XmHTMLObjectTableElement owner, ToolkitAbstraction *tka)
1552 {
1553 	static XmHTMLWord *tab;
1554 	static char *raw;
1555 
1556 	/* The tab itself */
1557 	raw = (char*)malloc((size+1)*sizeof(char));
1558 
1559 	/* fill with spaces */
1560 	(void)memset(raw, ' ', size);
1561 	raw[size] = '\0'; /* NULL terminate */
1562 
1563 	tab = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1564 
1565 	/* Set all text fields for this tab */
1566 	tab->self      = tab;
1567 	tab->word      = raw;
1568 	tab->len       = size;
1569 	tab->height    = *height = font->height;
1570 	tab->width     = tka->TextWidth(font, raw, size);
1571 	tab->owner     = owner;
1572 	tab->spacing   = TEXT_SPACE_NONE;	/* a tab is already spacing */
1573 	tab->font      = font;
1574 	tab->type      = OBJ_TEXT;
1575 	tab->line_data = NO_LINE;
1576 
1577 	return(tab);
1578 }
1579 
1580 /*****
1581 * Name: 		CopyText
1582 * Return Type: 	String
1583 * Description: 	copies the given text to a newly malloc'd buffer.
1584 * In:
1585 *	html:		XmHTMLWidget id;
1586 *	text:		text to clean out.
1587 *	formatted:	true when this text occurs inside <pre></pre>
1588 *	text_data:	text option bits, spacing and such
1589 *	expand_escapes:
1590 *				True -> expand escape sequences in text. Only viable when
1591 *				copying pre-formatted text (plain text documents are handled
1592 *				internally as consisting completely of preformatted text for
1593 *				which the escapes may not be expanded).
1594 * Returns:
1595 *	cleaned up text. Terminates if malloc fails.
1596 *****/
1597 static String
CopyText(XmHTMLWidget html,String text,Boolean formatted,Byte * text_data,Boolean expand_escapes,Boolean * i18n)1598 CopyText(XmHTMLWidget html, String text, Boolean formatted, Byte *text_data,
1599 	Boolean expand_escapes, Boolean *i18n)
1600 {
1601 	static String ret_val;
1602 	char *start = text;
1603 	int len;
1604 #ifdef I18N
1605 	char *chPtr, *end;
1606 	int n;
1607 	*i18n = False;			/* assume no multibyte text yet */
1608 #endif	/* I18N */
1609 
1610 	/* sanity check */
1611 	if(*text == '\0' || !strlen(text))
1612 		return(NULL);
1613 
1614 	/* preformatted text, just copy and return */
1615 	if(formatted)
1616 	{
1617 		/* formatted text resets spacing */
1618 		*text_data = TEXT_SPACE_NONE;
1619 		ret_val    = strdup(text);
1620 		/* expand all escape sequences in this text */
1621 		if(expand_escapes)
1622 			_XmHTMLExpandEscapes(ret_val, HTML_ATTR(bad_html_warnings));
1623 		return(ret_val);
1624 	}
1625 
1626 	_XmHTMLFullDebug(2, ("format.c: CopyText, text in is:\n%s\n", text));
1627 
1628 	/* initial length of full text */
1629 	len = strlen(text);
1630 
1631 	/*****
1632 	* Check interword spacing.
1633 	* If we've just had an explicit break, only check for possible trailing
1634 	* spaces.
1635 	*****/
1636 	if(*text_data & TEXT_BREAK && isspace(text[len-1]))
1637 		*text_data |= TEXT_SPACE_TRAIL;
1638 	else
1639 	{
1640 		/* unset no-space bit */
1641 		*text_data &= ~TEXT_SPACE_NONE;
1642 		/*****
1643 		* If first char is a space or the previous chunk had a trailing space,
1644 		* this chunk gets a leading space. Else it's glueud to the previous
1645 		* chunk.
1646 		*****/
1647 		if((isspace(*text) || *text_data & TEXT_SPACE_TRAIL))
1648 			*text_data = TEXT_SPACE_LEAD;
1649 		else
1650 			*text_data &= ~TEXT_SPACE_LEAD;
1651 
1652 		/* do we have a trailing space? */
1653 		if(isspace(text[len-1]))
1654 			*text_data |= TEXT_SPACE_TRAIL;
1655 		else
1656 			*text_data &= ~TEXT_SPACE_TRAIL;
1657 
1658 		/* set no-space bit if we don't have any spaces */
1659 		if(!(*text_data & TEXT_SPACE_TRAIL) && !(*text_data & TEXT_SPACE_LEAD))
1660 			*text_data |= TEXT_SPACE_NONE;
1661 	}
1662 
1663 	/*****
1664 	* Now remove leading/trailing spaces
1665 	* very special case: spaces between different text formatting
1666 	* elements must be retained
1667 	*****/
1668 	/* remove all leading space */
1669 #ifdef I18N
1670 	while(*start != '\0' && isspace(*start))
1671 	{
1672 		/* compute size of this character */
1673 		n = mblen((char*)text, (size_t)(strlen(start)));
1674 		if(n > 1)
1675 			break;				/* not a space */
1676 		if(n > 0)
1677 			start = start + n;	/* a space, skip to next multibyte char */
1678 		else
1679 			break;				/* break on error */
1680 	}
1681 
1682 	/* remove all trailing space */
1683 	chPtr = start;
1684 	n     = mblen((char*)start, (size_t)(strlen(start)));
1685 	end   = (char*)NULL;
1686 
1687 	while(n > 0)
1688 	{
1689 		if(n == 1)
1690 		{
1691 			/* not a multibyte character */
1692 			if(!isspace(*chPtr))
1693 				end = (char*)NULL;	/* last char not a space, move to next */
1694 			else if(end == NULL)
1695 				end = chPtr;		/* possible end space */
1696 		}
1697 		else if(n > 1)
1698 		{
1699 			int z;
1700 			end = (char*)NULL;		/* multibyte char, not a space */
1701 			*i18n = True;			/* we have found multibyte text */
1702 		}
1703 		else
1704 			break;					/* break on error */
1705 
1706 		/* compute size of next char */
1707 		n = mblen((char*)chPtr, (size_t)(strlen(chPtr)));
1708 		if(n > 0)
1709 		{
1710 			chPtr += n;				/* move to next char */
1711 		}
1712 	}
1713 #else
1714 	while(*start != '\0' && isspace(*start))
1715 		start++;
1716 
1717 	/* remove all trailing space */
1718 	len = strlen(start);
1719 	while(len > 0 && isspace(start[len-1]))
1720 		len--;
1721 
1722 #endif /* I18N */
1723 
1724 	/*****
1725 	* Spaces *can* appear between different text formatting elements.
1726 	* We want to retain this spacing since the above whitespace checking
1727 	* only yields the current element, and does not take the previous text
1728 	* element into account, *UNLESS* we just had a break.
1729 	*
1730 	* So when the current element doesn't have any leading or trailing spaces,
1731 	* we use the spacing from the previous full whitespace element.
1732 	*****/
1733 	if(!len)
1734 	{
1735 		if(*text_data & TEXT_BREAK)
1736 		{
1737 			*text_data &= (~TEXT_BREAK & ~TEXT_SPACE_TRAIL);
1738 			*text_data |= TEXT_SPACE_NONE;
1739 		}
1740 		return(NULL);
1741 	}
1742 
1743 	/* release break bit, no longer needed */
1744 	*text_data &= ~TEXT_BREAK;
1745 
1746 	/*****
1747 	* We are a little bit to generous here: consecutive multiple whitespace
1748 	* will be collapsed into a single space, so we may over-allocate.
1749 	* Hey, better to overdo this than to have one byte to short ;-)
1750 	*****/
1751 	ret_val = (String)malloc((len+1)*sizeof(char));
1752 	strncpy(ret_val, start, len);	/* copy it */
1753 	ret_val[len] = '\0';			/* NULL terminate */
1754 
1755 	/* expand all escape sequences in this text */
1756 	if(expand_escapes)
1757 		_XmHTMLExpandEscapes(ret_val, HTML_ATTR(bad_html_warnings));
1758 
1759 #ifdef I18N
1760 	if(*i18n == True)
1761 		fprintf(stderr, "found multibyte text!\n");
1762 #endif
1763 	return(ret_val);
1764 }
1765 
1766 /*****
1767 * Name: 		ParseBodyTags
1768 * Return Type: 	void
1769 * Description: 	checks the <BODY> element for additional tags
1770 * In:
1771 *	w:			HTML widget to check
1772 *	data:		body element data.
1773 * Returns:
1774 *	nothing, but the HTML widget is updated to reflect the body stuff.
1775 *****/
1776 static void
ParseBodyTags(XmHTMLWidget html,XmHTMLObject * data)1777 ParseBodyTags(XmHTMLWidget html, XmHTMLObject *data)
1778 {
1779 	char *chPtr;
1780 	Boolean bg_color_set = False;	/* flag for bodyImage substitution */
1781 
1782 	/* Check for HTML4.0 body events */
1783 	HTML_ATTR(body_events) = _XmHTMLCheckBodyEvents(html, data->attributes,
1784 								&(HTML_ATTR(event_mask)));
1785 
1786 	/* check all body color tags */
1787 	if(HTML_ATTR(body_colors_enabled) && data->attributes != NULL)
1788 	{
1789 		Boolean doit = True;
1790 
1791 		if((chPtr = _XmHTMLTagGetValue(data->attributes, "text")))
1792 		{
1793 			if(HTML_ATTR(strict_checking))
1794 				doit = _XmHTMLConfirmColor32(chPtr);
1795 
1796 			if(doit)
1797 				HTML_ATTR(body_fg) = _XmHTMLGetPixelByName(html, chPtr,
1798 					HTML_ATTR(body_fg_save));
1799 			free(chPtr);
1800 
1801 			/* also set as foreground for the entire widget */
1802 			MGR_ATTR(foreground) = HTML_ATTR(body_fg);
1803 		}
1804 
1805 		if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "bgcolor")))
1806 		{
1807 			bg_color_set = True;
1808 
1809 			if(HTML_ATTR(strict_checking))
1810 				doit = _XmHTMLConfirmColor32(chPtr);
1811 
1812 			/* only change if we had success */
1813 			if(doit)
1814 			{
1815 				HTML_ATTR(body_bg) = _XmHTMLGetPixelByName(html, chPtr,
1816 					HTML_ATTR(body_bg_save));
1817 
1818 				/* also set as background for the entire widget */
1819 				CORE_ATTR(background_pixel) = HTML_ATTR(body_bg);
1820 
1821 				/* get new values for top, bottom & highlight */
1822 				XmHTMLTkaRecomputeColors(html, HTML_ATTR(body_bg));
1823 			}
1824 
1825 			free(chPtr);
1826 		}
1827 
1828 		if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "link")))
1829 		{
1830 			if(HTML_ATTR(strict_checking))
1831 				doit = _XmHTMLConfirmColor32(chPtr);
1832 
1833 			if(doit)
1834 				HTML_ATTR(anchor_fg) = _XmHTMLGetPixelByName(html, chPtr,
1835 					HTML_ATTR(anchor_fg_save));
1836 			free(chPtr);
1837 		}
1838 
1839 		if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "vlink")))
1840 		{
1841 			if(HTML_ATTR(strict_checking))
1842 				doit = _XmHTMLConfirmColor32(chPtr);
1843 			if(doit)
1844 				HTML_ATTR(anchor_visited_fg) = _XmHTMLGetPixelByName(html,
1845 					chPtr, HTML_ATTR(anchor_visited_fg_save));
1846 			free(chPtr);
1847 		}
1848 
1849 		if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "alink")))
1850 		{
1851 			if(HTML_ATTR(strict_checking))
1852 				doit = _XmHTMLConfirmColor32(chPtr);
1853 			if(doit)
1854 				HTML_ATTR(anchor_activated_fg) = _XmHTMLGetPixelByName(html,
1855 					chPtr, HTML_ATTR(anchor_activated_fg_save));
1856 			free(chPtr);
1857 		}
1858 		/*****
1859 		* an invalid color spec, ignore them all together and revert to
1860 		* saved settings
1861 		*****/
1862 		if(doit == False)
1863 		{
1864 			/* first check if we changed the background color */
1865 			if(CORE_ATTR(background_pixel) != HTML_ATTR(body_bg_save))
1866 			{
1867 				HTML_ATTR(body_fg)          = HTML_ATTR(body_fg_save);
1868 				HTML_ATTR(body_bg)          = HTML_ATTR(body_bg_save);
1869 				MGR_ATTR(foreground)	    = HTML_ATTR(body_fg);
1870 				CORE_ATTR(background_pixel) = HTML_ATTR(body_bg);
1871 
1872 				/* restore values for top, bottom & highlight */
1873 				XmHTMLTkaRecomputeColors(html, HTML_ATTR(body_bg));
1874 			}
1875 
1876 			HTML_ATTR(body_fg)            = HTML_ATTR(body_fg_save);
1877 			HTML_ATTR(body_bg)            = HTML_ATTR(body_bg_save);
1878 			HTML_ATTR(anchor_fg)          = HTML_ATTR(anchor_fg_save);
1879 			HTML_ATTR(anchor_visited_fg)  = HTML_ATTR(anchor_visited_fg_save);
1880 			HTML_ATTR(anchor_activated_fg)=HTML_ATTR(anchor_activated_fg_save);
1881 			MGR_ATTR(foreground)          = HTML_ATTR(body_fg);
1882 			bg_color_set = False;
1883 		}
1884 	}
1885 
1886 	/* Check background image spec. First invalidate any existing body image */
1887 	if(HTML_ATTR(body_image))
1888 		HTML_ATTR(body_image->options) |= IMG_ORPHANED;
1889 	HTML_ATTR(body_image) = (XmHTMLImage*)NULL;
1890 	HTML_ATTR(body_image_url) = NULL;
1891 
1892 	/*
1893 	* *ALWAYS* load the body image if we want the SetValues method to
1894 	* behave itself.
1895 	*/
1896 	if(data->attributes &&
1897 		(chPtr = _XmHTMLTagGetValue(data->attributes, "background")))
1898 	{
1899 		_XmHTMLLoadBodyImage(html, chPtr);
1900 		/* store document's body image location */
1901 		if(HTML_ATTR(body_image))
1902 			HTML_ATTR(body_image_url) = HTML_ATTR(body_image->url);
1903 		free(chPtr);
1904 	}
1905 	/*
1906 	* Use default body image if present *and* if no background color
1907 	* has been set.
1908 	*/
1909 	else if(!bg_color_set && HTML_ATTR(def_body_image_url))
1910 		_XmHTMLLoadBodyImage(html, HTML_ATTR(def_body_image_url));
1911 
1912 	/*****
1913 	* Now nullify it if we aren't to show the background image.
1914 	* makes sense huh? (the list of images is a global widget resource so the
1915 	* storage occupied by this unused image is freed when all document
1916 	* data is freed).
1917 	*****/
1918 	if(!HTML_ATTR(images_enabled) || !HTML_ATTR(body_images_enabled))
1919 	{
1920 		if(HTML_ATTR(body_image))
1921 			HTML_ATTR(body_image->options) |= IMG_ORPHANED;
1922 		HTML_ATTR(body_image) = NULL;
1923 	}
1924 	/*****
1925 	* When a body image is present it is very likely that a highlight
1926 	* color based upon the current background actually makes an anchor
1927 	* invisible when highlighting is selected. Therefore we base the
1928 	* highlight color on the activated anchor background when we have a body
1929 	* image, and on the document background when no body image is present.
1930 	*****/
1931 	if(HTML_ATTR(body_image))
1932 		XmHTMLTkaRecomputeHighlightColor(html, HTML_ATTR(anchor_activated_fg));
1933 	else
1934 		XmHTMLTkaRecomputeHighlightColor(html, HTML_ATTR(body_bg));
1935 }
1936 
1937 static void
FillBullet(XmHTMLWidget html,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka)1938 FillBullet(XmHTMLWidget html, XmHTMLObjectTableElement owner,
1939 	ToolkitAbstraction *tka)
1940 {
1941 	Dimension radius;
1942 	char number[128];	/* large enough buffer for a zillion numbers */
1943 	XmHTMLfont *font = HTML_ATTR(default_font);
1944 	String prefix;
1945 
1946 	/*****
1947 	* x-offset for any marker and radius for a bullet or length of a
1948 	* side for a square marker.
1949 	*****/
1950 	radius = (Dimension)(0.5*(font->m_lbearing + font->m_rbearing));
1951 
1952 	if(owner->marker == XmMARKER_DISC || owner->marker == XmMARKER_SQUARE ||
1953 		owner->marker == XmMARKER_CIRCLE)
1954 	{
1955 		/* y-offset for this marker */
1956 		owner->height = (Dimension)(0.5*font->lineheight + 0.25*radius);
1957 		owner->width = radius;
1958 	}
1959 	else
1960 	{
1961 		/*****
1962 		* If we have a word, this is an ordered list for which the index
1963 		* should be propageted.
1964 		*****/
1965 		if(owner->words)
1966 			prefix = owner->words[0].word;
1967 		else
1968 			prefix = "";
1969 		switch(owner->marker)
1970 		{
1971 			case XmMARKER_ALPHA_LOWER:
1972 				sprintf(number, "%s%s.", prefix,
1973 					ToAsciiLower(owner->list_level));
1974 				break;
1975 			case XmMARKER_ALPHA_UPPER:
1976 				sprintf(number, "%s%s.", prefix,
1977 					ToAsciiUpper(owner->list_level));
1978 				break;
1979 			case XmMARKER_ROMAN_LOWER:
1980 				sprintf(number, "%s%s.", prefix,
1981 					ToRomanLower(owner->list_level));
1982 				break;
1983 			case XmMARKER_ROMAN_UPPER:
1984 				sprintf(number, "%s%s.", prefix,
1985 					ToRomanUpper(owner->list_level));
1986 				break;
1987 			case XmMARKER_ARABIC:
1988 			default:
1989 				sprintf(number, "%s%i.", prefix, owner->list_level);
1990 				break;
1991 		}
1992 		owner->text  = strdup(number);
1993 		owner->len   = strlen(number);
1994 		owner->width = radius + tka->TextWidth(font, owner->text, owner->len);
1995 		owner->height = HTML_ATTR(default_font)->height;
1996 	}
1997 }
1998 
1999 /*****
2000 * Name: 		CheckLineFeed
2001 * Return Type: 	int
2002 * Description: 	checks wether the requested newline is honored.
2003 * In:
2004 *	op:		newline to add.
2005 *	force:		add the requested newline anyway
2006 *	text_data:	current trailing/leading spacing
2007 * Returns:
2008 *	computed vertical pixel offset.
2009 * Note:
2010 *	any of CLEAR_NONE, CLEAR_SOFT or CLEAR_HARD
2011 *****/
2012 static int
CheckLineFeed(int op,Boolean force,Byte * text_data)2013 CheckLineFeed(int op, Boolean force, Byte *text_data)
2014 {
2015 	static int prev_state = CLEAR_NONE;
2016 	int ret_val = op;
2017 
2018 	/* anything except no-op clears spacing */
2019 	if(op != CLEAR_NONE)
2020 	{
2021 		*text_data &= (~TEXT_SPACE_LEAD & ~TEXT_SPACE_TRAIL);
2022 		*text_data |= TEXT_SPACE_NONE;
2023 	}
2024 
2025 	if(force)
2026 	{
2027 		prev_state = op;
2028 		return(ret_val);
2029 	}
2030 
2031 	/* multiple soft and hard returns are never honored */
2032 	switch(op)
2033 	{
2034 		case CLEAR_HARD:
2035 			if(prev_state == CLEAR_SOFT)
2036 			{
2037 				ret_val = CLEAR_SOFT;
2038 				prev_state = CLEAR_HARD;
2039 				break;
2040 			}
2041 			if(prev_state == CLEAR_HARD)
2042 			{
2043 				/* unchanged */
2044 				ret_val = CLEAR_NONE;
2045 				break;
2046 			}
2047 			prev_state = ret_val = op;
2048 			break;
2049 		case CLEAR_SOFT:
2050 			if(prev_state == CLEAR_SOFT)
2051 			{
2052 				ret_val = CLEAR_NONE;
2053 				prev_state = CLEAR_SOFT;
2054 				break;
2055 			}
2056 			if(prev_state == CLEAR_HARD)
2057 			{
2058 				/* unchanged */
2059 				ret_val = CLEAR_NONE;
2060 				break;
2061 			}
2062 			ret_val = prev_state = op;
2063 			break;
2064 		case CLEAR_NONE:
2065 			ret_val = prev_state = op;
2066 			break;
2067 	}
2068 	return(ret_val);
2069 }
2070 
2071 /*****
2072 * Name:			tableCheckProperties
2073 * Return Type: 	TableProperties
2074 * Description: 	scans a table element for common properties (border,
2075 *				alignment, background and border styles).
2076 * In:
2077 *	html:		XmHTMLWidget id;
2078 *	attributes:	attributes to be checked;
2079 *	parent:		properties of parent table element. Properties not found
2080 *				in the attributes are inherited from this parent.
2081 * Returns:
2082 *	a new property.
2083 *****/
2084 static TableProperties*
tableCheckProperties(XmHTMLWidget html,String attributes,TableProperties * parent,Alignment halign,Pixel bg)2085 tableCheckProperties(XmHTMLWidget html, String attributes,
2086 	TableProperties *parent, Alignment halign, Pixel bg)
2087 {
2088 	TableProperties prop;
2089 	static TableProperties *prop_ret;
2090 	String chPtr;
2091 
2092 	prop_ret = (TableProperties*)malloc(sizeof(TableProperties));
2093 	memset(prop_ret, 0, sizeof(TableProperties));
2094 
2095 	if(parent)
2096 		memcpy(&prop, parent, sizeof(TableProperties));
2097 	else
2098 	{
2099 		/* defaults assume a table without any borders */
2100 		prop.border   = -1;
2101 		prop.halign   = halign;		 		/* unused */
2102 		prop.valign   = XmVALIGN_TOP;		/* contents on top */
2103 		prop.bg       = bg;					/* propagate */
2104 		prop.bg_image = NULL;
2105 		prop.framing  = TFRAME_VOID;		/* no border */
2106 		prop.ruling   = TRULE_NONE;			/* no ruling */
2107 		prop.nowrap   = False;				/* wrap long lines */
2108 	}
2109 
2110 	/* Check for possible attributes */
2111 	if(attributes)
2112 	{
2113 		/*****
2114 		* Horizontal alignment is only inherited through the halign argument:
2115 		* the align attribute on the table tag applies to the table in a whole,
2116 		* not to any of it's members.
2117 		*****/
2118 		prop_ret->halign   = _XmHTMLGetHorizontalAlignment(attributes, halign);
2119 		prop_ret->valign   = _XmHTMLGetVerticalAlignment(attributes,
2120 								prop.valign);
2121 		prop_ret->nowrap   = _XmHTMLTagCheck(attributes, "nowrap");
2122 
2123 		/*****
2124 		* Border value. If -1 is returned, check for the presence of the word
2125 		* ``border'' in the attributes. If it exists, we assume a non-zero
2126 		* border width.
2127 		*****/
2128 		prop_ret->border   = _XmHTMLTagGetNumber(attributes, "border",
2129 								prop.border);
2130 		/* if the word ``border'' is present, use default border width */
2131 		if(prop_ret->border == -1 && _XmHTMLTagCheck(attributes, "border"))
2132 			prop_ret->border = XmHTML_DEFAULT_TABLE_BORDERWIDTH;
2133 
2134 		/*****
2135 		* Framing applies per-table. If border is non-zero, the default is
2136 		* to render a fully framed table.
2137 		* If a TFRAME_VOID is returned, discard the border width.
2138 		*****/
2139 		prop_ret->framing  = _XmHTMLGetFraming(attributes,
2140 							prop_ret->border > 0 ? TFRAME_BOX : prop.framing);
2141 		if(prop_ret->framing == TFRAME_VOID)
2142 			prop_ret->border = 0;
2143 
2144 		/*****
2145 		* Ruling applies per-cell. If border is non-zero, the default is to
2146 		* render full borders around this cell.
2147 		* If a TRULE_NONE is returned, discard the border width.
2148 		*****/
2149 		prop_ret->ruling   = _XmHTMLGetRuling(attributes,
2150 							prop_ret->border ? TRULE_ALL : prop.ruling);
2151 
2152 		if(prop_ret->ruling == TRULE_NONE)
2153 			prop_ret->border = 0;
2154 
2155 		/*****
2156 		* only pick up background color if we are allowed to honor this attrib
2157 		*****/
2158 		if(HTML_ATTR(allow_color_switching) &&
2159 			(chPtr = _XmHTMLTagGetValue(attributes, "bgcolor")) != NULL)
2160 		{
2161 			Boolean doit = True;
2162 			if(HTML_ATTR(strict_checking))
2163 				doit = _XmHTMLConfirmColor32(chPtr);
2164 			if(doit)
2165 				prop_ret->bg = _XmHTMLGetPixelByName(html, chPtr, prop.bg);
2166 			free(chPtr);
2167 		}
2168 		else
2169 			prop_ret->bg = prop.bg;
2170 
2171 		/* table background image? */
2172 		if((chPtr = _XmHTMLTagGetValue(attributes, "background")))
2173 		{
2174 			String buf;
2175 			Dimension width, height;
2176 			XmHTMLImage *image;
2177 
2178 			/* kludge so _XmHTMLNewImage recognizes it */
2179 			buf = malloc(strlen(chPtr)+7);
2180 			sprintf(buf, "src=\"%s\"", chPtr);
2181 
2182 			/* load it */
2183 			if((image = _XmHTMLNewImage(html, buf, &width, &height)) != NULL)
2184 			{
2185 				/* animations are not allowed as background images */
2186 				if(ImageIsAnim(image))
2187 					image = NULL;
2188 				/* and we sure won't have the default image as background */
2189 				else if(ImageIsInternal(image))
2190 					image = NULL;
2191 			}
2192 			prop_ret->bg_image = image;
2193 			free(buf);
2194 			free(chPtr);
2195 		}
2196 		else
2197 			prop_ret->bg_image = prop.bg_image;
2198 	}
2199 	else
2200 	{
2201 		/* nop, no attributes, inherit from parent */
2202 		prop_ret->halign   = halign;
2203 		prop_ret->valign   = prop.valign;
2204 		prop_ret->nowrap   = False;
2205 		prop_ret->border   = prop.border;
2206 		prop_ret->bg       = prop.bg;
2207 		prop_ret->bg_image = prop.bg_image;
2208 		if((prop_ret->framing = prop_ret->border > 0 ?
2209 			TFRAME_BOX : prop.framing) == TFRAME_VOID)
2210 			prop_ret->border = 0;
2211 		if((prop_ret->ruling = prop_ret->border ?
2212 			TRULE_ALL : prop.ruling) == TRULE_NONE)
2213 			prop_ret->border = 0;
2214 	}
2215 	return(prop_ret);
2216 }
2217 
2218 /*****
2219 * Name:			tableOpen
2220 * Return Type: 	XmHTMLTable
2221 * Description: 	opens a new table.
2222 * In:
2223 *	html;		XmHTMLWidget id;
2224 *	parent:		parent table. Storage for nested tables is always allocated
2225 *				in this table. It is NULL when a truely new table is needed.
2226 *	start:		start of objects contained in this table.
2227 *	obj:		XmHTMLObject starting this table.
2228 * Returns:
2229 *	a newly opened table.
2230 *****/
2231 static XmHTMLTable*
tableOpen(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Alignment * halign,Pixel * bg)2232 tableOpen(XmHTMLWidget html, XmHTMLTable *parent,
2233 	XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
2234 	Pixel *bg)
2235 {
2236 	static XmHTMLTable *table;
2237 	XmHTMLTable *parent_table;
2238 	XmHTMLObject *tmp;
2239 	int nrows = 0;				/* no of rows in table				*/
2240 	int depth = 0;
2241 	int nchilds = 0;			/* no of table childs in this table */
2242 	Alignment caption_position = XmVALIGN_TOP; /* where is the caption?	*/
2243 	Boolean have_caption = False;
2244 
2245 	if(parent)
2246 	{
2247 		/* get to the absolute parent of this table */
2248 		parent_table = parent;
2249 		while(parent_table->parent != NULL)
2250 			parent_table = parent_table->parent;
2251 
2252 		/* get correct ptr */
2253 		parent_table = &(parent_table->childs[0]);
2254 
2255 		/* sanity check */
2256 		if(parent_table->lastchild+1 == parent_table->nchilds)
2257 		{
2258 			_XmHTMLError(__WFUNC__(html, "tableOpen"),
2259 				"Bad table count!!!");
2260 		}
2261 		/* get next available table */
2262 		parent_table->lastchild++;
2263 		table = &(parent_table->childs[parent_table->lastchild]);
2264 	}
2265 	else
2266 	{
2267 		table = (XmHTMLTable*)calloc(1, sizeof(XmHTMLTable));
2268 	}
2269 
2270 	/*****
2271 	* Get table attributes.
2272 	* If attributes are present, set all default values to zero.
2273 	* If no attributes are present, use the default values specified in
2274 	* include/common/XmHTMLconf.h
2275 	*****/
2276 	if(obj->attributes)
2277 	{
2278 		table->width    = _XmHTMLTagCheckNumber(obj->attributes, "width", 0);
2279 		table->hmargin  = _XmHTMLTagGetNumber(obj->attributes, "cellspacing",
2280 							0);
2281 		table->hpadding = _XmHTMLTagGetNumber(obj->attributes, "cellpadding",
2282 							0);
2283 		table->ncols    = _XmHTMLTagGetNumber(obj->attributes, "cols", 0);
2284 
2285 		/* no negative margin, padding or columns */
2286 		if(table->hmargin < 0)
2287 			table->hmargin = 0;
2288 		if(table->hpadding < 0)
2289 			table->hpadding = 0;
2290 		if(table->ncols < 0)
2291 			table->ncols = 0;
2292 
2293 		/*****
2294 		* Rowspacing and rowpadding are non-standard attributes. Use
2295 		* them if they're present, use cell margins otherwise.
2296 		*****/
2297 		if(_XmHTMLTagCheck(obj->attributes, "rowspacing"))
2298 			table->vmargin  = _XmHTMLTagGetNumber(obj->attributes,
2299 								"rowspacing", 0);
2300 		else
2301 			table->vmargin  = table->hmargin;
2302 		if(_XmHTMLTagCheck(obj->attributes, "rowpadding"))
2303 			table->vpadding = _XmHTMLTagGetNumber(obj->attributes,
2304 								"rowpadding", 0);
2305 		else
2306 			table->vpadding = table->hpadding;
2307 	}
2308 	else
2309 	{
2310 		table->width    = 0;
2311 		table->hmargin  = XmHTML_DEFAULT_CELLSPACING;
2312 		table->vmargin  = XmHTML_DEFAULT_ROWSPACING;
2313 		table->hpadding = XmHTML_DEFAULT_CELLPADDING;
2314 		table->vpadding = XmHTML_DEFAULT_ROWPADDING;
2315 		table->ncols    = 0;
2316 	}
2317 	table->start    = start;	/* starting object */
2318 	table->owner    = start;	/* owning object */
2319 	table->parent	= NULL;		/* parent table */
2320 
2321 	/* Nested tables do not inherit their parent table properties */
2322 	table->props = tableCheckProperties(html, obj->attributes,
2323 		NULL, *halign, *bg);
2324 
2325 	/* set return alignment */
2326 	*halign = table->props->halign;
2327 
2328 	/* set return background */
2329 	*bg = table->props->bg;
2330 
2331 	/* count how many rows this table has */
2332 	for(tmp = obj->next; tmp != NULL; tmp = tmp->next)
2333 	{
2334 		/* check for end of table and child tables */
2335 		if(tmp->id == HT_TABLE)
2336 		{
2337 			if(tmp->is_end)
2338 			{
2339 				if(depth == 0)
2340 					break;
2341 				else
2342 					depth--;
2343 			}
2344 			else	/* new table opens */
2345 			{
2346 				depth++;
2347 				nchilds++;
2348 			}
2349 		}
2350 		/*****
2351 		* only count a row when it belongs to the top-level table.
2352 		* A caption is considered a special row that spans the entire table
2353 		* and has only one cell: the row itself.
2354 		*****/
2355 		if((tmp->id == HT_TR || tmp->id == HT_CAPTION) && depth == 0 &&
2356 			!tmp->is_end)
2357 		{
2358 			if(tmp->id == HT_CAPTION)
2359 			{
2360 				/*****
2361 				* see where the caption should be inserted: as the first
2362 				* or last row for this table.
2363 				*****/
2364 				String chPtr;
2365 				if(tmp->attributes == NULL ||(chPtr =
2366 					_XmHTMLTagGetValue(tmp->attributes, "align")) == NULL)
2367 					caption_position = XmVALIGN_TOP;
2368 				else
2369 				{
2370 					if(!(strcasecmp(chPtr, "bottom")))
2371 						caption_position = XmVALIGN_BOTTOM;
2372 					else
2373 						caption_position = XmVALIGN_TOP;
2374 					free(chPtr);
2375 				}
2376 				have_caption = True;
2377 			}
2378 			nrows++;
2379 		}
2380 	}
2381 	/* sanity, should never happen */
2382 	if(!nrows)
2383 	{
2384 		_XmHTMLWarning(__WFUNC__(html, "tableOpen"),
2385 			"Got an empty table: no rows found");
2386 		free(table->props);
2387 		free(table);
2388 		return(NULL);
2389 	}
2390 
2391 	/* allocate all rows for this table */
2392 	table->rows = (TableRow*)calloc(nrows, sizeof(TableRow));
2393 	table->nrows = nrows;
2394 	table->lastrow = 0;
2395 
2396 	/* set caption ptr. */
2397 	if(have_caption)
2398 	{
2399 		if(caption_position == XmVALIGN_TOP)
2400 		{
2401 			table->caption = &(table->rows[0]);
2402 			table->lastrow = 1;
2403 		}
2404 		else
2405 			table->caption = &(table->rows[nrows-1]);
2406 	}
2407 
2408 	/* The master table contains all tables */
2409 	if(parent == NULL)
2410 	{
2411 		nchilds++;
2412 		table->childs = (XmHTMLTable*)calloc(nchilds, sizeof(XmHTMLTable));
2413 		table->nchilds = nchilds;
2414 		table->childs[0] = *table;		/* we are the first table */
2415 		table->lastchild = 0;
2416 	}
2417 	else
2418 	{
2419 		table->childs = (XmHTMLTable*)NULL;
2420 		table->nchilds = 0;
2421 		table->lastchild = 0;
2422 		/* set parent table */
2423 		table->parent = parent;
2424 	}
2425 
2426 	/* and set as table in the element given to us */
2427 	start->table = table;
2428 
2429 	return(table);
2430 }
2431 
2432 /*****
2433 * Name:			tableClose
2434 * Return Type: 	int
2435 * Description: 	performs required table wrapup actions
2436 * In:
2437 *	html:		XmHTMLWidget id;
2438 *	parent:		parent table;
2439 *	end:		last object to be used by this table;
2440 * Returns:
2441 *	-1 when this was the last table to be closed, a 0 or a positive integer
2442 *	when there are still tables open.
2443 *****/
2444 static XmHTMLTable*
tableClose(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2445 tableClose(XmHTMLWidget html, XmHTMLTable *parent,
2446 	XmHTMLObjectTableElement end)
2447 {
2448 	/* current table */
2449 	XmHTMLTable *real_table, *table = parent;
2450 	int i, ncols = 0;
2451 
2452 	/* sanity */
2453 	if(parent == NULL)
2454 		return(NULL);
2455 
2456 #if 0
2457 	/* bad hack */
2458 	real_table = parent->owner->table;
2459 	real_table->start = parent->owner->next;
2460 	real_table->end   = end;
2461 #endif
2462 
2463 	/* pick up correct ptr */
2464 	if(table->parent == NULL)
2465 		table = &(parent->childs[0]);
2466 
2467 	/* deal with empty tables */
2468 #if 0
2469 	table->start = table->start->next ? table->start->next : end;
2470 #endif
2471 	table->start = table->start->next ? table->start : end;
2472 	table->end   = end;
2473 
2474 	/*****
2475 	* Sanity Check: check all cells in search of a rowspan attribute. If
2476 	* we detect a rowspan in the *last* cell of a row, we must add a bogus
2477 	* cell to this row. If we don't do this, any cells falling in this row
2478 	* will be skipped, causing text to disappear (at the least, in the worst
2479 	* case it will cause a crash).
2480 	*****/
2481 
2482 	/* See how many columns we have (if not already set by the COLS attr.) */
2483 	for(i = 0; i < table->nrows; i++)
2484 	{
2485 		if(ncols < table->rows[i].ncells)
2486 			ncols = table->rows[i].ncells;
2487 	}
2488 	if(ncols > table->ncols)
2489 		table->ncols = ncols;
2490 
2491 	/* move to current table */
2492 	return(table->parent);
2493 }
2494 
2495 /*****
2496 * Name:			tableOpenCaption
2497 * Return Type: 	void
2498 * Description: 	adds a caption to the given table.
2499 * In:
2500 *	html:		XmHTMLWidget id;
2501 *	parent:		parent table;
2502 *	start:		start of objects contained in this caption.
2503 *	obj:		XmHTMLObject starting this caption.
2504 * Returns:
2505 *	nothing, but upon return the current table will have a caption.
2506 *****/
2507 static void
tableOpenCaption(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Pixel * bg)2508 tableOpenCaption(XmHTMLWidget html, XmHTMLTable *parent,
2509 	XmHTMLObjectTableElement start, XmHTMLObject *obj,
2510 	Pixel *bg)
2511 {
2512 	XmHTMLTable *table = parent;
2513 	TableRow *caption;
2514 	TableCell *cell;
2515 
2516 	/* sanity */
2517 	if(parent == NULL)
2518 		return;
2519 
2520 	if(table->parent == NULL)
2521 		table = &(parent->childs[0]);
2522 
2523 	/* get caption */
2524 	caption = table->caption;
2525 
2526 	/* only one caption allowed */
2527 	if(caption->lastcell)
2528 		return;
2529 
2530 	/*****
2531 	* Get properties for this caption.
2532 	* The global table properties are *never* propagated since, officially,
2533 	* a Caption doesn't have any attributes...
2534 	*****/
2535 	caption->props = tableCheckProperties(html, obj->attributes,
2536 						NULL, HTML_ATTR(default_halign), *bg);
2537 
2538 	/* starting object */
2539 	caption->start = start;
2540 	caption->owner = start;
2541 
2542 	/* set parent table */
2543 	caption->parent = table;
2544 
2545 	/* one cell: the caption itself */
2546 	caption->cells = (TableCell*)calloc(1, sizeof(TableCell));
2547 	caption->ncells   = 1;
2548 	caption->lastcell = 1;
2549 
2550 	/* fill in used fields */
2551 	cell = &(caption->cells[0]);
2552 
2553 	cell->header = False;	/* unused */
2554 	cell->width = 0;		/* unused */
2555 	cell->height = 0;		/* unused */
2556 	cell->rowspan = 1;		/* unused */
2557 	cell->colspan = 0;		/* spans the full table width */
2558 
2559 	/* get properties for this cell */
2560 	cell->props = tableCheckProperties(html, obj->attributes,
2561 						NULL, caption->props->halign,
2562 						caption->props->bg);
2563 	/* set return background */
2564 	*bg = caption->props->bg;
2565 
2566 	/* starting object */
2567 	cell->start = start;
2568 	cell->owner = start;
2569 
2570 	/* ending object unknown */
2571 	cell->end = NULL;
2572 
2573 	/* set parent caption */
2574 	cell->parent = caption;
2575 
2576 	/* all done */
2577 }
2578 
2579 /*****
2580 * Name:			tableCloseCaption
2581 * Return Type: 	int
2582 * Description: 	performs required caption wrapup actions
2583 * In:
2584 *	html:		XmHTMLWidget id;
2585 *	parent:		parent table;
2586 *	end:		last object to be used by this caption;
2587 * Returns:
2588 *	nothing.
2589 *****/
2590 static void
tableCloseCaption(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2591 tableCloseCaption(XmHTMLWidget html, XmHTMLTable *parent,
2592 	XmHTMLObjectTableElement end)
2593 {
2594 	XmHTMLTable *table = parent;
2595 	TableRow *caption;
2596 	TableCell *cell;
2597 
2598 	/* sanity */
2599 	if(parent == NULL)
2600 		return;
2601 
2602 	if(table->parent == NULL)
2603 		table = &(parent->childs[0]);
2604 
2605 	caption = table->caption;
2606 
2607 	/* sanity */
2608 	if(caption->ncells == 0)
2609 		return;
2610 
2611 	cell = &(caption->cells[0]);
2612 
2613 	/* deal with empty cells */
2614 	cell->start = cell->start->next ? cell->start->next : end;
2615 	cell->end   = end;
2616 }
2617 
2618 /*****
2619 * Name:			tableOpenRow
2620 * Return Type: 	void
2621 * Description: 	adds a row to the current table.
2622 * In:
2623 *	html:		XmHTMLWidget id;
2624 *	parent:		parent table;
2625 *	start:		start of objects contained in this row.
2626 *	obj:		XmHTMLObject starting this row, used to compute no of cells
2627 *				in a row.
2628 * Returns:
2629 *	Nothing, but a new row has been prepared in the current table.
2630 *****/
2631 static void
tableOpenRow(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Alignment * halign,Pixel * bg)2632 tableOpenRow(XmHTMLWidget html, XmHTMLTable *parent,
2633 	XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
2634 	Pixel *bg)
2635 {
2636 	XmHTMLTable *table = parent;
2637 	TableRow *row;
2638 	XmHTMLObject *tmp;
2639 	int ncells = 0;
2640 	int depth  = 0;
2641 
2642 	/* sanity */
2643 	if(parent == NULL)
2644 		return;
2645 
2646 	if(table->parent == NULL)
2647 		table = &(parent->childs[0]);
2648 
2649 	/* sanity */
2650 	if(table->lastrow == table->nrows)
2651 	{
2652 		_XmHTMLError(__WFUNC__(html, "tableRowOpen"),
2653 			"Bad tablerow count!!!");
2654 	}
2655 
2656 	/* get next available row in this table */
2657 	row = &(table->rows[table->lastrow]);
2658 
2659 	/* get properties for this row */
2660 	row->props = tableCheckProperties(html, obj->attributes,
2661 						table->props, HTML_ATTR(default_halign), *bg);
2662 	/* set return alignment */
2663 	*halign = row->props->halign;
2664 
2665 	/* set return background */
2666 	*bg = row->props->bg;
2667 
2668 	/* starting object */
2669 	row->start = start;
2670 	row->owner = start;
2671 
2672 	/* set parent table */
2673 	row->parent = table;
2674 
2675 	/* count how many cells this row has */
2676 	for(tmp = obj->next; tmp != NULL; tmp = tmp->next)
2677 	{
2678 		/* check for end of row and child rows (in child tables) */
2679 		if(tmp->id == HT_TR)
2680 		{
2681 			if(tmp->is_end)
2682 			{
2683 				if(depth == 0)
2684 					break;
2685 				else
2686 					depth--;
2687 			}
2688 			else	/* new row opens */
2689 				depth++;
2690 		}
2691 		/* only count a cell when it belongs to the top-level row */
2692 		if((tmp->id == HT_TH || tmp->id == HT_TD) && depth == 0 && !tmp->is_end)
2693 			ncells++;
2694 	}
2695 	/* empty rows don't have cells */
2696 	if(ncells)
2697 		row->cells = (TableCell*)calloc(ncells, sizeof(TableCell));
2698 	else	/* allocate an empty cell */
2699 		row->cells = (TableCell*)calloc(1, sizeof(TableCell));
2700 	row->ncells   = ncells;
2701 	row->lastcell = 0;
2702 
2703 	/* move to next available row */
2704 	table->lastrow++;
2705 }
2706 
2707 /*****
2708 * Name:			tableCloseRow
2709 * Return Type: 	int
2710 * Description: 	performs required row wrapup actions
2711 * In:
2712 *	html:		XmHTMLWidget id;
2713 *	parent:		parent table;
2714 *	end:		last object to be used by this row;
2715 * Returns:
2716 *	nothing.
2717 *****/
2718 static void
tableCloseRow(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2719 tableCloseRow(XmHTMLWidget html, XmHTMLTable *parent,
2720 	XmHTMLObjectTableElement end)
2721 {
2722 	XmHTMLTable *table = parent;
2723 	TableRow *row;
2724 	int i, ncols = 0;
2725 
2726 	/* sanity */
2727 	if(parent == NULL)
2728 		return;
2729 
2730 	if(table->parent == NULL)
2731 		table = &(parent->childs[0]);
2732 
2733 	/* sanity */
2734 	if(table->lastrow == 0)
2735 	{
2736 		_XmHTMLWarning(__WFUNC__(html, "tableCloseRow"),
2737 			"Internal Error: zero row count in table");
2738 		row = &(table->rows[table->lastrow]);
2739 	}
2740 	else /* get current row in this table */
2741 		row = &(table->rows[table->lastrow-1]);
2742 
2743 	/*****
2744 	* Count how many columns this row has (including cells spanning multiple
2745 	* columns).
2746 	*****/
2747 	for(i = 0; i < row->ncells; i++)
2748 		ncols += row->cells[i].colspan;
2749 
2750 	if(ncols > table->ncols)
2751 		table->ncols = ncols;
2752 
2753 	/*****
2754 	* Empty rows (<tr> followed by another <tr> and only whitespace between
2755 	* them) will loose their starting point as start->next will still be
2756 	* empty: end has not yet been inserted in the ObjectTable.
2757 	*****/
2758 	row->start = (row->start->next ? row->start->next : end);
2759 	row->end   = end;
2760 
2761 #ifdef DEBUG
2762 	if(row->start == NULL)
2763 	{
2764 		_XmHTMLWarning(__WFUNC__(html, "tableCloseRow"), "Row %i lost"
2765 			"it's starting point. (near line %i in input)\n",
2766 			table->lastrow - 1, row->owner->object->line);
2767 	}
2768 #endif
2769 }
2770 
2771 /*****
2772 * Name:			tableOpenCell
2773 * Return Type: 	void
2774 * Description: 	adds a cell to the current row in the current table.
2775 * In:
2776 *	html:		XmHTMLWidget id;
2777 *	parent:		parent table;
2778 *	start:		start of objects contained in this cell;
2779 *	obj:		XmHTMLObject starting this cell;
2780 * Returns:
2781 *	Nothing, but a new cell has been prepared in the current row.
2782 *****/
2783 static void
tableOpenCell(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Alignment * halign,Pixel * bg)2784 tableOpenCell(XmHTMLWidget html, XmHTMLTable *parent,
2785 	XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
2786 	Pixel *bg)
2787 {
2788 	XmHTMLTable *table = parent;
2789 	TableRow *row;
2790 	TableCell *cell;
2791 
2792 	/* sanity */
2793 	if(parent == NULL)
2794 		return;
2795 
2796 	if(table->parent == NULL)
2797 		table = &(parent->childs[0]);
2798 
2799 	/* more sanity */
2800 	if(table->lastrow == 0)
2801 	{
2802 		_XmHTMLWarning(__WFUNC__(html, "tableOpenCell"),
2803 			XMHTML_MSG_42, obj->line);
2804 		return;
2805 	}
2806 
2807 	/* get current row in this table */
2808 	row = &(table->rows[table->lastrow-1]);
2809 
2810 	/* sanity */
2811 	if(row->lastcell == row->ncells)
2812 	{
2813 		_XmHTMLError(__WFUNC__(html, "tableCellOpen"),
2814 			"Internal Error: Bad tablerow cell count!!!");
2815 	}
2816 
2817 	/* get next available cell in this row */
2818 	cell = &(row->cells[row->lastcell]);
2819 
2820 	/* get cell-specific properties */
2821 	cell->header = (obj->id == HT_TH ? True : False);
2822 	if(obj->attributes)
2823 	{
2824 		cell->width   = _XmHTMLTagCheckNumber(obj->attributes, "width", 0);
2825 		cell->height  = _XmHTMLTagCheckNumber(obj->attributes, "height", 0);
2826 		cell->rowspan = _XmHTMLTagGetNumber(obj->attributes, "rowspan", 1);
2827 		cell->colspan = _XmHTMLTagGetNumber(obj->attributes, "colspan", 1);
2828 	}
2829 	else
2830 	{
2831 		cell->width   = 0;
2832 		cell->height  = 0;
2833 		cell->rowspan = 1;
2834 		cell->colspan = 1;
2835 	}
2836 
2837 	/* [row/cell]span = 0 : span entire table in requested direction */
2838 	if(cell->rowspan <= 0 || cell->rowspan > table->nrows)
2839 		cell->rowspan = table->nrows;
2840 
2841 	/*****
2842 	* colspan <= 0 gets handled in SetTable when we now how many columns
2843 	* this table has
2844 	*****/
2845 
2846 	/* get global properties for this cell */
2847 	cell->props = tableCheckProperties(html, obj->attributes,
2848 						row->props, row->props->halign, *bg);
2849 	/* set return alignment */
2850 	*halign = cell->props->halign;
2851 
2852 	/* set return background */
2853 	*bg = cell->props->bg;
2854 
2855 	/* starting object */
2856 	cell->start = start;
2857 	cell->owner = start;
2858 
2859 	/* set parent row */
2860 	cell->parent = row;
2861 
2862 	/* move to next available cell */
2863 	row->lastcell++;
2864 }
2865 
2866 /*****
2867 * Name:			tableCloseCell
2868 * Return Type: 	int
2869 * Description: 	performs required cell wrapup actions
2870 * In:
2871 *	html:		XmHTMLWidget id;
2872 *	parent:		parent table;
2873 *	end:		last object to be used by this cell;
2874 * Returns:
2875 *	nothing.
2876 *****/
2877 static void
tableCloseCell(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2878 tableCloseCell(XmHTMLWidget html, XmHTMLTable *parent,
2879 	XmHTMLObjectTableElement end)
2880 {
2881 	XmHTMLTable *table = parent;
2882 	TableRow *row;
2883 	TableCell *cell;
2884 
2885 	/* sanity */
2886 	if(parent == NULL)
2887 		return;
2888 
2889 	if(table->parent == NULL)
2890 		table = &(parent->childs[0]);
2891 
2892 	/* sanity */
2893 	if(table->lastrow == 0)
2894 	{
2895 		_XmHTMLWarning(__WFUNC__(html, "tableCloseCell"),
2896 			"Internal Error: zero row count in table");
2897 		row = &(table->rows[table->lastrow]);
2898 	}
2899 	else /* get current row in this table */
2900 		row = &(table->rows[table->lastrow-1]);
2901 
2902 	/* sanity */
2903 	if(row->lastcell == 0)
2904 	{
2905 		_XmHTMLWarning(__WFUNC__(html, "tableCloseCell"),
2906 			"Internal Error: zero row cell count (row %i in table)",
2907 			table->lastrow-1);
2908 		if((cell = &(row->cells[row->lastcell])) == NULL)
2909 			return;
2910 	}
2911 	else /* get current cell in this row */
2912 		cell = &(row->cells[row->lastcell-1]);
2913 
2914 	/*****
2915 	* Empty cells (<td> followed by another <td> and only whitespace between
2916 	* them) will loose their starting point as start->next will still be
2917 	* empty: end has not yet been inserted in the ObjectTable.
2918 	*****/
2919 	cell->start = (cell->start->next ? cell->start->next : end);
2920 	cell->end   = end;
2921 
2922 #ifdef DEBUG
2923 	if(cell->start == NULL)
2924 	{
2925 		_XmHTMLWarning(__WFUNC__(html, "tableCloseCell"), "Cell %i (row %i) "
2926 			"lost it's start point. (near line %i in input)\n",
2927 			row->lastcell - 1, table->lastrow - 1, row->owner->object->line);
2928 	}
2929 #endif
2930 }
2931 
2932 #define PUSH_COLOR(WIDGET) { \
2933 	char *chPtr; \
2934 	/* check for color spec */ \
2935 	StackPush(fg_color_stack, fg); \
2936 	if(temp->attributes && \
2937 		(chPtr = _XmHTMLTagGetValue(temp->attributes, "color")) != NULL) \
2938 	{ \
2939 		Boolean doit = True; \
2940 		if(HTML_ATTR(strict_checking)) \
2941 			doit = _XmHTMLConfirmColor32(chPtr); \
2942 		if(doit) fg = _XmHTMLGetPixelByName(html, chPtr, fg); \
2943 		free(chPtr); \
2944 	} \
2945 }
2946 
2947 #define CHECK_LINE { \
2948 	if(element_data & ELE_ANCHOR) { \
2949 		if(element_data & ELE_ANCHOR_TARGET) \
2950 			line_data = HTML_ATTR(anchor_target_line); \
2951 		else if(element_data & ELE_ANCHOR_VISITED) \
2952 			line_data = HTML_ATTR(anchor_visited_line); \
2953 		else \
2954 			line_data = HTML_ATTR(anchor_line); \
2955 	} \
2956 	/* ignore <u> for anchors */ \
2957 	else { \
2958 		if(element_data & ELE_UNDERLINE) \
2959 			line_data  = LINE_SOLID | LINE_UNDER; \
2960 	} \
2961 	/* check strikeout flag */ \
2962 	if(element_data & ELE_STRIKEOUT) \
2963 		line_data |= LINE_STRIKE; \
2964 }
2965 
2966 /********
2967 ****** Private XmHTML Functions
2968 ********/
2969 
2970 /*****
2971 * Name: 		_XmHTMLNewAnchor
2972 * Return Type:	XmHTMLAnchor *
2973 * Description: 	allocates and fills an anchor object
2974 * In:
2975 *	html:		owning widget
2976 *	object:		raw anchor data
2977 * Returns:
2978 *	the allocated object
2979 * Note:
2980 *	this routine assumes that the <a> tag has attributes.
2981 *****/
2982 XmHTMLAnchor*
_XmHTMLNewAnchor(XmHTMLWidget html,XmHTMLObject * object)2983 _XmHTMLNewAnchor(XmHTMLWidget html, XmHTMLObject *object)
2984 {
2985 	static XmHTMLAnchor *anchor;
2986 
2987 	anchor = (XmHTMLAnchor*)malloc(sizeof(XmHTMLAnchor));
2988 
2989 	/* set all fields to zero */
2990 	(void)memset(anchor, 0, sizeof(XmHTMLAnchor));
2991 
2992 	/* anchors can be both named and href'd at the same time */
2993 	anchor->name = _XmHTMLTagGetValue(object->attributes, "name");
2994 
2995 	/* get the url specs */
2996 	parseHref(object->attributes, anchor);
2997 
2998 	/* get the url type */
2999 	anchor->url_type = XmHTMLGetURLType(anchor->href);
3000 
3001 	/* promote to named if necessary */
3002 	if(anchor->url_type == ANCHOR_UNKNOWN && anchor->name)
3003 		anchor->url_type = ANCHOR_NAMED;
3004 
3005 	/* see if we need to watch any events for this anchor */
3006 	if(object->attributes &&
3007 		(HTML_ATTR(event_proc) || HTML_ATTR(event_callback)))
3008 	{
3009 		/* pick up possible HTML4.0 events */
3010 		anchor->events = _XmHTMLCheckCoreEvents(html, object->attributes,
3011 			&anchor->event_mask);
3012 	}
3013 
3014 #ifdef PEDANTIC
3015 	if(anchor->url_type == ANCHOR_UNKNOWN)
3016 	{
3017 		_XmHTMLWarning(__WFUNC__(html, "_XmHTMLNewAnchor"),
3018 			XMHTML_MSG_43, object->attributes, object->line);
3019 	}
3020 #endif /* PEDANTIC */
3021 
3022 	/*
3023 	* If we have a proc available for anchor testing, call it and
3024 	* set the visited field.
3025 	*/
3026 	if(HTML_ATTR(anchor_visited_proc))
3027 		anchor->visited = HTML_ATTR(anchor_visited_proc)((Widget)html,
3028 				anchor->href, HTML_ATTR(client_data));
3029 
3030 	/* insert in the anchor list */
3031 	if(list_data.anchor_head)
3032 	{
3033 		/*****
3034 		* We can't do anything about duplicate anchors. Removing them
3035 		* would mess up the named anchor lookup table.
3036 		*****/
3037 		list_data.anchor_current->next = anchor;
3038 		list_data.anchor_current = anchor;
3039 	}
3040 	else
3041 	{
3042 		list_data.anchor_head = list_data.anchor_current = anchor;
3043 	}
3044 	return(anchor);
3045 }
3046 
3047 /*****
3048 * Name:			_XmHTMLformatObjects
3049 * Return Type:	XmHTMLObjectTable*
3050 * Description:	creates a list of formatted HTML objects.
3051 * In:
3052 *	old:		previous XmHTMLWidget id (contains all items to be freed);
3053 *	html:		current XmHTMLWidget id, containing raw (parser) html object
3054 *				data and will receive new formatted data.
3055 * Returns:
3056 *	nothing, but list of formatted objects, anchors, tables (and images)
3057 *	is updated upon return.
3058 *****/
3059 void
_XmHTMLformatObjects(XmHTMLWidget old,XmHTMLWidget html)3060 _XmHTMLformatObjects(XmHTMLWidget old, XmHTMLWidget html)
3061 {
3062 	/* text level variables */
3063 	String text;
3064 	int linefeed, n_words, anchor_words, named_anchors;
3065 	int x_offset = 0, y_offset = 0;
3066 	Byte text_data, line_data;		/* text and line data bits */
3067 	unsigned long element_data = 0;
3068 	XmHTMLWord *words;
3069 	XmHTMLAnchor *anchor_data, *form_anchor_data;
3070 	Boolean i18n = False;			/* multibyte input text flag */
3071 
3072 #ifdef DEBUG
3073 	int num_ignore = 0;
3074 	static String func = "_XmHTMLformatObjects";
3075 #endif
3076 
3077 	/* list variables */
3078 	int ul_level, ol_level, ident_level, current_list;
3079 	listStack list_stack[XmHTML_NESTED_LISTS_MAX];
3080 	int in_dt = 0;
3081 
3082 	/* remaining object variables */
3083 	Pixel fg, bg;
3084 	Dimension width, height;
3085 	Alignment halign, valign;
3086 	ObjectType object_type;
3087 	XmHTMLfont *font;
3088 #ifdef I18N
3089 	XmHTMLfont *i18nfont;
3090 #endif	/* I18N */
3091 
3092 	static XmHTMLObjectTableElement element;
3093 	int basefont;
3094 
3095 	/* imagemap and area stuff */
3096 	XmHTMLImageMap *imageMap = NULL;
3097 
3098 	/* local flags */
3099 	Boolean ignore = False, in_pre = False;
3100 
3101 	/* HTML tables */
3102 	XmHTMLTable *table = NULL;
3103 	XmHTMLTable *current_table = NULL;
3104 
3105 	/* misc. variables */
3106 	XmHTMLObject *temp;
3107 	int i, new_anchors = 0;
3108 	Boolean anchor_data_used = False;
3109 	ToolkitAbstraction *tka = HTML_ATTR(tka);
3110 	int fnheight;		/* current font height, for linefeeding */
3111 
3112 	/* stacks */
3113 	Stack align_stack, fg_color_stack, bg_color_stack, font_stack;
3114 
3115 #ifdef DEBUG
3116 	allocated = 0;
3117 #endif
3118 
3119 	/* Free any previous lists and initialize it */
3120 	InitObjectTable(ATTR_HTML(old, formatted), ATTR_HTML(old, anchor_data));
3121 	HTML_ATTR(formatted)   = (XmHTMLObjectTable*)NULL;
3122 	HTML_ATTR(anchor_data) = (XmHTMLAnchor*)NULL;
3123 
3124 	/* free table data */
3125 	freeTables(ATTR_HTML(old, tables));
3126 	HTML_ATTR(tables) = (XmHTMLTable*)NULL;
3127 
3128 	/*
3129 	* Nothing to do, just return. Should only happen if we get called
3130 	* from Destroy().
3131 	*/
3132 	if(HTML_ATTR(elements) == NULL)
3133 	{
3134 		/* free top of list */
3135 		if(list_data.head)
3136 			free(list_data.head);
3137 		list_data.head = NULL;
3138 		return;
3139 	}
3140 
3141 	/* Move to the body element */
3142 	for(temp = HTML_ATTR(elements); temp != NULL && temp->id != HT_BODY;
3143 		temp = temp->next);
3144 
3145 	/*
3146 	* No <body> element found. This is an error since the parser will
3147 	* *always* add a <body> element if none is present in the source
3148 	* document.
3149 	*/
3150 	if(temp == NULL)
3151 	{
3152 		/*****
3153 		* The only exception is a document only containing plain text and no
3154 		* BODY tag was present in the input. In this case, check the input and
3155 		* start outputting at the end of the first text element not belonging
3156 		* to the head of a document.
3157 		* Fix 01/04/98-01, kdh
3158 		*****/
3159 		XmHTMLObject *last_obj = NULL;
3160 
3161 		for(temp = HTML_ATTR(elements); temp != NULL; temp = temp->next)
3162 		{
3163 			switch(temp->id)
3164 			{
3165 				case HT_DOCTYPE:
3166 				case HT_BASE:
3167 					/* these two have no ending tag */
3168 					last_obj = temp;
3169 					break;
3170 				case HT_HTML:
3171 					/* don't use the closing tag for this, it's the end... */
3172 					if(!temp->is_end)
3173 						last_obj = temp;
3174 					break;
3175 				case HT_HEAD:
3176 				case HT_TITLE:
3177 				case HT_SCRIPT:
3178 				case HT_STYLE:
3179 				case HT_PAGE:
3180 					/* pick up the last closing tag */
3181 					if(temp->is_end)
3182 						last_obj = temp;
3183 				default:
3184 					break;
3185 			}
3186 		}
3187 
3188 		if(last_obj == NULL || last_obj->next == NULL)
3189 		{
3190 			_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_44);
3191 			return;
3192 		}
3193 		/* we move to the correct object a bit further down */
3194 		temp = last_obj;
3195 	}
3196 
3197 	/* initialize font stack */
3198 	font       = _XmHTMLSelectFontCache(html, False);
3199 	basefont   = 4;
3200 	font_stack = StackCreateDouble((void*)font, (void*)basefont, NULL, NULL);
3201 
3202 	/* Reset anchor count */
3203 	anchor_words = 0;
3204 	named_anchors = 0;
3205 	anchor_data = form_anchor_data = NULL;
3206 
3207 	/* initialize list variables */
3208 	ul_level = 0;
3209 	ol_level = 0;
3210 	ident_level = 0;
3211 	current_list = 0;
3212 
3213 	/* reset stacks */
3214 	for(i = 0; i < XmHTML_NESTED_LISTS_MAX; i++)
3215 	{
3216 		list_stack[i].isindex = False;
3217 		list_stack[i].marker  = XmMARKER_NONE;
3218 		list_stack[i].level   = 0;
3219 		list_stack[i].type    = HT_ZTEXT;
3220 	}
3221 
3222 	/* Initialize linefeeding mechanism */
3223 	text_data = TEXT_SPACE_NONE;
3224 	linefeed = CheckLineFeed(CLEAR_SOFT, True, &text_data);
3225 
3226 	/* Initialize alignment */
3227 	halign = HTML_ATTR(default_halign);
3228 	align_stack = StackCreate((void*)halign, NULL);
3229 	valign = XmVALIGN_NONE;
3230 	object_type = OBJ_NONE;
3231 
3232 	/* check for background stuff */
3233 	ParseBodyTags(html, temp);
3234 
3235 	/* foreground color to use */
3236 	fg = HTML_ATTR(body_fg);
3237 	/* background color to use */
3238 	bg = HTML_ATTR(body_bg);
3239 
3240 	/* Initialize color stacks */
3241 	fg_color_stack = StackCreate((void*)fg, NULL);
3242 	bg_color_stack = StackCreate((void*)bg, NULL);
3243 
3244 	/* move to the next element */
3245 	temp = temp->next;
3246 
3247 	/*
3248 	* Insert a dummy element at the head of the list to prevent incorrect
3249 	* handling of the first real element to be rendered.
3250 	* fix 12/14/97-01, kdh
3251 	*/
3252 	NewTableElement(element,temp);
3253 	element->object_type = OBJ_NONE;
3254 	element->font = HTML_ATTR(default_font);
3255 	InsertTableElement(element, False);
3256 	text_data = TEXT_SPACE_NONE;
3257 
3258 	/*
3259 	* Only elements between <BODY></BODY> elements are really interesting.
3260 	* BUT: if the HTML verification/reparation routines in parse.c should
3261 	* fail, we might have a premature </body> element, so we don't check on
3262 	* it but walk thru every item found.
3263 	*/
3264 	while(temp != HTML_ATTR(last_element))
3265 	{
3266 		/* create new element */
3267 		if(!ignore)
3268 			NewTableElement(element,temp);
3269 		else
3270 		{
3271 			/*****
3272 			* reuse current element if it wasn't needed on the previous
3273 			* pass.
3274 			* fix 11/12/97-01, kdh
3275 			*****/
3276 			(void)memset(element, 0, sizeof(XmHTMLObjectTable));
3277 			/* fill in appropriate fields */
3278 			element->object = temp;
3279 		}
3280 
3281 		element->prev = list_data.current;
3282 
3283 		/* Initialize all fields changed in here */
3284 		text = NULL;
3285 		ignore = False;
3286 		object_type = OBJ_NONE;
3287 		n_words = 0;
3288 		width = height = 0;
3289 		words = NULL;
3290 		linefeed = CLEAR_NONE;
3291 		line_data = NO_LINE;
3292 		fnheight = font->lineheight;
3293 
3294 		_XmHTMLDebug(2, ("format.c, _XmHTMLformatObjects, object data:\n"
3295 			"\telement id: %s\n\tattributes: %s\n\tis_end: %s\n",
3296 			html_tokens[temp->id],
3297 			temp->attributes ? temp->attributes : "<none>",
3298 			temp->is_end ? "Yes" : "No"));
3299 
3300 		switch(temp->id)
3301 		{
3302 			/* plain text */
3303 			case HT_ZTEXT:
3304 				object_type = OBJ_TEXT;
3305 				/*****
3306 				* CopyText
3307 				* We do not want escape expansion if we are loading a plain
3308 				* text document.
3309 				*****/
3310 				if((text = CopyText(html, temp->element, in_pre, &text_data,
3311 					HTML_ATTR(mime_id) != XmNONE, &i18n)) == NULL)
3312 				{
3313 					/*****
3314 					* named anchors can be empty, so keep them by inserting
3315 					* a dummy word, but only do that once (prevents a margin
3316 					* reset as well).
3317 					*****/
3318 					if((element_data & ELE_ANCHOR_INTERN) && !anchor_data_used)
3319 					{
3320 						words = MakeDummyWord(&height, font, line_data,
3321 									element);
3322 						n_words = 1;
3323 					}
3324 					else
3325 						ignore = True; /* ignore empty text fields */
3326 					break;
3327 				}
3328 				if(!in_pre)
3329 				{
3330 					CollapseWhiteSpace(text);
3331 					/*****
3332 					* If this turns out to be an empty block, ignore it,
3333 					* but only if it's not an empty named anchor.
3334 					*****/
3335 					if(strlen(text) == 0)
3336 					{
3337 						if((element_data & ELE_ANCHOR_INTERN) &&
3338 							!anchor_data_used)
3339 						{
3340 							object_type = OBJ_NONE;
3341 							words = MakeDummyWord(&height, font, line_data,
3342 										element);
3343 							n_words = 1;
3344 						}
3345 						else
3346 							ignore = True;
3347 						free(text);
3348 						break;
3349 					}
3350 					/* check line data */
3351 					CHECK_LINE;
3352 
3353 					/* convert text to a number of words */
3354 					words = TextToWords(text, &n_words, &height, font,
3355 						line_data, text_data, element, tka);
3356 
3357 					/* Plain text does a hard reset */
3358 					linefeed = CheckLineFeed(CLEAR_NONE, True, &text_data);
3359 				}
3360 				else
3361 				{
3362 					object_type = OBJ_PRE_TEXT;
3363 					/* check line data */
3364 					CHECK_LINE;
3365 					/* convert text to a number of words, keep formatting. */
3366 					words = TextToPre(text, &n_words, font, line_data, element,
3367 								tka, HTML_ATTR(tabwidth));
3368 
3369 					/* Preformatted text does a soft reset */
3370 					linefeed = CheckLineFeed(CLEAR_NONE, False, &text_data);
3371 				}
3372 				break;
3373 
3374 			/* images */
3375 			case HT_IMG:
3376 				/* release break bit */
3377 				text_data &= ~TEXT_BREAK;
3378 				text_data |= TEXT_IMAGE;
3379 				if((words = ImageToWord(html, temp->attributes, &n_words,
3380 					&height, element, in_pre, tka,
3381 					(Boolean)(anchor_data != NULL), text_data)) == NULL)
3382 				{
3383 					/* loose image bit */
3384 					text_data &= ~TEXT_IMAGE;
3385 					ignore = True;
3386 					break;
3387 				}
3388 				object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
3389 
3390 				/* No explicit returns for images, reset */
3391 				linefeed = CheckLineFeed(CLEAR_NONE, False, &text_data);
3392 
3393 				break;
3394 
3395 			/* anchors */
3396 			case HT_A:
3397 				if(temp->is_end)
3398 				{
3399 					/*
3400 					* this is a very sneaky hack: since empty named anchors
3401 					* are allowed, we must store it somehow. And this is how
3402 					* we do it: insert a dummy word (to prevent margin reset)
3403 					* and back up one element.
3404 					*/
3405 					if(!anchor_data_used && (element_data & ELE_ANCHOR_INTERN))
3406 					{
3407 						_XmHTMLDebug(2, ("format.c: _XmHTMLformatObjects, "
3408 							"adding bogus named anchor %s\n",
3409 							anchor_data->name));
3410 
3411 						/* insert a dummy word to prevent margin reset */
3412 						words = MakeDummyWord(&height, font, line_data,
3413 									element);
3414 						n_words = 1;
3415 						object_type = OBJ_TEXT;
3416 						anchor_data_used = True;
3417 						temp = temp->prev;
3418 						break;
3419 					}
3420 					/* unset anchor bitfields */
3421 					element_data &= ( ~ELE_ANCHOR & ~ELE_ANCHOR_TARGET &
3422 							~ELE_ANCHOR_VISITED & ~ELE_ANCHOR_INTERN);
3423 					fg = (Pixel)StackPop(fg_color_stack);
3424 
3425 					anchor_data = NULL;
3426 
3427 					ignore = True;	/* only need anchor data */
3428 					_XmHTMLDebug(2,("format.c: _XmHTMLformatObjects: anchor "
3429 						"end\n"));
3430 				}
3431 				else
3432 				{
3433 					/* allocate a new anchor */
3434 					anchor_data = (temp->attributes ?
3435 							_XmHTMLNewAnchor(html, temp) : NULL);
3436 
3437 					/* sanity check */
3438 					if(!anchor_data)
3439 					{
3440 						ignore = True;
3441 						break;
3442 					}
3443 					/* save current color */
3444 					StackPush(fg_color_stack, fg);
3445 
3446 					new_anchors++;
3447 					anchor_data_used = False;
3448 
3449 					/* set proper element bits */
3450 
3451 					/* maybe it's a named one */
3452 					if(anchor_data->name)
3453 						element_data |= ELE_ANCHOR_INTERN;
3454 
3455 					/*
3456 					* maybe it's also a plain anchor. If so, see what
3457 					* foreground color we have to use to render this
3458 					* anchor.
3459 					*/
3460 					if(anchor_data->href[0] != '\0')
3461 					{
3462 						element_data |= ELE_ANCHOR;
3463 						fg = HTML_ATTR(anchor_fg);
3464 
3465 						/* maybe it's been visited */
3466 						if(anchor_data->visited)
3467 						{
3468 							element_data |= ELE_ANCHOR_VISITED;
3469 							fg = HTML_ATTR(anchor_visited_fg);
3470 						}
3471 						/* maybe it's a target */
3472 						else if(anchor_data->target)
3473 						{
3474 							element_data |= ELE_ANCHOR_TARGET;
3475 							fg = HTML_ATTR(anchor_target_fg);
3476 						}
3477 					}
3478 					_XmHTMLDebug(2,("format.c: _XmHTMLformatObjects: anchor "
3479 						"start\n"));
3480 					ignore = True;	/* only need anchor data */
3481 				}
3482 				break;
3483 
3484 			/* font changes */
3485 			case HT_CITE:		/* italic */
3486 			case HT_I:			/* italic */
3487 			case HT_EM:			/* italic */
3488 			case HT_DFN:		/* italic */
3489 			case HT_STRONG:		/* bold */
3490 			case HT_B:			/* bold */
3491 			case HT_SAMP:		/* fixed width */
3492 			case HT_TT:			/* fixed width */
3493 			case HT_VAR:		/* fixed width */
3494 			case HT_CODE:		/* fixed width */
3495 			case HT_KBD:		/* fixed width */
3496 				if(temp->is_end)
3497 				{
3498 					if(HTML_ATTR(allow_color_switching))
3499 						fg = (Pixel)StackPop(fg_color_stack);
3500 					font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3501 				}
3502 				else
3503 				{
3504 					if(HTML_ATTR(allow_color_switching))
3505 						PUSH_COLOR(html);
3506 					StackPushDouble(font_stack, font, basefont);
3507 					font = _XmHTMLLoadFont(html, temp->id, basefont, font);
3508 
3509 					/* new height for linefeeds */
3510 					fnheight = font->lineheight;
3511 				}
3512 				ignore = True; /* only need font data */
3513 				break;
3514 
3515 			case HT_U:
3516 				if(temp->is_end) /* unset underline bitfields */
3517 					element_data &= (~ELE_UNDERLINE & ~ELE_UNDERLINE_TEXT);
3518 				else
3519 					element_data |= ELE_UNDERLINE;
3520 				ignore = True; /* only need underline data */
3521 				break;
3522 
3523 			case HT_STRIKE:
3524 				if(temp->is_end) /* unset strikeout bitfields */
3525 					element_data &= (~ELE_STRIKEOUT & ~ELE_STRIKEOUT_TEXT);
3526 				else
3527 					element_data |= ELE_STRIKEOUT;
3528 				ignore = True; /* only need strikeout data */
3529 				break;
3530 
3531 			case HT_BASEFONT:
3532 				{
3533 					basefont = temp->attributes ?
3534 						_XmHTMLTagGetNumber(temp->attributes, "size", 0) : 0;
3535 					/* take absolute value */
3536 					basefont = Abs(basefont);
3537 					if(basefont < 1 || basefont > 7)
3538 					{
3539 						if(HTML_ATTR(bad_html_warnings))
3540 							_XmHTMLWarning(__WFUNC__(html, func),
3541 								XMHTML_MSG_45, basefont, temp->line);
3542 						basefont = 4;
3543 					}
3544 				}
3545 				ignore = True;	/* only need font data */
3546 				break;
3547 
3548 			/*****
3549 			* <font> is a big performance hit. We always need to push & pop
3550 			* the font *even* if only the font color has been changed as we
3551 			* can't keep track of what has actually been changed.
3552 			*****/
3553 			case HT_FONT:
3554 				if(temp->is_end)
3555 				{
3556 					if(HTML_ATTR(allow_font_switching))
3557 						font =(XmHTMLfont*)StackPopDouble(font_stack, basefont);
3558 					if(HTML_ATTR(allow_color_switching))
3559 						fg = (Pixel)StackPop(fg_color_stack);
3560 				}
3561 				else
3562 				{
3563 					char *chPtr;
3564 					int size = xmhtml_basefont_sizes[basefont - 1];
3565 
3566 					if(HTML_ATTR(allow_color_switching))
3567 						PUSH_COLOR(html);
3568 
3569 					if(HTML_ATTR(allow_font_switching))
3570 						StackPushDouble(font_stack, font, basefont);
3571 					else
3572 						break;
3573 
3574 					/* can't use TagGetNumber: fontchange can be relative */
3575 					chPtr = temp->attributes ?
3576 						_XmHTMLTagGetValue(temp->attributes, "size") : NULL;
3577 					if(chPtr != NULL)
3578 					{
3579 						int f_inc = atoi(chPtr);
3580 
3581 						/* check wether size is relative or not */
3582 						if(chPtr[0] == '-' || chPtr[0] == '+')
3583 						{
3584 							f_inc = basefont + f_inc; /* + 1; */
3585 						}
3586 						/* sanity check */
3587 						if(f_inc < 1 || f_inc > 7)
3588 						{
3589 							if(f_inc < 1)
3590 								f_inc = 1;
3591 							else
3592 								f_inc = 7;
3593 						}
3594 
3595 						basefont = f_inc;
3596 						/* minus one: zero based array */
3597 						size = xmhtml_basefont_sizes[f_inc-1];
3598 						free(chPtr); /* fix 01/28/98-02, kdh */
3599 						chPtr = NULL;
3600 					}
3601 					/*****
3602 					* Font face changes only allowed when not in preformatted
3603 					* text.
3604 					* Only check when not being pedantic.
3605 					*****/
3606 #ifndef PEDANTIC
3607 					if(!in_pre)
3608 #endif
3609 						chPtr = temp->attributes ?
3610 							_XmHTMLTagGetValue(temp->attributes, "face") : NULL;
3611 
3612 					if(chPtr != NULL)
3613 					{
3614 #ifdef PEDANTIC
3615 						if(in_pre)
3616 						{
3617 							_XmHTMLWarning(__WFUNC__(html, func),
3618 								XMHTML_MSG_41, "FONT FACE=xxx",
3619 								html_tokens[HT_PRE], temp->line);
3620 							/*****
3621 							* Ignore face but must allow for size change.
3622 							* (Font stack will get unbalanced otherwise!)
3623 							*****/
3624 							font = _XmHTMLLoadFont(html, HT_FONT, size, font);
3625 						}
3626 						else
3627 #endif
3628 							font = _XmHTMLLoadFontWithFace(html, size, chPtr,
3629 										font);
3630 
3631 						free(chPtr);
3632 					}
3633 					else
3634 						font = _XmHTMLLoadFont(html, HT_FONT, size, font);
3635 				}
3636 				ignore = True; /* only need font data */
3637 				break;
3638 
3639 			case HT_BIG:
3640 				if(temp->is_end)
3641 					font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3642 				else /* multiple big elements are not honoured */
3643 				{
3644 					StackPushDouble(font_stack, font, basefont);
3645 					font = _XmHTMLLoadFont(html, HT_FONT,
3646 								xmhtml_basefont_sizes[4], font);
3647 				}
3648 				ignore = True; /* only need font data */
3649 				break;
3650 
3651 			case HT_SMALL:
3652 				if(temp->is_end)
3653 					font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3654 				else /* multiple small elements are not honoured */
3655 				{
3656 					StackPushDouble(font_stack, font, basefont);
3657 					font = _XmHTMLLoadFont(html, HT_FONT,
3658 								xmhtml_basefont_sizes[2], font);
3659 				}
3660 				ignore = True; /* only need font data */
3661 				break;
3662 
3663 			case HT_SUB:
3664 			case HT_SUP:
3665 				if(temp->is_end)
3666 				{
3667 					/* restore vertical offset */
3668 					y_offset = 0;
3669 					x_offset = 0;
3670 					font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3671 				}
3672 				else /* multiple small elements are not honoured */
3673 				{
3674 					StackPushDouble(font_stack, font, basefont);
3675 					font = _XmHTMLLoadFont(html, HT_FONT,
3676 								xmhtml_basefont_sizes[2], font);
3677 					y_offset = (temp->id == HT_SUB ?
3678 						font->sub_yoffset : font->sup_yoffset);
3679 					x_offset = (temp->id == HT_SUB ?
3680 						font->sub_xoffset : font->sup_xoffset);
3681 				}
3682 				ignore = True; /* only need font data */
3683 				break;
3684 
3685 			case HT_H1:
3686 			case HT_H2:
3687 			case HT_H3:
3688 			case HT_H4:
3689 			case HT_H5:
3690 			case HT_H6:
3691 				if(temp->is_end)
3692 				{
3693 					if(HTML_ATTR(allow_color_switching))
3694 						fg = (Pixel)StackPop(fg_color_stack);
3695 					halign = (Alignment)StackPop(align_stack);
3696 					font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3697 				}
3698 				else
3699 				{
3700 					/*****
3701 					* <H> elements are sometimes used as spacers. This gives
3702 					* *horrible* rendering. Look forward to the first text
3703 					* element and check if it contains text. If it does, allow
3704 					* it, else ignore this element.
3705 					*****/
3706 					XmHTMLObject *elePtr = temp->next;
3707 					for(; elePtr != NULL && elePtr->id != HT_ZTEXT &&
3708 						elePtr->id != temp->id; elePtr = elePtr->next);
3709 
3710 					/* this <h> only contains text, check it */
3711 					if(elePtr && elePtr->id == HT_ZTEXT &&
3712 						elePtr->next->id == temp->id)
3713 					{
3714 						String chPtr = elePtr->element;
3715 						/*
3716 						* found a text element. Check if it has valid
3717 						* content.
3718 						*/
3719 						while(*chPtr != '\0' && isspace(*chPtr))
3720 							chPtr++;
3721 						/* empty head. Ignore it */
3722 						if(*chPtr == '\0')
3723 						{
3724 							_XmHTMLDebug(2, ("format.c: _XmHTMLformatObjects; "
3725 								"empty element %s found at line %i in input, "
3726 								"ignoring.\n",
3727 								html_tokens[temp->id], temp->line));
3728 
3729 							/* set to closing <h> and ignore it */
3730 							temp = elePtr->next;
3731 							object_type = OBJ_NONE;
3732 							ignore = True;
3733 							break;
3734 						}
3735 					}
3736 					if(HTML_ATTR(allow_color_switching))
3737 						PUSH_COLOR(html);
3738 
3739 					StackPush(align_stack, halign);
3740 					halign = (temp->attributes ?
3741 						_XmHTMLGetHorizontalAlignment(temp->attributes, halign)
3742 						: halign);
3743 
3744 					StackPushDouble(font_stack, font, basefont);
3745 					font = _XmHTMLLoadFont(html, temp->id, basefont, font);
3746 
3747 					/* new height for linefeeds */
3748 					fnheight = font->lineheight;
3749 
3750 					/*****
3751 					* Need to update basefont size as well so font face changes
3752 					* changes *inside* these elements use the correct font
3753 					* size as well.
3754 					* The sizes used by the headers are in reverse order.
3755 					*****/
3756 					basefont = (int)(HT_H6 - temp->id) + 1;
3757 				}
3758 				linefeed = CheckLineFeed(CLEAR_HARD, False, &text_data);
3759 				object_type = OBJ_BLOCK;
3760 				break;
3761 
3762 			/* lists. The COMPACT tag is ignored */
3763 			case HT_UL:
3764 			case HT_DIR:
3765 			case HT_MENU:
3766 				if(temp->is_end)
3767 				{
3768 
3769 					ul_level--;
3770 					ident_level--;
3771 					if(ident_level < 0)
3772 					{
3773 						if(HTML_ATTR(bad_html_warnings))
3774 							_XmHTMLWarning(__WFUNC__(html, func),
3775 								XMHTML_MSG_46, temp->line);
3776 						ident_level = 0;
3777 					}
3778 					current_list = (ident_level ? ident_level - 1 : 0);
3779 				}
3780 				else
3781 				{
3782 					int mark_id;
3783 					/* avoid overflow of mark id array */
3784 					mark_id = ul_level % UL_ARRAYSIZE;
3785 
3786 					/* set default marker & list start */
3787 					list_stack[ident_level].marker = ul_markers[mark_id].type;
3788 					list_stack[ident_level].level = 0;
3789 					list_stack[ident_level].type = temp->id;
3790 
3791 					if(ident_level == XmHTML_NESTED_LISTS_MAX)
3792 					{
3793   						_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_47,
3794 							XmHTML_NESTED_LISTS_MAX, temp->line);
3795 						ident_level = XmHTML_NESTED_LISTS_MAX-1;
3796 					}
3797 					current_list = ident_level;
3798 
3799 					if(temp->id == HT_UL)
3800 					{
3801 						char *chPtr;
3802 						/* check if user specified a custom marker */
3803 						chPtr = temp->attributes ?
3804 							_XmHTMLTagGetValue(temp->attributes, "type") : NULL;
3805 						if(chPtr != NULL)
3806 						{
3807 							/*
3808 							* Walk thru the list of possible markers. If a
3809 							* match is found, store it so we can switch back
3810 							* to the correct marker once this list terminates.
3811 							*/
3812 							for(i = 0 ; i < UL_ARRAYSIZE; i++)
3813 							{
3814 								if(!(strcasecmp(ul_markers[i].name, chPtr)))
3815 								{
3816 									list_stack[ident_level].marker =
3817 										ul_markers[i].type;
3818 									break;
3819 								}
3820 							}
3821 							free(chPtr);
3822 						}
3823 					}
3824 					ul_level++;
3825 					ident_level++;
3826 				}
3827 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3828 				object_type = OBJ_BLOCK;
3829 				break;
3830 
3831 			case HT_OL:
3832 				if(temp->is_end)
3833 				{
3834 					/* must be reset properly, only possible for <ol> lists. */
3835 					list_stack[current_list].isindex = False;
3836 
3837 					ol_level--;
3838 					ident_level--;
3839 					if(ident_level < 0)
3840 					{
3841 						if(HTML_ATTR(bad_html_warnings))
3842 							_XmHTMLWarning(__WFUNC__(html, func),
3843 								XMHTML_MSG_46, temp->line);
3844 						ident_level = 0;
3845 					}
3846 					current_list = (ident_level ? ident_level - 1 : 0);
3847 				}
3848 				else
3849 				{
3850 					int mark_id;
3851 					char *chPtr;
3852 
3853 					/* avoid overflow of mark id array */
3854 					mark_id = ol_level % OL_ARRAYSIZE;
3855 
3856 					/* set default marker & list start */
3857 					list_stack[ident_level].marker = ol_markers[mark_id].type;
3858 					list_stack[ident_level].level = 0;
3859 					list_stack[ident_level].type = temp->id;
3860 
3861 					if(ident_level == XmHTML_NESTED_LISTS_MAX)
3862 					{
3863   						_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_47,
3864 							XmHTML_NESTED_LISTS_MAX, temp->line);
3865 						ident_level = XmHTML_NESTED_LISTS_MAX-1;
3866 					}
3867 					current_list = ident_level;
3868 
3869 					/* check if user specified a custom marker */
3870 					chPtr = temp->attributes ?
3871 							_XmHTMLTagGetValue(temp->attributes, "type") : NULL;
3872 					if(chPtr != NULL)
3873 					{
3874 						/*
3875 						* Walk thru the list of possible markers. If a
3876 						* match is found, store it so we can switch back
3877 						* to the correct marker once this list terminates.
3878 						*/
3879 						for(i = 0 ; i < OL_ARRAYSIZE; i++)
3880 						{
3881 							if(!(strcmp(ol_markers[i].name, chPtr)))
3882 							{
3883 								list_stack[ident_level].marker =
3884 									ol_markers[i].type;
3885 								break;
3886 							}
3887 						}
3888 						free(chPtr);
3889 					}
3890 
3891 					/* see if a start tag exists */
3892 					if(temp->attributes &&
3893 						_XmHTMLTagCheck(temp->attributes, "start"))
3894 					{
3895 						/* pick up a start spec */
3896 						list_stack[ident_level].level =
3897 							_XmHTMLTagGetNumber(temp->attributes, "start", 0);
3898 						list_stack[ident_level].level--;
3899 					}
3900 
3901 					/* see if we have to propage the current index number */
3902 					list_stack[ident_level].isindex = temp->attributes ?
3903 						_XmHTMLTagCheck(temp->attributes, "isindex") : False;
3904 					ol_level++;
3905 					ident_level++;
3906 				}
3907 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3908 				object_type = OBJ_BLOCK;
3909 				break;
3910 
3911 			case HT_LI:
3912 				if(temp->is_end)	/* optional termination */
3913 					object_type = OBJ_BLOCK;
3914 				else
3915 				{
3916 					char *chPtr;
3917 
3918 					/* increase list counter */
3919 					list_stack[current_list].level++;
3920 
3921 					/* check if user specified a custom marker */
3922 					chPtr = temp->attributes ?
3923 						_XmHTMLTagGetValue(temp->attributes, "type") : NULL;
3924 					if(chPtr != NULL)
3925 					{
3926 						/*
3927 						* depending on current list type, check and set
3928 						* the marker.
3929 						*/
3930 						if(list_stack[current_list].type == HT_OL)
3931 						{
3932 							for(i = 0 ; i < OL_ARRAYSIZE; i++)
3933 							{
3934 								if(!(strcmp(ol_markers[i].name, chPtr)))
3935 								{
3936 									list_stack[current_list].marker =
3937 										ol_markers[i].type;
3938 									break;
3939 								}
3940 							}
3941 						}
3942 						else if(list_stack[current_list].type == HT_UL)
3943 						{
3944 							for(i = 0 ; i < UL_ARRAYSIZE; i++)
3945 							{
3946 								if(!(strcmp(ul_markers[i].name, chPtr)))
3947 								{
3948 									list_stack[current_list].marker =
3949 										ul_markers[i].type;
3950 									break;
3951 								}
3952 							}
3953 						}
3954 						else	/* dir, menu, dt elements */
3955 							list_stack[current_list].marker = XmMARKER_NONE;
3956 						free(chPtr);
3957 					}
3958 					/* check if user specified a custom number for ol lists */
3959 					if(list_stack[current_list].type == HT_OL &&
3960 						temp->attributes &&
3961 						_XmHTMLTagCheck(temp->attributes, "value"))
3962 					{
3963 						list_stack[current_list].level =
3964 							_XmHTMLTagGetNumber(temp->attributes, "value", 0);
3965 					}
3966 					/*
3967 					* If the current list is an index, create a prefix for
3968 					* the current item
3969 					*/
3970 					if(list_stack[current_list].isindex)
3971 					{
3972 						words = indexToWord(html, list_stack, current_list,
3973 									element, in_pre);
3974 						n_words = 1;
3975 					}
3976 					object_type = OBJ_BULLET;
3977 				}
3978 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3979 				break;
3980 
3981 			case HT_DL:
3982 				if(temp->is_end)
3983 					ident_level--;
3984 				else
3985 					ident_level++;
3986 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3987 				object_type = OBJ_BLOCK;
3988 				break;
3989 
3990 			case HT_DT:
3991 				if(temp->is_end)
3992 					in_dt--;
3993 				else
3994 					in_dt++;
3995 			case HT_DD:
3996 				object_type = OBJ_BLOCK;
3997 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3998 				break;
3999 
4000 			/* block commands */
4001 			case HT_ADDRESS:
4002 				if(temp->is_end)
4003 				{
4004 					if(HTML_ATTR(allow_color_switching))
4005 						fg = (Pixel)StackPop(fg_color_stack);
4006 					font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
4007 				}
4008 				else
4009 				{
4010 					if(HTML_ATTR(allow_color_switching))
4011 						PUSH_COLOR(html);
4012 					StackPushDouble(font_stack, font, basefont);
4013 					font = _XmHTMLLoadFont(html, temp->id, basefont, font);
4014 
4015 					/* new height for linefeeds */
4016 					fnheight = font->lineheight;
4017 				}
4018 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4019 				object_type = OBJ_BLOCK;
4020 				break;
4021 
4022 			/* <BR> is a special word */
4023 			case HT_BR:
4024 #if 0
4025 				{
4026 					String chPtr;
4027 
4028 					if((chPtr = _XmHTMLTagGetValue(temp->attributes,
4029 						"clear")) != NULL && locase(chPtr[0]) != 'n')
4030 					{
4031 						/* all clear attribs but none reset the linefeeder */
4032 						(void)CheckLineFeed(CLEAR_HARD, True);
4033 						linefeed = CLEAR_ALL;
4034 
4035 						if(locase(chPtr[0]) == 'l')	/* clear = left */
4036 							halign = XmHALIGN_LEFT;
4037 						else if(locase(chPtr[0]) == 'r')	/* clear = right */
4038 							halign = XmHALIGN_RIGHT;
4039 						/* no default */
4040 
4041 						free(chPtr);
4042 					}
4043 					else /* fix 01/20/97-02, kdh */
4044 						linefeed = CheckLineFeed(CLEAR_SOFT, False);
4045 				}
4046 #endif
4047 				if((linefeed = CheckLineFeed(CLEAR_SOFT, False,
4048 					&text_data)) != CLEAR_NONE)
4049 				{
4050 					words = BreakToWord(&height, font, linefeed, element);
4051 					n_words = 1;
4052 					object_type = OBJ_TEXT;
4053 					text_data &= ~TEXT_SPACE_LEAD & ~TEXT_SPACE_TRAIL;
4054 					text_data |= TEXT_BREAK;	/* it's a linebreak */
4055 				}
4056 				else
4057 				{
4058 					/* linebreak follows a stronger linebreak, ignore */
4059 					object_type = OBJ_NONE;
4060 					ignore = True;
4061 				}
4062 				break;
4063 
4064 			case HT_TAB:
4065 				{
4066 					char *chPtr;
4067 
4068 					object_type = OBJ_TEXT;
4069 
4070 					element->len = 8; /* default tabsize */
4071 
4072 					/* see if we have a width spec */
4073 					chPtr = temp->attributes ?
4074 						_XmHTMLTagGetValue(temp->attributes, "size") : NULL;
4075 					if(chPtr != NULL)
4076 					{
4077 						element->len = atoi(chPtr);
4078 						free(chPtr);
4079 					}
4080 					n_words = 1;
4081 					words = SetTab(element->len, &height, font, element, tka);
4082 				}
4083 				break;
4084 
4085 			case HT_PRE:
4086 				if(temp->is_end)
4087 				{
4088 					if(HTML_ATTR(allow_color_switching))
4089 						fg = (Pixel)StackPop(fg_color_stack);
4090 					font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
4091 					in_pre = False;
4092 				}
4093 				else
4094 				{
4095 					if(HTML_ATTR(allow_color_switching))
4096 						PUSH_COLOR(html);
4097 					StackPushDouble(font_stack, font, basefont);
4098 					font = _XmHTMLLoadFont(html, temp->id, basefont, font);
4099 					in_pre = True;
4100 
4101 					/* new height for linefeeds */
4102 					fnheight = font->lineheight;
4103 				}
4104 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4105 				object_type = OBJ_BLOCK;
4106 				break;
4107 
4108 			case HT_BLOCKQUOTE:
4109 				if(temp->is_end)
4110 				{
4111 					ident_level--;
4112 					if(HTML_ATTR(allow_color_switching))
4113 						fg = (Pixel)StackPop(fg_color_stack);
4114 				}
4115 				else
4116 				{
4117 					ident_level++;
4118 					if(HTML_ATTR(allow_color_switching))
4119 						PUSH_COLOR(html);
4120 				}
4121 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4122 				object_type = OBJ_BLOCK;
4123 				break;
4124 
4125 			/*****
4126 			* <P> and <DIV> are equal all except for the amount of
4127 			* vertical whitespace added: <P> causes a hard linebreak whereas
4128 			* <DIV> causes a soft linebreak.
4129 			*****/
4130 			case HT_P:
4131 			case HT_DIV:
4132 				if(temp->is_end)
4133 				{
4134 					halign = (Alignment)StackPop(align_stack);
4135 					/*
4136 					* Paragraph ending also adds linespacing (natural flow
4137 					* of text between paragraphs).
4138 					*/
4139 					linefeed = CheckLineFeed(
4140 						(temp->id == HT_P ? CLEAR_HARD : CLEAR_SOFT), False,
4141 						&text_data);
4142 
4143 					/* do we have a color attrib? */
4144 					if(HTML_ATTR(allow_color_switching))
4145 						fg = (Pixel)StackPop(fg_color_stack);
4146 				}
4147 				else
4148 				{
4149 					StackPush(align_stack, halign);
4150 					halign = temp->attributes ?
4151 						_XmHTMLGetHorizontalAlignment(temp->attributes, halign)
4152 						: halign;
4153 					linefeed = CheckLineFeed(
4154 						(temp->id == HT_P ? CLEAR_HARD : CLEAR_SOFT), False,
4155 						&text_data);
4156 					/* do we have a color attrib? */
4157 					if(HTML_ATTR(allow_color_switching))
4158 						PUSH_COLOR(html);
4159 				}
4160 				object_type = OBJ_BLOCK;
4161 				break;
4162 
4163 			case HT_CENTER:
4164 				if(temp->is_end)
4165 				{
4166 					halign = (Alignment)StackPop(align_stack);
4167 					/* do we have a color attrib? */
4168 					if(HTML_ATTR(allow_color_switching))
4169 						fg = (Pixel)StackPop(fg_color_stack);
4170 				}
4171 				else
4172 				{
4173 					StackPush(align_stack, halign);
4174 					halign = XmHALIGN_CENTER;
4175 					/* do we have a color attrib? */
4176 					if(HTML_ATTR(allow_color_switching))
4177 						PUSH_COLOR(html);
4178 				}
4179 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4180 				object_type = OBJ_BLOCK;
4181 				break;
4182 
4183 			case HT_HR:
4184 				{
4185 					/*
4186 					* horizontal rules don't have an ending counterpart,
4187 					* so the alignment is never pushed. If we should do that,
4188 					* we would get an unbalanced stack.
4189 					*/
4190 					if(temp->attributes)
4191 					{
4192 						element->halign =
4193 							_XmHTMLGetHorizontalAlignment(temp->attributes,
4194 								halign);
4195 						/* see if we have a width spec */
4196 						element->len = _XmHTMLTagCheckNumber(temp->attributes,
4197 										"width", 0);
4198 
4199 						/* check height */
4200 						height = _XmHTMLTagGetNumber(temp->attributes, "size",
4201 								0);
4202 						/* sanity check */
4203 						if(height <= 0 )
4204 							height = 2;
4205 						/* y_offset is used as a flag for the NOSHADE attr. */
4206 						element->y_offset =
4207 							(int)_XmHTMLTagCheck(temp->attributes, "noshade");
4208 					}
4209 					else
4210 					{
4211 						element->halign   = halign;
4212 						element->len      = 0;
4213 						element->y_offset = 0;
4214 						height            = 0;
4215 					}
4216 
4217 					/* do we have a color attrib? */
4218 					if(HTML_ATTR(allow_color_switching) &&
4219 						!HTML_ATTR(strict_checking))
4220 						PUSH_COLOR(html);
4221 				}
4222 				/* horizontal rules always have a soft return */
4223 				linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4224 				object_type = OBJ_HRULE;
4225 				break;
4226 
4227 			/* forms */
4228 			case HT_FORM:
4229 				if(temp->is_end)
4230 					_XmHTMLEndForm(html);
4231 				else
4232 					_XmHTMLStartForm(html, temp->attributes);
4233 
4234 				/* only need form data */
4235 				ignore = True;
4236 				break;
4237 
4238 			case HT_SELECT:
4239 				/* this form component can only contain option tags */
4240 				if((words = SelectToWord(html, temp, &n_words, &width, &height,
4241 					element, in_pre)) == NULL)
4242 				{
4243 					ignore = True;
4244 					break;
4245 				}
4246 				/* walk to the end of this select */
4247 				temp = temp->next;
4248 				for(; temp != NULL && temp->id != HT_SELECT;
4249 					temp = temp->next);
4250 
4251 				text_data |= TEXT_FORM;
4252 				object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
4253 				break;
4254 
4255 			/*****
4256 			* It's an error if we get this, SelectToWord deals with these
4257 			* tags.
4258 			*****/
4259 			case HT_OPTION:
4260 				if(!temp->is_end)
4261 				{
4262 					if(HTML_ATTR(bad_html_warnings))
4263 						_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_48,
4264 							html_tokens[HT_OPTION], html_tokens[HT_SELECT],
4265 							temp->line);
4266 				}
4267 				ignore = True;
4268 				break;
4269 
4270 			case HT_TEXTAREA:
4271 				if((words = TextAreaToWord(html, temp, &n_words, &width,
4272 					&height, element, in_pre)) == NULL)
4273 				{
4274 					ignore = True;
4275 					break;
4276 				}
4277 				/*
4278 				* Walk to the end of this textarea. If there was any text
4279 				* provided, we've already picked it up.
4280 				*/
4281 				temp = temp->next;
4282 				for(; temp != NULL && temp->id != HT_TEXTAREA;
4283 					temp = temp->next);
4284 
4285 				text_data |= TEXT_FORM;
4286 				object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
4287 				break;
4288 
4289 			case HT_INPUT:
4290 				if((words = InputToWord(html, temp->attributes, &n_words,
4291 					&width, &height, element, in_pre)) == NULL)
4292 				{
4293 					ignore = True;
4294 					break;
4295 				}
4296 				/* type=image is promoted to a true image */
4297 				if(words->form->type == FORM_IMAGE)
4298 				{
4299 					text_data |= TEXT_IMAGE;
4300 
4301 					/* allocate a new anchor */
4302 					if((form_anchor_data = temp->attributes ?
4303 						_XmHTMLNewAnchor(html, temp): NULL) == NULL)
4304 						break;
4305 
4306 					/* promote to internal form anchor */
4307 					form_anchor_data->url_type = ANCHOR_FORM_IMAGE;
4308 
4309 					new_anchors++;
4310 
4311 					/* set proper element bits, we assume it's a plain one */
4312 					element_data |= ELE_ANCHOR;
4313 				}
4314 				else
4315 				{
4316 					text_data |= TEXT_FORM;
4317 				}
4318 				object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
4319 				break;
4320 
4321 			/* applets */
4322 			case HT_APPLET:
4323 				if(temp->is_end)
4324 				{
4325 					/*
4326 					* INSERT CODE
4327 					* to end this applet
4328 					*/
4329 				}
4330 				else
4331 				{
4332 					if(HTML_ATTR(bad_html_warnings))
4333 						_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_49,
4334 							html_tokens[HT_APPLET]);
4335 					/*
4336 					* INSERT CODE
4337 					* to start this applet
4338 					*/
4339 				}
4340 				object_type = OBJ_APPLET;
4341 				ignore = True;
4342 				break;
4343 
4344 			case HT_PARAM:		/* applet params */
4345 				if(HTML_ATTR(bad_html_warnings))
4346 					_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_49,
4347 						html_tokens[HT_PARAM]);
4348 				object_type = OBJ_APPLET;
4349 				ignore = True;
4350 				break;
4351 
4352 			case HT_MAP:
4353 				if(temp->is_end)
4354 				{
4355 					_XmHTMLStoreImagemap(html, imageMap);
4356 					imageMap = NULL;
4357 				}
4358 				else
4359 				{
4360 					String chPtr;
4361 
4362 					chPtr = temp->attributes ?
4363 						_XmHTMLTagGetValue(temp->attributes, "name") : NULL;
4364 					if(chPtr != NULL)
4365 					{
4366 						imageMap = _XmHTMLCreateImagemap(chPtr);
4367 						free(chPtr);
4368 					}
4369 					else if(HTML_ATTR(bad_html_warnings))
4370 						_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_50,
4371 							temp->line);
4372 				}
4373 				ignore = True;	/* only need imagemap name */
4374 				break;
4375 
4376 			case HT_AREA:
4377 				if(imageMap)
4378 					_XmHTMLAddAreaToMap(html, imageMap, temp);
4379 				else if(HTML_ATTR(bad_html_warnings))
4380 					_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_48,
4381 						html_tokens[HT_AREA], html_tokens[HT_MAP], temp->line);
4382 				ignore = True;	/* only need area data */
4383 				break;
4384 
4385 			/* tables */
4386 			case HT_TABLE:
4387 				if(temp->is_end)
4388 				{
4389 					/* wrapup current table */
4390 					table = tableClose(html, table, element);
4391 
4392 					halign = (Alignment)StackPop(align_stack);
4393 					bg = (Pixel)StackPop(bg_color_stack);
4394 					object_type = OBJ_NONE;
4395 				}
4396 				else
4397 				{
4398 					/*****
4399 					* Tables start cause a hard break if it has no
4400 					* parent, and a soft one if it has.
4401 					*****/
4402 					if(table)
4403 						linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4404 					else
4405 						linefeed = CheckLineFeed(CLEAR_HARD, True, &text_data);
4406 
4407 					StackPush(align_stack, halign);
4408 					StackPush(bg_color_stack, bg);
4409 
4410 					/*****
4411 					* Open a new table. Returns a parent or a child table.
4412 					*****/
4413 					table = tableOpen(html, table, element, temp, &halign, &bg);
4414 
4415 					if(table == NULL)
4416 						break;
4417 
4418 					/* new master table, insert */
4419 					if(table->parent == NULL)
4420 					{
4421 						/* insert this table in the list of tables */
4422 						if(HTML_ATTR(tables))
4423 						{
4424 							current_table->next = table;
4425 							current_table = table;
4426 						}
4427 						else
4428 						{
4429 							HTML_ATTR(tables) = table;
4430 							current_table = table;
4431 						}
4432 					}
4433 					object_type = OBJ_TABLE;
4434 				}
4435 				break;
4436 
4437 			case HT_CAPTION:		/* table caption */
4438 				if(temp->is_end)
4439 				{
4440 					/* close the caption */
4441 					tableCloseCaption(html, table, element);
4442 
4443 					halign = (Alignment)StackPop(align_stack);
4444 					bg = (Pixel)StackPop(bg_color_stack);
4445 
4446 					object_type = OBJ_NONE;
4447 				}
4448 				else
4449 				{
4450 					StackPush(align_stack, halign);
4451 					StackPush(bg_color_stack, bg);
4452 
4453 					/* captions are always centered */
4454 					halign = XmHALIGN_CENTER;
4455 
4456 					/* open the caption */
4457 					tableOpenCaption(html, table, element, temp, &bg);
4458 
4459 					object_type = OBJ_TABLE_FRAME;
4460 				}
4461 				break;
4462 
4463 			case HT_TR:		/* table row */
4464 				if(temp->is_end)	/* optional termination */
4465 				{
4466 					/* close current row */
4467 					tableCloseRow(html, table, element);
4468 
4469 					halign = (Alignment)StackPop(align_stack);
4470 					bg = (Pixel)StackPop(bg_color_stack);
4471 					object_type = OBJ_NONE;
4472 				}
4473 				else
4474 				{
4475 					/* open a row */
4476 					StackPush(align_stack, halign);
4477 					StackPush(bg_color_stack, bg);
4478 
4479 					tableOpenRow(html, table, element, temp, &halign, &bg);
4480 
4481 					object_type = OBJ_TABLE_FRAME;
4482 				}
4483 				break;
4484 
4485 			case HT_TH:		/* header cell */
4486 			case HT_TD:		/* regular cell */
4487 				if(temp->is_end)	/* optional termination */
4488 				{
4489 					/* header cell used a bold font, restore */
4490 					if(temp->id == HT_TH)
4491 						font = (XmHTMLfont*)StackPopDouble(font_stack,basefont);
4492 
4493 					/* close current cell */
4494 					tableCloseCell(html, table, element);
4495 
4496 					halign = (Alignment)StackPop(align_stack);
4497 					bg = (Pixel)StackPop(bg_color_stack);
4498 
4499 					object_type = OBJ_NONE;
4500 				}
4501 				else
4502 				{
4503 					/* header cell uses a bold font */
4504 					if(temp->id == HT_TH)
4505 					{
4506 						StackPushDouble(font_stack, font, basefont);
4507 						font = _XmHTMLLoadFont(html, HT_B, basefont, font);
4508 
4509 						/* new height for linefeeds */
4510 						fnheight = font->lineheight;
4511 					}
4512 
4513 					StackPush(align_stack, halign);
4514 					StackPush(bg_color_stack, bg);
4515 
4516 					/* open a cell */
4517 					tableOpenCell(html, table, element, temp, &halign, &bg);
4518 
4519 					/* table cell always resets linefeeding */
4520 					(void)CheckLineFeed(CLEAR_SOFT, True, &text_data);
4521 
4522 					object_type = OBJ_TABLE_FRAME;
4523 				}
4524 				break;
4525 
4526 			/*****
4527 			* According to HTML3.2, the following elements may not occur
4528 			* inside the body content, but a *lot* of HTML documents are
4529 			* in direct violation with this and the parser isn't always
4530 			* successfully in removing them. So we need to handle these
4531 			* elements as well and skip all data between the opening and
4532 			* closing element.
4533 			*****/
4534 			case HT_STYLE:
4535 				{
4536 					htmlEnum end_id = temp->id;
4537 					/* move past element */
4538 					temp = temp->next;
4539 					/* skip it entirely */
4540 					for(; temp != NULL; temp = temp->next)
4541 						if(temp->id == end_id && temp->is_end)
4542 							break;
4543 					ignore = True;
4544 				}
4545 				break;
4546 			case HT_SCRIPT:
4547 				{
4548 					htmlEnum end_id = temp->id;
4549 
4550 					if(HTML_ATTR(script_proc))
4551 					{
4552 						static XmHTMLScriptData script;
4553 						String data;
4554 
4555 						/* initialize */
4556 						memset((void*)&script, 0, sizeof(script));
4557 
4558 						/* set script attributes */
4559 						script.type = _XmHTMLTagGetValue(temp->attributes, "type");
4560 						script.lang = _XmHTMLTagGetValue(temp->attributes, "language");
4561 						script.src = _XmHTMLTagGetValue(temp->attributes, "src");
4562 
4563 						/* move past element */
4564 						temp = temp->next;
4565 
4566 						data = malloc(sizeof(char));
4567 						data[0] = '\0';
4568 
4569 						/* collect all enclosed text elements */
4570 						for(; temp != NULL; temp = temp->next)
4571 						{
4572 							if(temp->id == end_id && temp->is_end)
4573 								break;
4574 
4575 							if(temp->element != NULL)
4576 							{
4577 								data = realloc(data,
4578 									(strlen(data) +
4579 									strlen(temp->element))*sizeof(char));
4580 								strcat(data, temp->element);
4581 							}
4582 						}
4583 						script.script = data;
4584 
4585 						/* call user proc for this script */
4586 						(void)HTML_ATTR(script_proc)((Widget)html,
4587 							&script, HTML_ATTR(client_data));
4588 
4589 						/* release again */
4590 						if(script.type)
4591 							free(script.type);
4592 						if(script.lang)
4593 							free(script.lang);
4594 						if(script.src)
4595 							free(script.src);
4596 						free(script.script);
4597 					}
4598 					else
4599 					{
4600 						/* move past element */
4601 						temp = temp->next;
4602 						/* skip it entirely */
4603 						for(; temp != NULL; temp = temp->next)
4604 							if(temp->id == end_id && temp->is_end)
4605 								break;
4606 					}
4607 					ignore = True;
4608 				}
4609 				break;
4610 
4611 			default:
4612 				_XmHTMLDebug(2, ("format.c: _XmHTMLformatObjects; "
4613 					"Unused element %s.\n", temp->element));
4614 				ignore = True;
4615 		}
4616 		if(!ignore)
4617 		{
4618 			/* unset break bit if this is not a text object */
4619 			if(object_type != OBJ_TEXT)
4620 				text_data &= ~TEXT_BREAK;
4621 			else if(object_type == OBJ_BLOCK)
4622 			{
4623 				/* clear all spacing */
4624 				text_data &= ~TEXT_SPACE_LEAD & ~TEXT_SPACE_TRAIL;
4625 				text_data |= TEXT_SPACE_NONE;
4626 			}
4627 
4628 			/* adjust anchor count */
4629 			if(element_data & ELE_ANCHOR)
4630 			{
4631 				text_data |= TEXT_ANCHOR;
4632 				anchor_words += n_words;
4633 				anchor_data_used = True;
4634 			}
4635 			/* mark object as internal anchor */
4636 			if(element_data & ELE_ANCHOR_INTERN)
4637 			{
4638 				text_data |= TEXT_ANCHOR_INTERN;
4639 				anchor_data_used = True;
4640 				/* add an anchor id */
4641 				element->id = named_anchors;
4642 				named_anchors++;
4643 			}
4644 			element->text = text;
4645 			element->text_data = text_data;
4646 			element->words = words;
4647 			element->n_words = n_words;
4648 			element->width = width;
4649 			element->height = height;
4650 			element->fg = fg;
4651 			element->bg = bg;
4652 			element->font = font;
4653 			element->marker = list_stack[current_list].marker;
4654 			element->list_level = list_stack[current_list].level;
4655 			element->table = table;
4656 			/*
4657 			* <dt> elements have an identation one less than the current.
4658 			* all identation must use the default font (consistency).
4659 			*/
4660 			if(in_dt && ident_level)
4661 				element->ident = (ident_level-1) * XmHTML_INDENT_SPACES *
4662 					HTML_ATTR(default_font)->m_width;
4663 			else
4664 				element->ident = ident_level * XmHTML_INDENT_SPACES *
4665 					HTML_ATTR(default_font)->m_width;
4666 
4667 			element->linefeed = (int)((1+linefeed)*fnheight);
4668 
4669 			/* stupid hack so HT_HR won't mess up alignment and color stack */
4670 			if(temp->id != HT_HR)
4671 			{
4672 				element->halign = halign;
4673 				element->y_offset = y_offset;
4674 				element->x_offset = x_offset;
4675 			}
4676 			else if(HTML_ATTR(allow_color_switching) &&
4677 						!HTML_ATTR(strict_checking))
4678 				fg = (Pixel)StackPop(fg_color_stack);
4679 
4680 			element->object_type = object_type;
4681 
4682 			if(object_type == OBJ_BULLET)
4683 				FillBullet(html, element, tka);
4684 
4685 			/*****
4686 			* If we have a form component of type <input type="image">, we
4687 			* have promoted it to an anchor. Set this anchor data as the
4688 			* anchor for this element and, as it is used only once, reset
4689 			* it to NULL. In all other case we have a plain anchor.
4690 			*
4691 			* Note: as form components are allowed inside anchors, this is
4692 			* the only place in which we can possibly have nested anchors.
4693 			* This is a problem we will have to live with...
4694 			*****/
4695 			if(form_anchor_data)
4696 			{
4697 				element->anchor = form_anchor_data;
4698 				form_anchor_data = NULL;
4699 				element_data &= ~ELE_ANCHOR;
4700 			}
4701 			else
4702 				element->anchor = anchor_data;
4703 
4704 			/*****
4705 			* Discard non-spacing bits so we only carry current spacing
4706 			* to the next chunk of text. Break bit must be kept to carry
4707 			* lack of leading space over to the next chunk of text.
4708 			*****/
4709 			text_data &= (~TEXT_ANCHOR & ~TEXT_ANCHOR_INTERN &
4710 							~TEXT_IMAGE & ~TEXT_FORM); /* & ~TEXT_BREAK); */
4711 
4712 			InsertTableElement(element, element_data & ELE_ANCHOR);
4713 		}
4714 #ifdef DEBUG
4715 		else /* element will be reused if it was ignored */
4716 			num_ignore++;
4717 #endif
4718 
4719 		/* move to next element */
4720 		temp = temp->next;
4721 	}
4722 	/* if there still is an open table, close it now */
4723 	if(table)
4724 		tableClose(html, table, element);
4725 
4726 	/*****
4727 	* Insert a dummy element at the end of the list, saves some NULL tests
4728 	* in the layout routines.
4729 	*****/
4730 	if(ignore)
4731 	{
4732 		/* reuse ignored element */
4733 		memset(element, 0, sizeof(XmHTMLObjectTable));
4734 		element->object_type = OBJ_NONE;
4735 	}
4736 	else
4737 		NewTableElement(element,NULL);
4738 
4739 	/* each element must have a font with it */
4740 	element->font = HTML_ATTR(default_font);
4741 
4742 	InsertTableElement(element, False);
4743 
4744 	/*
4745 	* Some sucker forget to terminate a list and parser failed to repair it.
4746 	* Spit out a warning.
4747 	*/
4748 	if(HTML_ATTR(bad_html_warnings) && ident_level != 0)
4749 		_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_51);
4750 
4751 	/* clear all allocated stacks */
4752 #ifdef DEBUG
4753 	if((i = StackDestroy(fg_color_stack)) != 0)
4754 		_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4755 			"Foreground color", i);
4756 	if((i = StackDestroy(bg_color_stack)) != 0)
4757 		_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4758 			"Background color", i);
4759 	if((i = StackDestroy(align_stack)) != 0)
4760 		_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4761 			"Horizontal Alignment", i);
4762 	if((i = StackDestroy(font_stack)) != 0)
4763 		_XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4764 			"Font", i);
4765 #else
4766 	(void)StackDestroy(fg_color_stack);
4767 	(void)StackDestroy(bg_color_stack);
4768 	(void)StackDestroy(align_stack);
4769 	(void)StackDestroy(font_stack);
4770 #endif
4771 
4772 	/*
4773 	* allocate memory for all anchor words in this document, gets filled in
4774 	* paint.c
4775 	*/
4776 	if(anchor_words)
4777 	{
4778 		HTML_ATTR(anchors)= (XmHTMLWord*)calloc(anchor_words+1,
4779 			sizeof(XmHTMLWord));
4780 
4781 		_XmHTMLDebug(2,("_XmHTMLFormatObjects: anchors contain %i words\n",
4782 			anchor_words));
4783 		HTML_ATTR(anchor_words) = anchor_words;
4784 	}
4785 
4786 	/* allocated memory for all named anchors. Gets filled in paint.c */
4787 	if(named_anchors)
4788 	{
4789 		HTML_ATTR(named_anchors) =
4790 			(XmHTMLObjectTable*)calloc(named_anchors+1,
4791 				sizeof(XmHTMLObjectTable));
4792 
4793 		HTML_ATTR(num_named_anchors) = named_anchors;
4794 	}
4795 
4796 	_XmHTMLDebug(2,("_XmHTMLFormatObjects: formatted %li elements of which %li"
4797 		" anchors.\n", list_data.num_elements, list_data.num_anchors));
4798 	_XmHTMLDebug(2,("_XmHTMLFormatObjects: found %i named anchors.\n",
4799 		named_anchors));
4800 	_XmHTMLDebug(2, ("_XmHTMLformatObjects, allocated %i elements and "
4801 		"ignored %i objects.\n", allocated, num_ignore));
4802 
4803 	_XmHTMLDebug(2, ("_XmHTMLformatObjects, allocated %i XmHTMLAnchor "
4804 		"objects\n", new_anchors));
4805 
4806 	/* store the anchor list */
4807 	HTML_ATTR(anchor_data) = list_data.anchor_head;
4808 
4809 	/* Since the table head is a dummy element, we return the next one */
4810 	list_data.current = list_data.head->next;
4811 
4812 	/* this is *required* */
4813 	if(list_data.current)
4814 		list_data.current->prev = NULL;
4815 
4816 	/* free top of the list */
4817 	free(list_data.head);
4818 	list_data.head = NULL;
4819 
4820 	/* store it */
4821 	HTML_ATTR(formatted) = list_data.current;
4822 
4823 	/* all done! */
4824 }
4825 
4826 /********
4827 ****** Public XmHTML Functions
4828 ********/
4829 
4830 /* all url types we know about, 17 in total */
4831 static String anchor_tokens[] = {"about", "exec", "file", "ftp", "gopher",
4832 	"help", "http", "https" ,"info", "mailto", "man", "news", "pipe", "telnet",
4833 	"wais", "xexec", "zzz"};
4834 
4835 /*****
4836 * Name: 		XmHTMLGetURLType
4837 * Return Type: 	URLType
4838 * Description: 	tries to figure out what type of url the given href is
4839 * In:
4840 *	href:		url specification
4841 * Returns:
4842 *	type of url when we know it, ANCHOR_UNKNOWN otherwise.
4843 *****/
4844 URLType
XmHTMLGetURLType(String href)4845 XmHTMLGetURLType(String href)
4846 {
4847 	char *chPtr;
4848 
4849 	if(href == NULL || *href == '\0')
4850 		return(ANCHOR_UNKNOWN);
4851 
4852 	_XmHTMLDebug(2, ("format.c: XmHTMLGetURLType; checking url type of %s\n",
4853 		href));
4854 
4855 	/* first pick up any leading url spec */
4856 	if((chPtr = strstr(href, ":")) != NULL && (chPtr - href) < 7)
4857 	{
4858 		char token[7];
4859 		URLType ret_val;
4860 
4861 		strncpy(token, href, chPtr - href);
4862 		token[chPtr - href] = '\0';	/* NULL terminate */
4863 
4864 		ret_val = (URLType)stringToToken(token, anchor_tokens,
4865 					(int)ANCHOR_UNKNOWN);
4866 
4867 #ifdef DEBUG
4868 		if(ret_val == ANCHOR_UNKNOWN)
4869 			_XmHTMLDebug(2, ("format.c: XmHTMLGetURLType; unknown type %s\n",
4870 				token));
4871 #endif
4872 		return(ret_val);
4873 	}
4874 	return(href[0] == '#' ? ANCHOR_JUMP : ANCHOR_FILE_LOCAL);
4875 }
4876