1%%	options
2
3copyright owner	=	Dirk Krause
4copyright year	=	2013-xxxx
5SPDX-License-Identifier:	BSD-3-Clause
6
7
8%%	header
9
10#ifdef __cplusplus
11extern "C" {
12#endif
13
14/**	Write one HTML file.
15	@param	job		Job structure.
16	@param	nodeptr		Node for file to write.
17	@param	p		Previous node.
18	@param	n		Next node.
19*/
20void
21hbhtml_write_file(hb_job_t *job,hb_node_t *nodeptr,hb_node_t *p,hb_node_t *n);
22
23/**	Write one 32-bit character to HTML output.
24	@param	job	Job structure.
25	@param	c32	Character to write.
26*/
27void
28hbhtml_c32(hb_job_t *job, dk3_c32_t c32);
29
30/**	Write some text as HTML.
31	@param	job	Job structure.
32	@param	text	Text to write.
33	@return	1 on success, 0 on error.
34*/
35int
36hbhtml_output_for_text(hb_job_t *job, dkChar const *text);
37
38/**	Write some URL text as HTML.
39	@param	job	Job structure.
40	@param	text	Text to write.
41	@return	1 on success, 0 on error.
42*/
43int
44hbhtml_url_output_for_text(hb_job_t *job, dkChar const *text);
45
46/**	Handle one input line, either from template or from text file.
47	@param	obj	Line processor structure.
48	@param	il	Input line to process.
49	@return	1 on success, 0 on error (continue), -1 on error (exit).
50*/
51int
52hbhtml_line_handler(void *obj, dkChar *il);
53
54/**	Construct output file name.
55	@param	dptr	Destination buffer pointer.
56	@param	sz	Destination buffer size.
57	@param	job	Job structure.
58	@param	nptr	Node to process.
59	@return	1 on success, 0 on error.
60*/
61int
62hbhtml_create_output_filename(
63  dkChar	*dptr,
64  size_t 	 sz,
65  hb_job_t	*job,
66  hb_node_t	*nptr
67);
68
69/**	Check whether a link is an external link (http:// or ftp://
70	at start).
71	@param	link	Link to check.
72	@return	1 for external links, 0 for internal links.
73*/
74int
75hbhtml_check_link_for_external(dkChar const *link);
76
77/**	Find HTML doctype for a node.
78	@param	job	Htmlbook job.
79	@param	nodeptr	Node to start search.
80	@return	Doctype for node or job.
81*/
82int
83hbhtml_get_doctype(
84  hb_job_t	*job,
85  hb_node_t	*nodeptr
86);
87
88#ifdef __cplusplus
89}
90#endif
91
92
93%%	state machine
94
95[options]
96
97name		=	hbhtml_stm1
98write header	=	no
99
100[states]
101STM1_ST_START		# Start state
102STM1_ST_ERROR		# Error occured
103STM1_ST_BS		# Backslash found
104STM1_ST_PERCENT		# One percent sign found
105STM1_ST_SPECIAL		# Recording a special
106STM1_ST_SPEC_BS		# Backslash found in special
107STM1_ST_SPEC_PC		# Percent found in special
108
109[inputs]
110STM1_I_ANY		# Any character
111STM1_I_BS		# Backslash
112STM1_I_PERCENT		# Percent sign
113
114[outputs]
115STM1_O_ERROR		# Error, do nothing
116STM1_O_NOTHING		# Do nothing
117STM1_O_SHIPOUT		# Add character to shipout buffer
118STM1_O_BS_SHIPOUT	# Shipout character after backslash
119STM1_O_PERCENT_SHIPOUT	# Shipout percent sign and current character
120STM1_O_PERCENT		# Shipout just percent sign
121STM1_O_SWITCH		# Flush shipout and switch to saving special
122STM1_O_SPECIAL		# Add current character to special
123STM1_O_SPEC_BS		# Add char after backslash to special
124STM1_O_SPEC_PC_ADD	# Add percent and current char to special
125STM1_O_SPEC_PC		# Add percent sign to special
126STM1_O_EXECUTE		# Execute special
127
128[rules]
129*		*		STM1_ST_ERROR	STM1_O_ERROR
130
131STM1_ST_START	*		STM1_ST_START	STM1_O_SHIPOUT
132STM1_ST_START	STM1_I_BS	STM1_ST_BS	STM1_O_NOTHING
133STM1_ST_START	STM1_I_PERCENT	STM1_ST_PERCENT	STM1_O_NOTHING
134
135STM1_ST_BS	*		STM1_ST_START	STM1_O_BS_SHIPOUT
136
137STM1_ST_PERCENT	*		STM1_ST_START	STM1_O_PERCENT_SHIPOUT
138STM1_ST_PERCENT	STM1_I_BS	STM1_ST_BS	STM1_O_PERCENT
139STM1_ST_PERCENT	STM1_I_PERCENT	STM1_ST_SPECIAL	STM1_O_SWITCH
140
141STM1_ST_SPECIAL	*		STM1_ST_SPECIAL	STM1_O_SPECIAL
142STM1_ST_SPECIAL	STM1_I_BS	STM1_ST_SPEC_BS	STM1_O_NOTHING
143STM1_ST_SPECIAL	STM1_I_PERCENT	STM1_ST_SPEC_PC	STM1_O_NOTHING
144
145STM1_ST_SPEC_BS	*		STM1_ST_SPECIAL	STM1_O_SPEC_BS
146
147STM1_ST_SPEC_PC	*		STM1_ST_SPECIAL	STM1_O_SPEC_PC_ADD
148STM1_ST_SPEC_PC	STM1_I_BS	STM1_ST_SPEC_BS	STM1_O_SPEC_PC
149STM1_ST_SPEC_PC	STM1_I_PERCENT	STM1_ST_START	STM1_O_EXECUTE
150
151%%	state machine
152[options]
153	name		= hbhtml_stm3
154	write header	= no
155[states]
156	STM3_ST_START		# No character processed yet
157	STM3_ST_NAME		# ASCII characters (for protocol name) found
158	STM3_ST_COLON		# Colon found
159	STM3_ST_COLSL		# Colon and slash
160	STM3_ST_SUCCESS		# Colon and two slashes found
161	STM3_ST_ERROR		# Not a protocol at start of text
162[inputs]
163	STM3_I_ANY		# Anything not listed below
164	STM3_I_ASCII		# a-zA-Z01-9_
165	STM3_I_COLON		# Colon
166	STM3_I_SLASH		# Slash
167[outputs]
168	STM3_O_OK		# OK so far
169	STM3_O_ERROR		# ERROR found
170[rules]
171	*		*		STM3_ST_ERROR	STM3_O_ERROR
172	STM3_ST_START	STM3_I_ASCII	STM3_ST_NAME	STM3_O_OK
173	STM3_ST_NAME	STM3_I_ASCII	STM3_ST_NAME	STM3_O_OK
174	STM3_ST_NAME	STM3_I_COLON	STM3_ST_COLON	STM3_O_OK
175	STM3_ST_COLON	STM3_I_SLASH	STM3_ST_COLSL	STM3_O_OK
176	STM3_ST_COLSL	STM3_I_SLASH	STM3_ST_SUCCESS	STM3_O_OK
177	STM3_ST_SUCCESS	*		STM3_ST_SUCCESS	STM3_O_OK
178
179
180%%	module
181
182
183#include <libdk3c/dk3all.h>
184#include <libdk3c/dk3strkv.h>
185#include <htmlbook/htmlbook.h>
186#include <libdk3c/dk3enc.h>
187
188
189
190$!trace-include
191
192
193
194/**	Object for line reader (special comments).
195*/
196typedef struct {
197  hb_job_t	*job;		/**< Job structure. */
198  hb_node_t	*nodeptr;	/**< Current node to process. */
199  unsigned long	 lineno;	/**< Current line number to process. */
200} hb_spc_reader_t;
201
202
203
204/**	Translation table for ASCII characters.
205*/
206static char const * const hbhtml_translation_table[] = {
207/*   0 00 */	 NULL,
208/*   1 01 */	 NULL,
209/*   2 02 */	 NULL,
210/*   3 03 */	 NULL,
211/*   4 04 */	 NULL,
212/*   5 05 */	 NULL,
213/*   6 06 */	 NULL,
214/*   7 07 */	 NULL,
215/*   8 08 */	 NULL,
216/*   9 09 */	"\t",
217/*  10 0a */	 NULL,
218/*  11 0b */	 NULL,
219/*  12 0c */	 NULL,
220/*  13 0d */	 NULL,
221/*  14 0e */	 NULL,
222/*  15 0f */	 NULL,
223/*  16 10 */	 NULL,
224/*  17 11 */	 NULL,
225/*  18 12 */	 NULL,
226/*  19 13 */	 NULL,
227/*  20 14 */	 NULL,
228/*  21 15 */	 NULL,
229/*  22 16 */	 NULL,
230/*  23 17 */	 NULL,
231/*  24 18 */	 NULL,
232/*  25 19 */	 NULL,
233/*  26 1a */	 NULL,
234/*  27 1b */	 NULL,
235/*  28 1c */	 NULL,
236/*  29 1d */	 NULL,
237/*  30 1e */	 NULL,
238/*  31 1f */	 NULL,
239/*  32 20 */	" ",
240/*  33 21 */	"!",
241/*  34 22 */	"&quot;",
242/*  35 23 */	"#",
243/*  36 24 */	"$",
244/*  37 25 */	"%",
245/*  38 26 */	"&amp;",
246/*  39 27 */	"'",
247/*  40 28 */	"(",
248/*  41 29 */	")",
249/*  42 2a */	"*",
250/*  43 2b */	"+",
251/*  44 2c */	",",
252/*  45 2d */	"-",
253/*  46 2e */	".",
254/*  47 2f */	"/",
255/*  48 30 */	"0",
256/*  49 31 */	"1",
257/*  50 32 */	"2",
258/*  51 33 */	"3",
259/*  52 34 */	"4",
260/*  53 35 */	"5",
261/*  54 36 */	"6",
262/*  55 37 */	"7",
263/*  56 38 */	"8",
264/*  57 39 */	"9",
265/*  58 3a */	":",
266/*  59 3b */	";",
267/*  60 3c */	"&lt;",
268/*  61 3d */	"=",
269/*  62 3e */	"&gt;",
270/*  63 3f */	"?",
271/*  64 40 */	"@",
272/*  65 41 */	"A",
273/*  66 42 */	"B",
274/*  67 43 */	"C",
275/*  68 44 */	"D",
276/*  69 45 */	"E",
277/*  70 46 */	"F",
278/*  71 47 */	"G",
279/*  72 48 */	"H",
280/*  73 49 */	"I",
281/*  74 4a */	"J",
282/*  75 4b */	"K",
283/*  76 4c */	"L",
284/*  77 4d */	"M",
285/*  78 4e */	"N",
286/*  79 4f */	"O",
287/*  80 50 */	"P",
288/*  81 51 */	"Q",
289/*  82 52 */	"R",
290/*  83 53 */	"S",
291/*  84 54 */	"T",
292/*  85 55 */	"U",
293/*  86 56 */	"V",
294/*  87 57 */	"W",
295/*  88 58 */	"X",
296/*  89 59 */	"Y",
297/*  90 5a */	"Z",
298/*  91 5b */	"[",
299/*  92 5c */	"\\",
300/*  93 5d */	"]",
301/*  94 5e */	"^",
302/*  95 5f */	"_",
303/*  96 60 */	"`",
304/*  97 61 */	"a",
305/*  98 62 */	"b",
306/*  99 63 */	"c",
307/* 100 64 */	"d",
308/* 101 65 */	"e",
309/* 102 66 */	"f",
310/* 103 67 */	"g",
311/* 104 68 */	"h",
312/* 105 69 */	"i",
313/* 106 6a */	"j",
314/* 107 6b */	"k",
315/* 108 6c */	"l",
316/* 109 6d */	"m",
317/* 110 6e */	"n",
318/* 111 6f */	"o",
319/* 112 70 */	"p",
320/* 113 71 */	"q",
321/* 114 72 */	"r",
322/* 115 73 */	"s",
323/* 116 74 */	"t",
324/* 117 75 */	"u",
325/* 118 76 */	"v",
326/* 119 77 */	"w",
327/* 120 78 */	"x",
328/* 121 79 */	"y",
329/* 122 7a */	"z",
330/* 123 7b */	"{",
331/* 124 7c */	"|",
332/* 125 7d */	"}",
333/* 126 7e */	"~",
334/* 127 7f */	 NULL,
335/* 128 80 */	 NULL,
336/* 129 81 */	 NULL,
337/* 130 82 */	 NULL,
338/* 131 83 */	 NULL,
339/* 132 84 */	 NULL,
340/* 133 85 */	 NULL,
341/* 134 86 */	 NULL,
342/* 135 87 */	 NULL,
343/* 136 88 */	 NULL,
344/* 137 89 */	 NULL,
345/* 138 8a */	 NULL,
346/* 139 8b */	 NULL,
347/* 140 8c */	 NULL,
348/* 141 8d */	 NULL,
349/* 142 8e */	 NULL,
350/* 143 8f */	 NULL,
351/* 144 90 */	 NULL,
352/* 145 91 */	 NULL,
353/* 146 92 */	 NULL,
354/* 147 93 */	 NULL,
355/* 148 94 */	 NULL,
356/* 149 95 */	 NULL,
357/* 150 96 */	 NULL,
358/* 151 97 */	 NULL,
359/* 152 98 */	 NULL,
360/* 153 99 */	 NULL,
361/* 154 9a */	 NULL,
362/* 155 9b */	 NULL,
363/* 156 9c */	 NULL,
364/* 157 9d */	 NULL,
365/* 158 9e */	 NULL,
366/* 159 9f */	 NULL,
367/* 160 a0 */	"&nbsp;",
368/* 161 a1 */	"&iexcl;",
369/* 162 a2 */	"&cent;",
370/* 163 a3 */	"&pound;",
371/* 164 a4 */	"&curren;",
372/* 165 a5 */	"&yen;",
373/* 166 a6 */	"&brvbar;",
374/* 167 a7 */	"&sect;",
375/* 168 a8 */	"&uml;",
376/* 169 a9 */	"&copy;",
377/* 170 aa */	"&ordf;",
378/* 171 ab */	"&laquo;",
379/* 172 ac */	"&not;",
380/* 173 ad */	"&shy;",
381/* 174 ae */	"&reg;",
382/* 175 af */	"&macr;",
383/* 176 b0 */	"&deg;",
384/* 177 b1 */	"&plusmn;",
385/* 178 b2 */	"&sup2;",
386/* 179 b3 */	"&sup3;",
387/* 180 b4 */	"&acute;",
388/* 181 b5 */	"&micro;",
389/* 182 b6 */	"&para;",
390/* 183 b7 */	"&middot;",
391/* 184 b8 */	"&cedil;",
392/* 185 b9 */	"&sup1;",
393/* 186 ba */	"&ordm;",
394/* 187 bb */	"&raquo;",
395/* 188 bc */	"&frac14;",
396/* 189 bd */	"&frac12;",
397/* 190 be */	"&frac34;",
398/* 191 bf */	"&iquest;",
399/* 192 c0 */	"&Agrave;",
400/* 193 c1 */	"&Aacute;",
401/* 194 c2 */	"&Acirc;",
402/* 195 c3 */	"&Atilde;",
403/* 196 c4 */	"&Auml;",
404/* 197 c5 */	"&Aring;",
405/* 198 c6 */	"&AElig;",
406/* 199 c7 */	"&Ccedil;",
407/* 200 c8 */	"&Egrave;",
408/* 201 c9 */	"&Eacute;",
409/* 202 ca */	"&Ecirc;",
410/* 203 cb */	"&Euml;",
411/* 204 cc */	"/Igrave;",
412/* 205 cd */	"&Iacute;",
413/* 206 ce */	"&Icirc;",
414/* 207 cf */	"&Iuml;",
415/* 208 d0 */	"&ETH;",
416/* 209 d1 */	"&Ntilde;",
417/* 210 d2 */	"&Ograve;",
418/* 211 d3 */	"&Oacute;",
419/* 212 d4 */	"&Ocirc;",
420/* 213 d5 */	"&Otilde;",
421/* 214 d6 */	"&Ouml;",
422/* 215 d7 */	"&times;",
423/* 216 d8 */	"&Oslash;",
424/* 217 d9 */	"&Ugrave;",
425/* 218 da */	"&Uacute;",
426/* 219 db */	"&Ucirc;",
427/* 220 dc */	"&Uuml;",
428/* 221 dd */	"&Yacute;",
429/* 222 de */	"&THORN;",
430/* 223 df */	"&szlig;",
431/* 224 e0 */	"&agrave;",
432/* 225 e1 */	"&aacute;",
433/* 226 e2 */	"&acirc;",
434/* 227 e3 */	"&atilde;",
435/* 228 e4 */	"&auml;",
436/* 229 e5 */	"&aring;",
437/* 230 e6 */	"&aelig;",
438/* 231 e7 */	"&ccedil;",
439/* 232 e8 */	"&egrave;",
440/* 233 e9 */	"&eacute;",
441/* 234 ea */	"&ecirc;",
442/* 235 eb */	"&euml;",
443/* 236 ec */	"&igrave;",
444/* 237 ed */	"&iacute;",
445/* 238 ee */	"&icirc;",
446/* 239 ef */	"&iuml;",
447/* 240 f0 */	"&eth;",
448/* 241 f1 */	"&ntilde;",
449/* 242 f2 */	"&ograve;",
450/* 243 f3 */	"&oacute;",
451/* 244 f4 */	"&ocirc;",
452/* 245 f5 */	"&otilde;",
453/* 246 f6 */	"&ouml;",
454/* 247 f7 */	"&divide;",
455/* 248 f8 */	"&oslash;",
456/* 249 f9 */	"&ugrave;",
457/* 250 fa */	"&uacute;",
458/* 251 fb */	"&ucirc;",
459/* 252 fc */	"&uuml;",
460/* 253 fd */	"&yacute;",
461/* 254 fe */	"&thorn;",
462/* 255 ff */	"&yuml;"
463};
464
465
466
467/**	Constant 8-bit character keywords.
468*/
469static char const * const	hbhtml_c8_kw[] = {
470$!string-table
471#
472#	0	Newline
473#
474\n
475#
476#	1	Space
477#
478
479#
480#	2	Start of an image
481#
482<img
483#
484#	3	General tag end
485#
486>
487#
488#	4	Image source
489#
490src="
491#
492#	5	End of attribute
493#
494"
495#
496#	6	alt attribute
497#
498 alt="
499#
500#	7	title attribute
501#
502 title="
503#
504#	8	border attribute
505#
506 border="
507#
508#	9	width attribute
509#
510 width="
511#
512#	10	height attribute
513#
514 height="
515#
516#	11 12	HTML tag
517#
518<html>\n
519</html>\n
520#
521#	13 14	BODY tag
522#
523<body>\n
524</body>\n
525#
526#	15 16	HEAD tag
527#
528<head>\n
529</head>\n
530#
531#	17 18	Generated by
532#
533<!--\nGenerated by htmlbook (see http://sourceforge.net/p/dktools/wiki/htmlbook%20manual/)\n-->\n
534<meta name="generator" content="htmlbook (http://sourceforge.net/p/dktools/wiki/htmlbook/)">\n
535#
536#	19 20	TITLE tag
537#
538<title>
539</title>\n
540#
541#	21 22	Style sheet
542#
543<link rel="stylesheet" type="text/css" href="
544">\n
545#
546#	23 24	Charset specifications
547#
548<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">\n
549<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n
550#
551#	25 26	Author
552#
553<meta name="author" content="
554">\n
555#
556#	27 28	Shortcut icon
557#
558<link rel="shortcut icon" href="
559">\n
560#
561#	29 30	Navi menu div
562#
563<div class="hb_nm_div">\n
564</div><!-- class=hb_nm_div -->\n
565#
566#	31 32	Navi menu ul
567#
568<ul class="hb_nm_ul">\n
569</ul><!-- class=hb_nm_ul -->\n
570#
571#	33	Navi menu separator
572#
573<li class="hb_nm_sep">&nbsp;</li>\n
574#
575#	34 35	Navi menu current node entry
576#
577<li class="hb_nm_c"
578</li>\n
579#
580#	36 37	Navi menu current entry
581#
582<li class="hb_nm_li">
583</li>\n
584#
585#	38	Class name for navigation link
586#
587hb_nm_a
588#
589#	39 40 41 42 43 44 45	Link
590#
591<a href="
592>
593</a>
594"
595 class="
596 title="
597 type="
598#
599#	46 47	Navigation div
600#
601<div class="hb_navigation">
602</div>\n
603#
604#	48	Space between navigation images
605#
606&nbsp;
607#
608#	49 50 51 52	Navigation link
609#
610<a class="hb_navi_a" href="
611" title="
612">
613</a>
614#
615#	53 54 55 56	Image in navigation link
616#
617<img src="
618" alt="
619" title="
620" class="hb_navi_img" border="0"
621#
622#	57	Default border
623#
6240
625#
626#	58	Name attribute
627#
628 name="
629#
630#	59	longdesc attribute
631#
632 longdesc="
633#
634#	60	Class attribute
635#
636 class="
637#
638#	61	ID attribute
639#
640 id="
641#
642#	62	Usemap attribute
643#
644 usemap="
645#
646#	63	Align attribute
647#
648 align="
649#
650#	64	Hspace attribute
651#
652 hspace="
653#
654#	65	Vspace attribute
655#
656 vspace="
657#
658#	66	Ismap attribute
659#
660 ismap
661#
662#	67	Width and height attributes
663#
664 width="%lu" height="%lu"
665#
666#	68	Link to enlarge image
667#
668<a class="hb_img_a_larger" href="
669#
670#	69	Link target is a blank window
671#
672" target="_blank"
673#
674#	70 71 72 73	PDF object
675#
676<object type="application/pdf" data="
677" width="
678" height="
679</object>
680#
681#	74		Named anchor
682#
683<a name="
684#
685#	75 76 77 78		target	type	class	id
686#
687" target="
688" type="
689" class="
690" id="
691#
692#	79 80 81 82		style	hreflang	title	onblur
693#
694" style="
695" hreflang="
696" title="
697" onblur="
698#
699#	83 84 85 86	onfocus	rel	rev	shape
700#
701" onfocus="
702" rel="
703" rev="
704" shape="
705#
706#	87 88 89 90	tabindex accesskey charset coords
707#
708" tabindex="
709" accesskey="
710" charset="
711" coords="
712#
713#	91
714#
715<span class="hb_link_number">&nbsp;&#9001;%lu&#9002;</span>
716#
717#	92 93 94 95 96 97 98 99	Menu entry table.
718#
719<table class="hb_me_ta" summary="
720</table>\n
721<tr class="hb_me_tr">\n
722</tr>\n
723<td class="hb_me_td_text" colspan="%u">
724</td>\n
725<td></td>\n
726<td class="hb_me_td_arrow">&nbsp;&#8594;&nbsp;</td>\n
727#
728#  100 101	Link classes
729#
730hb_a_i
731hb_a_e
732#
733#  102 103 104 105
734#
735<div class="hb_file_div">\n
736<a class="hb_file_a" target="_blank" href="
737text/plain
738<br>\n
739#
740#  106 107
741#
742<pre>\n
743</pre>\n
744#
745#  108
746#
747" target="_blank
748#
749#  109 110 111 112 113
750#
751<div class="hb_img_div_outer">\n
752<div class="hb_img_div_inner_img">\n
753<div class="hb_img_div_inner_img_centered">\n
754<p class="hb_img_p_download">\n
755<br class="clearboth">\n</p>\n
756#
757#  114 115
758#
759<object type="image/svg+xml" data="
760<param name="src" value="
761#
762#  116 117
763#
764<br>\n<span class="hb_img_caption">
765</span>
766#
767#  118 119
768#
769 type="image/svg+xml"
770 type="application/pdf"
771#
772#  120
773#
774<p><br class="clearboth"></p>\n
775#
776#  121 122
777#
778<script src="
779" type="text/javascript"></script>\n
780#
781#  123 124 125 126
782#
783<link rel="
784" title="
785" href="
786">\n
787#
788#  127 ... 135
789#
790author
791contents
792index
793top
794up
795first
796prev
797next
798last
799#
800#  136 137
801#
802\#hb_toc
803\#hb_index
804#
805#  138 139 140 141 142 143
806#
807<table class="hb_img_ta" summary="
808<tr class="hb_img_tr">\n
809<td class="hb_img_td_img">\n
810<td class="hb_img_td_empty">&nbsp;</td>\n
811<td class="hb_img_td_a">\n
812">\n
813#
814#  144
815#
816hb_nm_a_parent
817#
818#  145
819#
820<div class="hb_img_div_no_vspace">\n
821#
822#  146 147 148 149 150
823#
824<code title="
825">
826</code>
827
828
829#
830#	151 152 153
831#
832<span class="hint" title="
833">
834</span>
835#
836#	154
837#
838<meta name="description" content="
839#
840#	155
841#
842<meta name="keywords" content="
843#
844#	156 157
845#
846&#x
847;&nbsp;
848# &#x2197;&nbsp;
849#
850#	158
851#
852<table class="hb_me_ta">\n
853#
854# 159
855#
856<table class="hb_img_ta">\n
857#
858# 160
859#
860<body bgcolor="#FFFFFF">\n
861#
862# 161, 162
863#
864<html lang="
865">\n
866#
867# 163, 164
868#
869<meta charset="utf-8">\n
870<meta name="viewport" content="width=device-width, initial-scale=1.0">\n
871#
872# 165
873#
874<link rel="stylesheet" href="
875#
876# 166, 167, 168
877#
878"></script>\n
879" async></script>\n
880" type="text/javascript" async></script>\n
881#
882# 169, 170
883#
884<script type="text/javascript" src="
885<script src="
886$!end
887};
888
889
890
891static const char * const	hbhtml_doctypes[] = {
892$!string-table
893<!DOCTYPE html>\n
894<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n
895<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n
896<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">\n
897<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n
898<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n
899<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">\n
900<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
901$!end
902};
903
904
905
906/**	Special commands.
907*/
908dkChar const * const	hbhtml_special_commands[] = {
909$!string-table	macro=dkT
910#
911#   0:	Contents
912#
913contents
914#
915#   1:	Title
916#
917title
918#
919#   2:	Image
920#
921img
922#
923#   3:	Menu
924#
925menu
926#
927#   4:	Navigation
928#
929navigation
930#
931#   5:	Links
932#
933links
934#
935#   6:	Author
936#
937author
938#
939#   7:	Location
940#
941location
942#
943#   8:	Date
944#
945date
946#
947#   9:	Variable
948#
949var
950#
951#  10:	Code
952#
953code
954#
955#  11:	a (link)
956#
957a
958#
959#  12:	menuentry
960#
961menuentry
962#
963#  13:	index (create keyword index entry)
964#
965index
966#
967#  14:	source file
968#
969sourcefile
970#
971#  15:	HTML file name
972#
973htmlname
974#
975#  16:	Hint
976#
977hint
978$!end
979};
980
981
982
983
984/**	Attributes allowed in image special command.
985*/
986dkChar const * const	hbhtml_img_keys[] = {
987$!string-table	macro=dkT
988ali$gn
989alt
990b$order
991he$ight
992hs$pace
993is$map
994lo$ngdesc
995n$ame
996sr$c
997u$semap
998v$space
999w$idth
1000la$rger
1001p$df
1002cac$he
1003ti$tle
1004cl$ass
1005id
1006div
1007cap$tion
1008ce$ntered
1009svg
1010svgwh
1011do$wnloads
1012o$bject
1013ta$ble
1014no-v$space
1015f$ile
1016$!end
1017};
1018
1019
1020
1021/**	Keys for hint.
1022*/
1023dkChar const * const	hbhtml_hint_keys[] = {
1024$!string-table	macro=dkT
1025te$xt
1026ti$tle
1027$!end
1028};
1029
1030
1031
1032/**	Attributes for menu.
1033*/
1034dkChar const * const	hbhtml_menu_keys[] = {
1035$!string-table	macro=dkT
1036st$yle
1037su$b
1038$!end
1039};
1040
1041
1042
1043/**	Attributes for menuentry.
1044*/
1045dkChar const * const	hbhtml_menuentry_keys[] = {
1046$!string-table macro=dkT
1047i$tem
1048$!end
1049};
1050
1051
1052
1053/**	Attributes for index entry.
1054*/
1055dkChar const * const	hbhtml_index_keys[] = {
1056$!string-table	macro=dkT
1057t$ext
1058n$ame
1059ref$erence
1060$!end
1061};
1062
1063
1064
1065/**	Attributes for sourcefile special command.
1066*/
1067dkChar const * const	hbhtml_sourcef_keys[] = {
1068$!string-table	macro=dkT
1069f$ile
1070ty$pe
1071ti$tle
1072l$ineno
1073$!end
1074};
1075
1076
1077/**	Attributes for navigation.
1078*/
1079dkChar const * const	hbhtml_navi_keys[] = {
1080$!string-table	macro=dkT
1081t$oc
1082p$revious
1083n$ext
1084i$ndex
1085h$ome
1086$!end
1087};
1088
1089
1090
1091/**	Attributes for code special command.
1092*/
1093dkChar const * const	hbhtml_code_keys[] = {
1094$!string-table macro=dkT
1095e$nable
1096on
1097d$isable
1098of$f
1099t$oggle
1100l$ineno
1101$!end
1102};
1103
1104
1105
1106/**	Attribute for a.
1107*/
1108dkChar const * const    hbhtml_a_keys[] = {
1109$!string-table macro=dkT
1110te$xt
1111ti$tle
1112ac$cesskey
1113ch$arset
1114co$ords
1115href
1116hrefl$ang
1117na$me
1118onb$lur
1119onf$ocus
1120rel
1121rev
1122sh$ape
1123tab$index
1124tar$get
1125ty$pe
1126cl$ass
1127id
1128st$yle
1129ext$ernal
1130$!end
1131};
1132
1133
1134
1135/**	The menu styles.
1136*/
1137dkChar const * const	hbhtml_menu_styles[] = {
1138$!string-table	macro=dkT
1139r$everse
1140f$ull
1141c$urrent
1142t$op
1143$!end
1144};
1145
1146
1147
1148/**	Attributes allowed in date special.
1149*/
1150dkChar const * const	hbhtml_date_keys[] = {
1151$!string-table	macro=dkT
1152st$yle
1153$!end
1154};
1155
1156
1157
1158/**	Subcommands of img with not vertical space.
1159*/
1160dkChar const * const	hbhtml_no_vspace_suffixes[] = {
1161$!string-table	macro=dkT
1162tex
1163$!end
1164};
1165
1166
1167
1168/**	File name suffixes.
1169*/
1170dkChar const * const	hbhtml_image_filename_suffixes[] = {
1171$!string-table	macro=dkT
1172.png
1173.pdf
1174.svg
1175$!end
1176};
1177
1178
1179
1180/**	Check whether a character is useable in an URL.
1181	RFC 1630 and 3986 allow only 8-bit characters (octets)
1182	in URLs.
1183	@param	c32	Character to check.
1184	@return	1 for usable character, 0 otherwise.
1185*/
1186static
1187int
1188hbhtml_url_useable(dk3_c32_t c32)
1189{
1190  int		back = 1;
1191  $? "+ hbhtml_url_useable %lx %lu", dk3enc_uc_to_ul(c32), dk3enc_uc_to_ul(c32)
1192  if((dk3_c32_t)255 < c32) {
1193    back = 0;	/* NOT REPORTED */
1194  } $? "- hbhtml_url_useable %d", back
1195  return back;
1196}
1197
1198
1199
1200#if 0
1201/**	Check whether a character must be written hex-encoded to an URL.
1202	See RFC 3986 for reserved and unreserved characters.
1203	@param	c32	Character to check.
1204	@return	1 for hex-encoding, 0 for direct use.
1205*/
1206static
1207int
1208hbhtml_url_must_encode_normally(dk3_c32_t c32)
1209{
1210  int		 back = 1;
1211  char		 c;
1212  if(128UL > dk3enc_uc_to_ul(c32)) {
1213    c = (char)c32;
1214    c &= 0x7F;
1215    if(('a' <= c) && ('z' >= c)) {
1216      back = 0;
1217    } else {
1218      if(('A' <= c) && ('Z' >= c)) {
1219        back = 0;
1220      } else {
1221        if(('0' <= c) && ('9' >= c)) {
1222	  back = 0;
1223	} else {
1224	  switch(c) {
1225	    case '-': case '_': case '.': case '~': {
1226	      back = 0;
1227	    } break;
1228	  }
1229	}
1230      }
1231    }
1232  }
1233  return back;
1234}
1235#endif
1236
1237
1238
1239/**	Check whether a character must be written hex-encoded to an URL.
1240	See RFC 3986 for reserved and unreserved characters.
1241	@param	c32	Character to check.
1242	@return	1 for hex-encoding, 0 for direct use.
1243*/
1244static
1245int
1246hbhtml_url_must_encode(dk3_c32_t c32)
1247{
1248  int		 back = 0;
1249  $? "+ hbhtml_url_must_encode %lx %lu", dk3enc_uc_to_ul(c32), dk3enc_uc_to_ul(c32)
1250  if((dk3_c32_t)126 < c32) {
1251    back = 1;
1252  } else {
1253    if((dk3_c32_t)33 > c32) {
1254      back = 1;
1255    } else {
1256#if 0
1257      /*
1258      	2017-10-10	Removed. Must use &amp; instead of hex notation.
1259      */
1260      /*
1261      	2017-10-03	Recode ampersand.
1262      */
1263      if (0x00000026UL == c32) {
1264        back = 1;
1265      }
1266#endif
1267    }
1268  } $? "- hbhtml_url_must_encode %d", back
1269  return back;
1270}
1271
1272
1273
1274/**	Write one character in URL encoding, complain on problems.
1275	Unusable characters (not in octect range 0...0xFF)
1276	are UTF-8 encoded first and hex-converted for the URL.
1277	Some ASCII characters have special meaning in URL encoding
1278	(i.e. ampersand, question mark...). We assume the user
1279	intended to use these characters and specified the URL
1280	so we can use it "as is".
1281
1282	@param	job	Job structure.
1283	@param	c32	Character to print.
1284*/
1285static
1286void
1287hbhtml_url_c32(hb_job_t *job, dk3_c32_t c32)
1288{
1289  char		buf[16];	/* Buffer for hexadecimal notation */
1290  unsigned char	ub[16];		/* Buffer for UTF-8 notation */
1291  size_t	sz;		/* Number of bytes used in ub */
1292  size_t	i;		/* Traverse ub */
1293  $? "+ hbhtml_url_c32 0x%lx = %lu", dk3enc_uc_to_ul(c32), dk3enc_uc_to_ul(c32)
1294  if(hbhtml_url_useable(c32)) {
1295    if(hbhtml_url_must_encode(c32)) {
1296      buf[0] = '%';
1297      sprintf(&(buf[1]), "%02X", (unsigned)c32);
1298      fputs(buf, job->of);
1299    } else {
1300      if ((dk3_c32_t)'&' == c32) {
1301        fputc((((char)c32) & ((char)0x7F)), job->of);
1302      }
1303      else {
1304        fputc((((char)c32) & ((char)0x7F)), job->of);
1305      }
1306    }
1307  } else {
1308    job->non_url = 1;
1309    sz = dk3enc_uc2utf8(c32, ub, sizeof(ub));
1310    for(i = 0; i < sz; i++) {
1311      buf[0] = '%';
1312      sprintf(&(buf[1]), "%02X", ((unsigned)(ub[i]) & 0x00FFU));
1313      fputs(buf, job->of);
1314    }
1315  } $? "- hbhtml_url_c32"
1316}
1317
1318
1319
1320/**	Write shipout buffer in URL encoding.
1321	@param	job	Job  structure.
1322*/
1323static
1324void
1325hbhtml_url_flush_shipout(hb_job_t *job)
1326{
1327#if DK3_CHAR_SIZE > 1
1328#if DK3_CHAR_SIZE > 2
1329  size_t		 i;
1330#else
1331  dk3_c16_t const	*sp;		/* Source character pointer */
1332  dk3_c32_t		 c32;		/* 32-bit character */
1333  size_t		 sl;		/* Remaining source string length */
1334  size_t		 used;		/* Number of source chars used */
1335#endif
1336#else
1337  unsigned char const	*sp;		/* Source character pointer */
1338  dk3_c32_t		 c32;		/* 32-bit character */
1339  size_t		 sl;		/* Remaining source string length */
1340  size_t		 used;		/* Number of source chars used */
1341  size_t		 i;		/* Process all source chars */
1342  unsigned char		 c;		/* Current source char to process */
1343#endif
1344  $? "+ hbhtml_url_flush_shipout"
1345  if(job->shipused) {
1346    job->non_url = 0;
1347    if(job->shipused < job->bs) {
1348      (job->shipbuffer)[job->shipused] = dkT('\0');
1349#if DK3_CHAR_SIZE > 1
1350#if DK3_CHAR_SIZE > 2
1351	for(i = 0; i < (job->shipused); i++) {
1352	  hbhtml_url_c32(job, (job->shipused)[i]);
1353	}
1354#else
1355	sp = (dk3_c16_t const *)(job->shipbuffer);
1356	sl = job->shipused;
1357	while(sl > 0) {
1358	  used = 0;
1359	  if(dk3enc_utf162uc(&c32, sp, sl, &used)) {
1360	    hbhtml_url_c32(job, c32);
1361	    if(used > 0) {
1362	      if(sl >= used) {
1363	        sl = sl - used;
1364		sp = &(sp[used]);
1365	      } else {
1366	        sl = 0;
1367		/* ERROR: Decoding */
1368		dk3app_log_i1(job->app, DK3_LL_ERROR, 119);
1369	      }
1370	    } else {
1371	      sl = 0;
1372	      /* ERROR: Decoding */
1373	      dk3app_log_i1(job->app, DK3_LL_ERROR, 119);
1374	    }
1375	  } else {
1376	    sl = 0;
1377	    /* ERROR: Decoding */
1378	    dk3app_log_i1(job->app, DK3_LL_ERROR, 119);
1379	  }
1380	}
1381#endif
1382#else
1383	$? "+ shipout ASCII"
1384        if(dk3app_get_encoding(job->app) == DK3_ENCODING_UTF8) {
1385	  $? ". UTF-8"
1386	  sp = (unsigned char const *)(job->shipbuffer);
1387	  sl = job->shipused;
1388	  while(sl > 0) {
1389	    used = 0;
1390	    if(dk3enc_utf82uc(&c32, sp, sl, &used)) {
1391	      hbhtml_url_c32(job, c32);
1392	      if(used > 0) {
1393	        if(sl >= used) {
1394		  sl = sl - used;
1395		  sp = &(sp[used]);
1396		} else {
1397		  sl = 0;
1398		  /* ERROR: Decoding problem */
1399		  dk3app_log_i1(job->app, DK3_LL_ERROR, 118);
1400		}
1401	      } else {
1402	        sl = 0;
1403		/* ERROR: Decoding problem */
1404		dk3app_log_i1(job->app, DK3_LL_ERROR, 118);
1405	      }
1406	    } else {
1407	      sl = 0;
1408	      /* ERROR: Decoding problem */
1409	      dk3app_log_i1(job->app, DK3_LL_ERROR, 118);
1410	    }
1411	  }
1412	} else {
1413	  sp = (unsigned char const *)(job->shipbuffer);
1414	  for(i = 0; i < (job->shipused); i++) {
1415	    c = sp[i];
1416	    c32 = (dk3_c32_t)c;
1417	    c32 &= 0x000000FFUL;
1418	    hbhtml_url_c32(job, c32);
1419	  }
1420	}
1421	$? "- shipout ASCII"
1422#endif
1423      if(job->non_url) {
1424        /* WARNING: Non-URL characters in string! */
1425	dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 21, 22, job->shipbuffer);
1426      }
1427    }
1428    job->non_url = 0;
1429  }
1430  job->shipused = 0;
1431  $? "- hbhtml_url_flush_shipout"
1432}
1433
1434
1435
1436void
1437hbhtml_c32(hb_job_t *job, dk3_c32_t c32)
1438{
1439  unsigned char	ub[16];		/* Buffer for UTF-8 conversion */
1440  size_t	sz;		/* Number of chars used in ub */
1441  size_t	i;		/* Process all chars in ub */
1442  char		c;		/* Current char from ub */
1443  $? "+ hbhtml_c32 0x%lx %lu", dk3enc_uc_to_ul(c32), dk3enc_uc_to_ul(c32)
1444  if(128UL > dk3enc_uc_to_ul((unsigned long)c32)) {
1445    c = (((char)c32) & ((char)0x7F));
1446    switch(c) {
1447      case '\n': {
1448        fputc('\n', job->of);
1449      } break;
1450      case '&': {
1451        if((job->options) & HB_JOB_OPT_CODE) {
1452	  fputs(hbhtml_translation_table[38], job->of);
1453	} else {
1454	  if(((job->currentnode)->options) & HB_NODE_OPT_REPLACE_AMPERSAND) {
1455	    fputs(hbhtml_translation_table[38], job->of);
1456	  } else {
1457	    fputc('&', job->of);
1458	  }
1459	}
1460      } break;
1461      case '<': {
1462        if((job->options) & HB_JOB_OPT_CODE) {
1463	  fputs(hbhtml_translation_table[60], job->of);
1464	} else {
1465	  fputc('<', job->of);
1466	}
1467      } break;
1468      case '>': {
1469        if((job->options) & HB_JOB_OPT_CODE) {
1470	  fputs(hbhtml_translation_table[62], job->of);
1471	} else {
1472	  fputc('>', job->of);
1473	}
1474      } break;
1475      case '"': {
1476        if((job->options) & HB_JOB_OPT_CODE) {
1477	  fputs(hbhtml_translation_table[34], job->of);
1478	} else {
1479	  fputc('"', job->of);
1480	}
1481      } break;
1482      case '%': {
1483        fputc('%', job->of);	/* PROBABLY NEED A CODE */
1484      } break;
1485      default: {
1486        sz = (size_t)c32;
1487	sz &= (size_t)0x007FU;
1488	if(hbhtml_translation_table[sz]) {
1489	  fputs(hbhtml_translation_table[sz], job->of);
1490	} else {
1491	  fprintf(job->of, "&#x%02lx;", dk3enc_uc_to_ul((unsigned long)c32));
1492	}
1493      } break;
1494    }
1495  } else {
1496    if(((dk3_c32_t)256) > c32) {
1497      sz = (size_t)c32;
1498      sz &= (size_t)0x00FFU;
1499      if(hbhtml_translation_table[sz]) {
1500        fputs(hbhtml_translation_table[sz], job->of);
1501      } else {
1502	fprintf(job->of, "&#x%02lx;", dk3enc_uc_to_ul((unsigned long)c32));
1503      }
1504    } else {
1505      if(HB_CS_UTF_8 == job->cs) {
1506        sz = dk3enc_uc2utf8(c32, ub, sizeof(ub));
1507	for(i = 0; i < sz; i++) { fputc(ub[i], job->of); }
1508      } else {
1509        if(((dk3_c32_t)0x010000UL) > c32) {
1510	  fprintf(job->of, "&#x%04lx;", dk3enc_uc_to_ul((unsigned long)c32));
1511	} else {
1512	  if(((dk3_c32_t)0x01000000UL) > c32) {
1513	    fprintf(job->of, "&#x%06lx;", dk3enc_uc_to_ul((unsigned long)c32));
1514	  } else {
1515	    fprintf(job->of, "&#x%08lx;", dk3enc_uc_to_ul((unsigned long)c32));
1516	  }
1517	}
1518      }
1519    }
1520  } $? "- hbhtml_c32"
1521}
1522
1523
1524
1525/**	Flush the shipout buffer.
1526	@param	job	Job structure.
1527*/
1528static
1529void
1530hbhtml_flush_shipout(hb_job_t *job)
1531{
1532#if DK3_CHAR_SIZE > 1
1533#if DK3_CHAR_SIZE > 2
1534  size_t		 i;		/* Process all characters */
1535#else
1536  dk3_c16_t const	*sp;		/* Current source start */
1537  dk3_c32_t		 c32;		/* Current 32-bit char to process */
1538  size_t		 sl;		/* Remaining source length */
1539  size_t		 used;		/* Source chars used */
1540#endif
1541#else
1542  unsigned char const	*sp;		/* Current source start */
1543  dk3_c32_t		 c32;		/* Current 32-bit char to process */
1544  size_t		 sl;		/* Remaining source length */
1545  size_t		 used;		/* Source chars used */
1546  size_t		 i;		/* Process all chars from buffer */
1547  unsigned char		 c;		/* Current char to process */
1548#endif
1549  $? "+ hbhtml_flush_shipout"
1550  if(job->shipused) {
1551    if(job->shipused < job->bs) {
1552      (job->shipbuffer)[job->shipused] = dkT('\0');
1553#if DK3_CHAR_SIZE > 1
1554#if DK3_CHAR_SIZE > 2
1555	for(i = 0; i < (job->shipused); i++) {
1556	  hbhtml_c32(job, (job->shipused)[i]);
1557	}
1558#else
1559	sp = (dk3_c16_t const *)(job->shipbuffer);
1560	sl = job->shipused;
1561	while(sl > 0) {
1562	  used = 0;
1563	  if(dk3enc_utf162uc(&c32, sp, sl, &used)) {
1564	    hbhtml_c32(job, c32);
1565	    if(used > 0) {
1566	      if(sl >= used) {
1567	        sl = sl - used;
1568		sp = &(sp[used]);
1569	      } else {
1570	        sl = 0;
1571		/* ERROR: Decoding */
1572		dk3app_log_i1(job->app, DK3_LL_ERROR, 119);
1573	      }
1574	    } else {
1575	      sl = 0;
1576	      /* ERROR: Decoding */
1577	      dk3app_log_i1(job->app, DK3_LL_ERROR, 119);
1578	    }
1579	  } else {
1580	    sl = 0;
1581	    /* ERROR: Decoding */
1582	    dk3app_log_i1(job->app, DK3_LL_ERROR, 119);
1583	  }
1584	}
1585#endif
1586#else
1587	$? "+ shipout ASCII"
1588        if(dk3app_get_encoding(job->app) == DK3_ENCODING_UTF8) {
1589	  $? ". UTF-8"
1590	  sp = (unsigned char const *)(job->shipbuffer);
1591	  sl = job->shipused;
1592	  while(sl > 0) {
1593	    used = 0;
1594	    if(dk3enc_utf82uc(&c32, sp, sl, &used)) {
1595	      hbhtml_c32(job, c32);
1596	      if(used > 0) {
1597	        if(sl >= used) {
1598		  sl = sl - used;
1599		  sp = &(sp[used]);
1600		} else {
1601		  sl = 0;
1602		  /* ERROR: Decoding problem */
1603		  dk3app_log_i1(job->app, DK3_LL_ERROR, 118);
1604		}
1605	      } else {
1606	        sl = 0;
1607		/* ERROR: Decoding problem */
1608		dk3app_log_i1(job->app, DK3_LL_ERROR, 118);
1609	      }
1610	    } else {
1611	      sl = 0;
1612	      /* ERROR: Decoding problem */
1613	      dk3app_log_i1(job->app, DK3_LL_ERROR, 118);
1614	    }
1615	  }
1616	} else {
1617	  sp = (unsigned char const *)(job->shipbuffer);
1618	  for(i = 0; i < (job->shipused); i++) {
1619	    c = sp[i];
1620	    c32 = (dk3_c32_t)c;
1621	    c32 &= 0x000000FFUL;
1622	    hbhtml_c32(job, c32);
1623	  }
1624	}
1625	$? "- shipout ASCII"
1626#endif
1627    }
1628  }
1629  job->shipused = 0;
1630  $? "- hbhtml_flush_shipout"
1631}
1632
1633
1634
1635/**	Add one character to shipout buffer.
1636	@param	job	Job structure.
1637	@param	c	Character to add.
1638	@return	1 on success, 0 on error.
1639*/
1640static
1641int
1642hbhtml_shipout(hb_job_t *job, dkChar c)
1643{
1644  int		 back = 1;
1645  $? "+ hbhtml_shipout"
1646  if((job->shipused) < (job->bs - 1)) {
1647    (job->shipbuffer)[job->shipused] = c;
1648    job->shipused += 1;
1649    if(job->shipused >= job->bs) {
1650      back = 0;
1651    }
1652  } else {
1653    back = 0;
1654  } $? "- hbhtml_shipout %d", back
1655  return back;
1656}
1657
1658
1659
1660/**	Add one character to special command buffer.
1661	@param	job	Job structure.
1662	@param	c	Character to add.
1663	@return	1 on success, 0 on error.
1664*/
1665static
1666int
1667hbhtml_add_to_special(hb_job_t *job, dkChar c)
1668{
1669  int		 back = 1;
1670  $? "+ hbhtml_add_to_special"
1671  if((job->spused) < (job->bs - 1)) {
1672    (job->special)[job->spused] = c;
1673    job->spused += 1;
1674    if(job->spused >= job->bs) {
1675      back = 0;
1676    }
1677  } else {
1678    back = 0;
1679  } $? "- hbhtml_add_to_special %d", back
1680  return back;
1681}
1682
1683
1684
1685/**	Classify a character for processing by state machine 1.
1686	@param	c	Character to classify.
1687	@return	Classification result.
1688*/
1689static
1690int
1691hbhtml_classify_for_stm1(dkChar c)
1692{
1693  int		back = STM1_I_ANY;
1694  switch(c) {
1695    case dkT('%'): {
1696      back = STM1_I_PERCENT;
1697    } break;
1698    case dkT('\\'): {
1699      back = STM1_I_BS;
1700    } break;
1701  }
1702  return back;
1703}
1704
1705
1706
1707/**	Write text to HTML output file.
1708	@param	job	Job structure.
1709	@param	text	Text to write.
1710	@return	1 on success, 0 on error.
1711*/
1712int
1713hbhtml_output_for_text(hb_job_t *job, dkChar const *text)
1714{
1715  size_t	sz;
1716#if VERSION_BEFORE_20131219
1717  int		oldcode;
1718#endif
1719  int		back	= 0;
1720  $? "+ hbhtml_output_for_text \"%!ds\"", TR_STR(text)
1721  if((job) && (text)) {
1722    sz = dk3str_len(text);
1723    if(sz < job->bs) {			$? ". size ok"
1724#if VERSION_BEFORE_20131219
1725      oldcode = (((job->options) & HB_JOB_OPT_CODE) ? 1 : 0);
1726      job->options &= (~(HB_JOB_OPT_CODE));
1727#endif
1728      dk3str_cpy(job->shipbuffer, text);
1729      job->shipused = sz;		$? ". text copied to shipout buffer"
1730      hbhtml_flush_shipout(job);	$? ". flush completed"
1731      back = 1;
1732      job->spused = 0;
1733      job->shipused = 0;
1734#if VERSION_BEFORE_20131219
1735      if(oldcode) {
1736        job->options |= HB_JOB_OPT_CODE;
1737      }
1738#endif
1739    }
1740  } $? "- hbhtml_output_for_text %d", back
1741  return back;
1742}
1743
1744
1745
1746/**	Write text to URL output file.
1747	@param	job	Job structure.
1748	@param	text	Text to write.
1749	@return	1 on success, 0 on error.
1750*/
1751int
1752hbhtml_url_output_for_text(hb_job_t *job, dkChar const *text)
1753{
1754  size_t		 sz;		/* Text length */
1755  int			 back = 0;
1756  $? "+ hbhtml_url_output_for_text \"%!ds\"", TR_STR(text)
1757  if((job) && (text)) {
1758    sz = dk3str_len(text);
1759    if(sz < job->bs) {
1760      dk3str_cpy(job->shipbuffer, text);
1761      job->shipused = sz;
1762      hbhtml_url_flush_shipout(job);
1763      back = 1;
1764      job->spused = 0;
1765      job->shipused = 0;
1766    }
1767  } $? "- hbhtml_url_output_for_text %d", back
1768  return back;
1769}
1770
1771
1772
1773/**	Process the title special command.
1774	@param	job	Job structure.
1775	@return	1 on success, 0 on error.
1776*/
1777static
1778int
1779hbhtml_process_sp_title(hb_job_t *job)
1780{
1781  int			 back = 0;
1782  $? "+ hbhtml_process_sp_title"
1783  if(job->rootnode) {			$? ". have node"
1784    if((job->rootnode)->title) {		$? ". have title"
1785      if(hbhtml_output_for_text(job, (job->rootnode)->title)) {
1786        back = 1;
1787      }
1788    }
1789  } $? "- hbhtml_process_sp_title %d", back
1790  return back;
1791}
1792
1793
1794
1795/**	Build new file name if necessary.
1796	@param	oldname		Existing file name.
1797	@param	fnb		File name buffer.
1798	@param	fnbsz		Size of file name buffer.
1799	@param	sc		Sub command.
1800	@param	newsuffix	New file name suffix.
1801	@param	job		Job structure.
1802	@param	ecp		Error code variable pointer.
1803*/
1804static
1805dkChar *
1806hbhtml_sc_fn(
1807  dkChar		*oldname,
1808  dkChar		*fnb,
1809  size_t		 fnbsz,
1810  dkChar		*sc,
1811  dkChar const		*newsuffix,
1812  hb_job_t		*job,
1813  int			*ecp
1814)
1815{
1816  dk3_stat_t	 stb;
1817  dkChar	*back;
1818  dkChar	*sp;
1819  $? "+ hbhtml_sc_fn \"%!ds\"", TR_STR(oldname)
1820  $? ". sc=\"%!ds\"", TR_STR(sc)
1821  $? ". newsuffix=\"%!ds\"", TR_STR(newsuffix)
1822  back = oldname;
1823  if(!(back)) {
1824    /*	Remove suffix if it matches subcommand.
1825    */
1826    sp = dk3str_get_suffix(fnb);
1827    if(sp) {
1828      if(0 == dk3str_casecmp(&(sp[1]), sc)) {
1829        *sp = dkT('\0');
1830      }
1831    }
1832    /*	Add new suffix.
1833    */
1834    if(fnbsz > (dk3str_len(fnb) + dk3str_len(newsuffix))) {
1835      dk3str_cat(fnb, newsuffix);
1836      /*	If file exists, use file name from fnb.
1837      */
1838      if(dk3sf_stat_app(&stb, fnb, NULL)) {
1839        switch(stb.ft) {
1840	  case DK3_FT_REGULAR: {
1841	    back = fnb;
1842	  } break;
1843	  default: {
1844	    /* ERROR: Not a regular file */
1845	    dk3app_log_i3(job->app, DK3_LL_ERROR, 255, 256, fnb);
1846	  } break;
1847	}
1848      } else {
1849        dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 84, 85, fnb);
1850      }
1851    } else {
1852      /* ERROR: File name too long */
1853      dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, fnb);
1854      if(ecp) { *ecp = 1; }
1855    }
1856  }  $? "- hbhtml_sc_fn \"%!ds\"", TR_STR(back)
1857  return back;
1858}
1859
1860
1861
1862/**	Process image special command.
1863	@param	job		Job structure.
1864	@param	pa		Pointer to arguments.
1865	@param	sc		Subcommand, may be NULL.
1866	@return	1 on success, 0 on error.
1867*/
1868static
1869int
1870hbhtml_process_sp_image(hb_job_t *job, dkChar *pa, dkChar *sc)
1871{
1872  dkChar		 svgnbuf[DK3_MAX_PATH];	/* SVG file name */
1873  dkChar		 pdfnbuf[DK3_MAX_PATH];	/* PDF file name */
1874  dkChar		 pngnbuf[DK3_MAX_PATH];	/* PNG file name */
1875  dk3_key_value_t	 kv[30];		/* Parameters */
1876  dkChar		 wbuf[64];		/* Width buffer */
1877  dkChar		 hbuf[64];		/* Height buffer */
1878  dkChar 		*align	= NULL;		/* Align specified */
1879  dkChar		*alt	= NULL;		/* Alternative text */
1880  dkChar		*border	= NULL;		/* Border specified */
1881  dkChar		*height	= NULL;		/* Height specified */
1882  dkChar		*hspace	= NULL;		/* Horiz space specified */
1883  dkChar		*longdesc	= NULL;	/* Longdesc specified */
1884  dkChar		*name	= NULL;		/* Name. */
1885  dkChar		*src	= NULL;		/* Image file name */
1886  dkChar		*usemap	= NULL;		/* Usemap text */
1887  dkChar		*vspace	= NULL;		/* Vertical space specified */
1888  dkChar		*width	= NULL;		/* Width specified */
1889  dkChar		*larger	= NULL;		/* File name large resolution */
1890  dkChar		*pdf	= NULL;		/* PDF file name */
1891  dkChar		*clptr	= NULL;		/* Image CSS class */
1892  dkChar		*idptr	= NULL;		/* Image CSS id */
1893  dkChar		*title	= NULL;		/* Image title */
1894  dkChar		*capt	= NULL;		/* Image caption */
1895  dkChar		*svg	= NULL;		/* SVG file name */
1896  dkChar		*fnptr	= NULL;		/* The file argument */
1897  unsigned long		 xw	= 0UL;		/* Width found */
1898  unsigned long		 xh	= 0UL;		/* Height found */
1899  size_t		 sz;			/* Size of key value array */
1900  size_t		 i;			/* Traverse key value array */
1901  int			 cache	= 0;		/* Flag: Cache width+height */
1902  int			 ismap	= 0;		/* Flag: Write isamp attrib */
1903  int			 back	= 0;		/* Result */
1904  int			 tispec	= 0;		/* Flag: Title specified */
1905  int			 caspec = 0;		/* Flag: Capction specified */
1906  int			 svgwh	= 0;		/* Flag: For SVG width+height */
1907  int			 dodiv	= 0;		/* Img in div(1) or table(2) */
1908  int			 center	= 0;		/* Flag: Center image */
1909  int			 downlo	= 0;		/* Flag: Create download div */
1910  int			 haveli	= 0;		/* Already have written link */
1911  int			 crobj	= 1;		/* Flag: Create object */
1912  int			 novsp	= 0;		/* Flag: No vertical space. */
1913  int			 fnerr	= 0;		/* Flag: Error related file */
1914  $? "+ hbhtml_process_sp_image pa=\"%!ds\" sc=\"%!ds\"", TR_STR(pa), TR_STR(sc)
1915  sz = DK3_SIZEOF(kv,dk3_key_value_t);
1916  svgnbuf[0] = pdfnbuf[0] = pngnbuf[0] = dkT('\0');
1917  svgwh = job->svgwhdef;
1918  if(dk3str_to_key_value(kv, &sz, pa, job->app)) {
1919    $? ". sz = %u", (unsigned)sz
1920    if(sc) {		$? ". sub command specified"
1921      tispec = 1;
1922      downlo = 1;
1923      dodiv  = 2;
1924      if(-1 < dk3str_array_index(hbhtml_no_vspace_suffixes, sc, 0)) {
1925        novsp = 1;
1926      }
1927    }
1928    for(i = 0; i < sz; i++) {
1929      if(kv[i].key) {				$? ". key = \"%!ds\"", kv[i].key
1930        switch(dk3str_array_abbr(hbhtml_img_keys, kv[i].key, dkT('$'), 0)) {
1931	  case 0: {			$? ". align"
1932	    align = kv[i].val;
1933	  } break;
1934	  case 1: {			$? ". alt"
1935	    alt = kv[i].val;
1936	  } break;
1937	  case 2: {			$? ". border"
1938	    border = kv[i].val;
1939	  } break;
1940	  case 3: {			$? ". height"
1941	    height = kv[i].val;
1942	  } break;
1943	  case 4: {			$? ". hspace"
1944	    hspace = kv[i].val;
1945	  } break;
1946	  case 5: {	/* ismap */	$? ". ismap"
1947	    if(kv[i].val) {
1948	      if(dk3str_is_bool(kv[i].val)) {
1949	        if(dk3str_is_on(kv[i].val)) {
1950		  ismap = 1;
1951		} else {
1952		  ismap = 0;
1953		}
1954	      }
1955	    } else {
1956	      ismap = 1;
1957	    }
1958	  } break;
1959	  case 6: {				$? ". longdesc"
1960	    longdesc = kv[i].val;
1961	  } break;
1962	  case 7: {				$? ". name"
1963	    name = kv[i].val;
1964	  } break;
1965	  case 8: {				$? ". src"
1966	    src = kv[i].val;
1967	  } break;
1968	  case 9: {				$? ". usemap"
1969	    usemap = kv[i].val;
1970	  } break;
1971	  case 10: {				$? ". vspace"
1972	    vspace = kv[i].val;
1973	  } break;
1974	  case 11: {				$? ". width"
1975	    width = kv[i].val;
1976	  } break;
1977	  case 12: {	$? ". set larger to \"%!ds\"", TR_STR(kv[i].val)
1978	    larger = kv[i].val;
1979	  } break;
1980	  case 13: {				$? ". pdf"
1981	    pdf = kv[i].val;
1982	  } break;
1983	  case 14: {	/* caching */		$? ". cache"
1984	    if(kv[i].val) {
1985	      if(dk3str_is_bool(kv[i].val)) {
1986	        if(dk3str_is_on(kv[i].val)) {
1987		  cache = 1;
1988		} else {
1989		  cache = 0;
1990		}
1991	      }
1992	    } else {
1993	      cache = 1;
1994	    }
1995	  } break;
1996	  case 15: {		$? ". title = \"%!ds\"", TR_STR(kv[i].val)
1997	    title = kv[i].val;
1998	    tispec = 1;
1999	  } break;
2000	  case 16: {		$? ". class = \"%!ds\"", TR_STR(kv[i].val)
2001	    clptr = kv[i].val;
2002	  } break;
2003	  case 17: {		$? ". id = \"%!ds\"", TR_STR(kv[i].val)
2004	    idptr = kv[i].val;
2005	  } break;
2006	  case 18: {	/* div */
2007	    if(kv[i].val) {
2008	      if(dk3str_is_bool(kv[i].val)) {
2009	        dodiv = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
2010	      } else {
2011	        /* ERROR: Not boolean */
2012		dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
2013	      }
2014	    } else {
2015	      dodiv = 1;
2016	    }
2017	  } break;
2018	  case 19: {	/* caption */
2019	    caspec = 1;
2020	    capt = kv[i].val;
2021	  } break;
2022	  case 20: {	/* centered */
2023	    if(kv[i].val) {
2024	      if(dk3str_is_bool(kv[i].val)) {
2025	        center = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
2026	      } else {
2027	        /* ERROR: Not boolean */
2028		dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
2029	      }
2030	    } else {
2031	      center = 1;
2032	    }
2033	  } break;
2034	  case 21: {	/* svg */
2035	    svg = kv[i].val;
2036	  } break;
2037	  case 22: {	/* svgwh */
2038	    if(kv[i].val) {
2039	      if(dk3str_is_bool(kv[i].val)) {
2040	        svgwh = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
2041	      } else {
2042	        /* ERROR: Not boolean */
2043		dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
2044	      }
2045	    } else {
2046	      svgwh = 1;
2047	    }
2048	  } break;
2049	  case 23: {	/* downloads */
2050	    if(kv[i].val) {
2051	      if(dk3str_is_bool(kv[i].val)) {
2052	        downlo = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
2053	      } else {
2054	        /* ERROR: Not boolean */
2055		dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
2056	      }
2057	    } else {
2058	      downlo = 1;
2059	    }
2060	  } break;
2061	  case 24: {
2062	    if(kv[i].val) {
2063	      if(dk3str_is_bool(kv[i].val)) {
2064	        crobj = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
2065	      } else {
2066	        /* ERROR: Not boolean */
2067		dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
2068	      }
2069	    } else {
2070	      crobj = 1;
2071	    }
2072	  } break;
2073	  case 25: {
2074	    if(kv[i].val) {
2075	      if(dk3str_is_bool(kv[i].val)) {
2076	        if(dk3str_is_on(kv[i].val)) {
2077		  if(1 == dodiv) {
2078		    /* Warning: Overwriting div by table */
2079		    dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 66);
2080		  }
2081		  dodiv = 2;
2082		} else {
2083		  if(1 != dodiv) {
2084		    dodiv = 0;
2085		  }
2086		}
2087	      } else {
2088	        dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
2089	      }
2090	    } else {
2091	      if(1 == dodiv) {
2092	        /* Warning: Overwriting div by table */
2093		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 66);
2094	      }
2095	      dodiv = 2;
2096	    }
2097	  } break;
2098	  case 26: {
2099	    if(kv[i].val) {
2100	      if(dk3str_is_bool(kv[i].val)) {
2101	        novsp = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
2102	      } else {
2103	        /* ERROR: Not a boolean */
2104	        dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
2105	      }
2106	    } else {
2107	      novsp = 1;
2108	    }
2109	  } break;
2110	  case 27: {	$? ". file argument"
2111	    if(kv[i].val) {
2112	      if(fnptr) {	$? ". overwriting"
2113	        /* WARNING: Overwriting file setting! */
2114		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 86);
2115	      }
2116	      fnptr = kv[i].val;
2117	    } else {		$? "! missing file name"
2118	      /* ERROR: No file name specified */
2119	    }
2120	  } break;
2121	}
2122      }
2123    }
2124    if(fnptr) {		$? ". file argument specified"
2125      if(dk3str_len(fnptr) < DK3_SIZEOF(svgnbuf,dkChar)) {
2126        dk3str_cpy(svgnbuf, fnptr);
2127	dk3str_cpy(pdfnbuf, fnptr);
2128	dk3str_cpy(pngnbuf, fnptr);
2129	src = hbhtml_sc_fn(
2130	  src, pngnbuf, DK3_SIZEOF(pngnbuf,dkChar), sc,
2131	  hbhtml_image_filename_suffixes[0], job, &fnerr
2132	);
2133	svg = hbhtml_sc_fn(
2134	  svg, svgnbuf, DK3_SIZEOF(svgnbuf,dkChar), sc,
2135	  hbhtml_image_filename_suffixes[2], job, &fnerr
2136	);
2137	pdf = hbhtml_sc_fn(
2138	  pdf, pdfnbuf, DK3_SIZEOF(pdfnbuf,dkChar), sc,
2139	  hbhtml_image_filename_suffixes[1], job, &fnerr
2140	);
2141      } else {
2142        /* ERROR: Name too long */
2143        dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, fnptr);
2144	fnerr = 1;
2145      }
2146    }
2147    if(src) {
2148      if(!hbtool_check_filename(job, src)) { src = NULL; }
2149    }
2150    if(src) {
2151      if(0 == fnerr) { back = 1; }
2152      if(larger) {	$? ". larger=\"%!ds\"", larger
2153        if(!hbtool_check_filename(job, larger)) { larger = NULL; }
2154      }
2155      if(pdf) {		$? ". pdf=\"%!ds\"", pdf
2156        if(!hbtool_check_filename(job, pdf)) { pdf = NULL; }
2157      }
2158      if(svg) {		$? ". svg=\"%!ds\"", svg
2159        if(!hbtool_check_filename(job, svg)) { svg = NULL; }
2160      }
2161      if(tispec) {
2162        if(!(title)) { title = alt; }
2163	if(!(title)) { title = src; }
2164      }
2165      if(caspec) {
2166        if(!(capt)) { capt = title; }
2167	if(!(capt)) { capt = alt; }
2168      }
2169      if(!((width) && (height))) {
2170        if(hbimgdim_find(job, src, &xw, &xh, cache)) {
2171	  if((xw) && (xh)) {
2172#if VERSION_BEFORE_20140716
2173	    dk3sf_sprintf3(wbuf, dkT("%lu"), xw);
2174	    dk3sf_sprintf3(hbuf, dkT("%lu"), xh);
2175	    if(!(width)) { width = wbuf; }
2176	    if(!(height)) { height = hbuf; }
2177#else
2178	    if(!(width)) {
2179	      if (dk3ma_um_to_string(wbuf,DK3_SIZEOF(wbuf,dkChar),(dk3_um_t)xw))
2180	      {
2181	        width = wbuf;
2182	      }
2183	    }
2184	    if(!(height)) {
2185	      if (dk3ma_um_to_string(hbuf,DK3_SIZEOF(hbuf,dkChar),(dk3_um_t)xh))
2186	      {
2187	        height = hbuf;
2188	      }
2189	    }
2190#endif
2191	  }
2192	}
2193      }
2194      if(capt) { if(!(dodiv)) { dodiv = 2;  } }
2195      if(downlo) { if(!(dodiv)) { dodiv = 2; } }
2196
2197      /*	Output creation starts here.
2198      */
2199      if(dodiv) {
2200        if(2 == dodiv) {
2201	  /* div		Open outer div */
2202	  fputs(hbhtml_c8_kw[((novsp) ? (145) : (109))], job->of);
2203	  /* table */
2204	  if (0 != job->tabsum) {
2205	    fputs(hbhtml_c8_kw[138], job->of);
2206	    hbhtml_output_for_text(job, (job->msg)[65]);
2207	    fputs(hbhtml_c8_kw[143], job->of);
2208	  } else {
2209	    fputs(hbhtml_c8_kw[159], job->of);
2210	  }
2211	  /* tr */
2212	  fputs(hbhtml_c8_kw[139], job->of);
2213	  /* td */
2214	  fputs(hbhtml_c8_kw[140], job->of);
2215	} else {
2216          /* div		Open outer div */
2217	  fputs(hbhtml_c8_kw[109], job->of);
2218	  /* div		Open image div */
2219	  fputs(hbhtml_c8_kw[(center) ? 111 : 110], job->of);
2220	}
2221      }
2222
2223      /*	SVG object start.
2224      */
2225      if((svg) && (crobj)) {
2226        /* Opening object tag */
2227	fputs(hbhtml_c8_kw[114], job->of);
2228	if(!hbhtml_url_output_for_text(job, svg)) { back = 0; }
2229	if((svgwh) && (width) && (height)) {
2230	  fputs(hbhtml_c8_kw[71], job->of);
2231	  if(!hbhtml_url_output_for_text(job, width)) { back = 0; }
2232	  fputs(hbhtml_c8_kw[72], job->of);
2233	  if(!hbhtml_url_output_for_text(job, height)) { back = 0; }
2234	}
2235	fputs(hbhtml_c8_kw[51], job->of);
2236	fputs(hbhtml_c8_kw[0], job->of);
2237	/* Object param */
2238	fputs(hbhtml_c8_kw[115], job->of);
2239	if(!hbhtml_url_output_for_text(job, svg)) { back = 0; }
2240	fputs(hbhtml_c8_kw[51], job->of);
2241	fputs(hbhtml_c8_kw[0], job->of);
2242      }
2243
2244      /* Image
2245      */
2246      fputs(hbhtml_c8_kw[2], job->of);
2247      fputs(hbhtml_c8_kw[4], job->of);
2248      if(!hbhtml_url_output_for_text(job, src)) {
2249        back = 0;
2250      }
2251      fputs(hbhtml_c8_kw[5], job->of);
2252      if(name) {
2253        fputs(hbhtml_c8_kw[58], job->of);
2254	if(!hbhtml_output_for_text(job, name)) {
2255	  back = 0;
2256	}
2257	fputs(hbhtml_c8_kw[5], job->of);
2258      }
2259      fputs(hbhtml_c8_kw[6], job->of);
2260      if(alt) {
2261        if(!hbhtml_output_for_text(job, alt)) {
2262	  back = 0;
2263	}
2264      }
2265      fputs(hbhtml_c8_kw[5], job->of);
2266      if(title) {
2267        fputs(hbhtml_c8_kw[7], job->of);
2268	if(!hbhtml_output_for_text(job, title)) {
2269	  back = 0;
2270	}
2271	fputs(hbhtml_c8_kw[5], job->of);
2272      }
2273      if(longdesc) {
2274        fputs(hbhtml_c8_kw[59], job->of);
2275	if(!hbhtml_url_output_for_text(job, longdesc)) {
2276	  back = 0;
2277	}
2278	fputs(hbhtml_c8_kw[5], job->of);
2279      }
2280      if((width) && (height)) {
2281        fputs(hbhtml_c8_kw[9], job->of);
2282	if(!hbhtml_url_output_for_text(job, width)) {
2283	  back = 0;
2284	}
2285	fputs(hbhtml_c8_kw[5], job->of);
2286	fputs(hbhtml_c8_kw[10], job->of);
2287	if(!hbhtml_url_output_for_text(job, height)) {
2288	  back = 0;
2289	}
2290	fputs(hbhtml_c8_kw[5], job->of);
2291      }
2292      fputs(hbhtml_c8_kw[8], job->of);
2293      if(border) {
2294	if(!hbhtml_url_output_for_text(job, border)) {
2295	  back = 0;
2296	}
2297      } else {
2298        fputs(hbhtml_c8_kw[57], job->of);
2299      }
2300      fputs(hbhtml_c8_kw[5], job->of);
2301      if(clptr) {
2302        fputs(hbhtml_c8_kw[60], job->of);
2303	if(!hbhtml_url_output_for_text(job, clptr)) {
2304	  back = 0;
2305	}
2306	fputs(hbhtml_c8_kw[5], job->of);
2307      }
2308      if(idptr) {
2309        fputs(hbhtml_c8_kw[61], job->of);
2310	if(!hbhtml_url_output_for_text(job, idptr)) {
2311	  back = 0;
2312	}
2313	fputs(hbhtml_c8_kw[5], job->of);
2314      }
2315      if(usemap) {
2316        fputs(hbhtml_c8_kw[62], job->of);
2317	if(!hbhtml_url_output_for_text(job, usemap)) {
2318	  back = 0;
2319	}
2320	fputs(hbhtml_c8_kw[5], job->of);
2321      }
2322      if(align) {
2323        fputs(hbhtml_c8_kw[63], job->of);
2324	if(!hbhtml_url_output_for_text(job, align)) {
2325	  back = 0;
2326	}
2327	fputs(hbhtml_c8_kw[5], job->of);
2328      }
2329      if(hspace) {
2330        fputs(hbhtml_c8_kw[64], job->of);
2331	if(!hbhtml_url_output_for_text(job, hspace)) {
2332	  back = 0;
2333	}
2334	fputs(hbhtml_c8_kw[5], job->of);
2335      }
2336      if(vspace) {
2337        fputs(hbhtml_c8_kw[65], job->of);
2338	if(!hbhtml_url_output_for_text(job, vspace)) {
2339	  back = 0;
2340	}
2341	fputs(hbhtml_c8_kw[5], job->of);
2342      }
2343      if(ismap) {
2344        fputs(hbhtml_c8_kw[66], job->of);
2345      }
2346      fputs(hbhtml_c8_kw[3], job->of);
2347      fputs(hbhtml_c8_kw[0], job->of);
2348
2349      /* SVG object end.
2350      */
2351      if((svg) && (crobj)) {
2352        /* Closing object tag */
2353	fputs(hbhtml_c8_kw[73], job->of);
2354	fputs(hbhtml_c8_kw[0], job->of);
2355      }
2356
2357      /* Table or div end
2358      */
2359      if(dodiv) {
2360        if(capt) {
2361	  /* Print caption
2362	  */
2363	  fputs(hbhtml_c8_kw[116], job->of);
2364	  if(!hbhtml_output_for_text(job, capt)) { back = 0; }
2365	  fputs(hbhtml_c8_kw[117], job->of);
2366	  /* fputs(hbhtml_c8_kw[0], job->of); */
2367	}
2368	if(2 == dodiv) {
2369	  /* !td 		Close image td */
2370	  fputs(hbhtml_c8_kw[97], job->of);
2371	} else {
2372	  /* !div		Close inner div */
2373	  fputs(hbhtml_c8_kw[47], job->of);
2374	}
2375	if(downlo) {
2376	  if(2 == dodiv) {
2377	    /* td space !td 	Space div */
2378	    fputs(hbhtml_c8_kw[141], job->of);
2379	    /* td		Open download td */
2380	    fputs(hbhtml_c8_kw[142], job->of);
2381	  } else {
2382	    /* p		Open download p */
2383	    fputs(hbhtml_c8_kw[112], job->of);
2384	  }
2385	  /* Write download links */
2386	  {
2387	    haveli = 0;
2388	    if(svg) {		$? ". svg = \"%!ds\"", svg
2389	      haveli = 1;
2390	      fputs(hbhtml_c8_kw[68], job->of);
2391	      if(!hbhtml_url_output_for_text(job, svg)) { back = 0; }
2392	      fputs(hbhtml_c8_kw[69], job->of);
2393	      fputs(hbhtml_c8_kw[118], job->of);
2394	      fputs(hbhtml_c8_kw[40], job->of);
2395	      if(!hbhtml_output_for_text(job, (job->kwnl)[33])) { back = 0; }
2396	      fputs(hbhtml_c8_kw[41], job->of);
2397	    }
2398	    if(pdf) {		$? ". pdf = \"%!ds\"", pdf
2399	      if(haveli) {
2400	          fputs(hbhtml_c8_kw[105], job->of);
2401	      }
2402	      haveli = 1;
2403	      fputs(hbhtml_c8_kw[68], job->of);
2404	      if(!hbhtml_url_output_for_text(job, pdf)) { back = 0; }
2405	      fputs(hbhtml_c8_kw[69], job->of);
2406	      fputs(hbhtml_c8_kw[119], job->of);
2407	      fputs(hbhtml_c8_kw[40], job->of);
2408	      if(!hbhtml_output_for_text(job, (job->kwnl)[34])) { back = 0; }
2409	      fputs(hbhtml_c8_kw[41], job->of);
2410	    }
2411	    if(larger) {	$? ". larger = \"%!ds\"", larger
2412	      if(haveli) {
2413	        fputs(hbhtml_c8_kw[105], job->of);
2414	      }
2415	      haveli = 1;
2416	      fputs(hbhtml_c8_kw[68], job->of);
2417	      if(!hbhtml_url_output_for_text(job, larger)) { back = 0; }
2418	      fputs(hbhtml_c8_kw[69], job->of);
2419	      fputs(hbhtml_c8_kw[40], job->of);
2420	      if(!hbhtml_output_for_text(job, (job->msg)[58])) { back = 0; }
2421	      fputs(hbhtml_c8_kw[41], job->of);
2422	    }
2423	    if(haveli) {
2424	      fputs(hbhtml_c8_kw[105], job->of);
2425	    }
2426	    fputs(hbhtml_c8_kw[68], job->of);
2427	    if(!hbhtml_url_output_for_text(job, src)) { back = 0; }
2428	    fputs(hbhtml_c8_kw[69], job->of);
2429	    fputs(hbhtml_c8_kw[40], job->of);
2430	    if(!hbhtml_output_for_text(job, (job->msg)[59])) { back = 0; }
2431	    fputs(hbhtml_c8_kw[41], job->of);
2432	  }
2433	  if(2 == dodiv) {
2434	    /* !td		Close download td */
2435	    fputs(hbhtml_c8_kw[97], job->of);
2436	  } else {
2437	    /* !p		Close download p */
2438	    fputs(hbhtml_c8_kw[113], job->of);
2439	  }
2440	} else {
2441	  if(2 == dodiv) {
2442	  } else {
2443	    fputs(hbhtml_c8_kw[120], job->of);
2444	  }
2445	}
2446	if(2 == dodiv) {
2447	  /* !tr */
2448	  fputs(hbhtml_c8_kw[95], job->of);
2449	  /* !table */
2450	  fputs(hbhtml_c8_kw[93], job->of);
2451	  /* !div		Close outer div */
2452	  fputs(hbhtml_c8_kw[47], job->of);
2453	} else {
2454	  /* !div		Close outer div */
2455	  fputs(hbhtml_c8_kw[47], job->of);
2456	}
2457#if 0
2458        if(2 == dodiv) {
2459	} else {
2460	  /* Close image div
2461	  */
2462	  fputs(hbhtml_c8_kw[47], job->of);
2463	  if(downlo) {
2464	    /* Open outer download div */
2465	    fputs(hbhtml_c8_kw[112], job->of);
2466	    /* Download links */
2467	    {
2468	      haveli = 0;
2469	      if(svg) {		$? ". svg = \"%!ds\"", svg
2470	        haveli = 1;
2471	        fputs(hbhtml_c8_kw[68], job->of);
2472	        if(!hbhtml_url_output_for_text(job, svg)) { back = 0; }
2473	        fputs(hbhtml_c8_kw[69], job->of);
2474	        fputs(hbhtml_c8_kw[118], job->of);
2475	        fputs(hbhtml_c8_kw[40], job->of);
2476	        if(!hbhtml_output_for_text(job, (job->kwnl)[33])) { back = 0; }
2477	        fputs(hbhtml_c8_kw[41], job->of);
2478	      }
2479	      if(pdf) {		$? ". pdf = \"%!ds\"", pdf
2480	        if(haveli) {
2481	          fputs(hbhtml_c8_kw[105], job->of);
2482	        }
2483	        haveli = 1;
2484	        fputs(hbhtml_c8_kw[68], job->of);
2485	        if(!hbhtml_url_output_for_text(job, pdf)) { back = 0; }
2486	        fputs(hbhtml_c8_kw[69], job->of);
2487	        fputs(hbhtml_c8_kw[119], job->of);
2488	        fputs(hbhtml_c8_kw[40], job->of);
2489	        if(!hbhtml_output_for_text(job, (job->kwnl)[34])) { back = 0; }
2490	        fputs(hbhtml_c8_kw[41], job->of);
2491	      }
2492	      if(larger) {	$? ". larger = \"%!ds\"", larger
2493	        if(haveli) {
2494	          fputs(hbhtml_c8_kw[105], job->of);
2495	        }
2496	        haveli = 1;
2497	        fputs(hbhtml_c8_kw[68], job->of);
2498	        if(!hbhtml_url_output_for_text(job, larger)) { back = 0; }
2499	        fputs(hbhtml_c8_kw[69], job->of);
2500	        fputs(hbhtml_c8_kw[40], job->of);
2501	        if(!hbhtml_output_for_text(job, (job->msg)[58])) { back = 0; }
2502	        fputs(hbhtml_c8_kw[41], job->of);
2503	      }
2504	      if(haveli) {
2505	        fputs(hbhtml_c8_kw[105], job->of);
2506	      }
2507	      fputs(hbhtml_c8_kw[68], job->of);
2508	      if(!hbhtml_url_output_for_text(job, src)) { back = 0; }
2509	      fputs(hbhtml_c8_kw[69], job->of);
2510	      fputs(hbhtml_c8_kw[40], job->of);
2511	      if(!hbhtml_output_for_text(job, (job->msg)[59])) { back = 0; }
2512	      fputs(hbhtml_c8_kw[41], job->of);
2513	    }
2514	    /* Close output download div */
2515	    fputs(hbhtml_c8_kw[113], job->of);
2516	  } else {
2517	    fputs(hbhtml_c8_kw[120], job->of);
2518	  }
2519          /* Close outer div
2520	  */
2521	  fputs(hbhtml_c8_kw[47], job->of);
2522	}
2523#endif
2524      }
2525
2526      /*	Output creation is finished here.
2527      		Now we add the files to copy to the list.
2528      */
2529      if(src)		{ if(!hbtool_add_url(job, src)) { back = 0; } }
2530      if(larger)	{ if(!hbtool_add_url(job, larger)) { back = 0; } }
2531      if(pdf)		{ if(!hbtool_add_url(job, pdf)) { back = 0; } }
2532      if(svg)		{ if(!hbtool_add_url(job, svg)) { back = 0; } }
2533    }
2534  } else {
2535    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
2536  } $? "- hbhtml_process_sp_image %d", back
2537  return back;
2538}
2539
2540
2541
2542/**	Write current node item to navigation menu.
2543	@param	job	Job structure.
2544	@param	nptr	Current node.
2545	@return	1 on success, 0 on error.
2546*/
2547static
2548int
2549hbhtml_link_to_current_node(hb_job_t *job, hb_node_t *nptr)
2550{
2551  dkChar	*p;		/* Text to show */
2552  int		 back	= 1;
2553  $? "+ hbhtml_link_to_current_node"
2554  p = nptr->shorttitle;
2555  if(!(p)) { p = nptr->title; }
2556  if(p) {
2557    fputs(hbhtml_c8_kw[34], job->of);
2558    if(nptr->title) {
2559      fputs(hbhtml_c8_kw[7], job->of);
2560      if(hbtool_write_header_number(job, nptr)) {
2561        fputs(hbhtml_c8_kw[1], job->of);
2562      }
2563      if(!hbhtml_output_for_text(job, nptr->title)) { back = 0; }
2564      fputs(hbhtml_c8_kw[5], job->of);
2565    }
2566    fputs(hbhtml_c8_kw[3], job->of);
2567    if (!hbhtml_output_for_text(job, p)) { back = 0; }
2568    fputs(hbhtml_c8_kw[35], job->of);
2569  } else {
2570    back = 0;						$? "! ERROR"
2571  } $? "- hbhtml_link_to_current_node %d", back
2572  return back;
2573}
2574
2575
2576
2577/**	Write opening link tag to HTML output.
2578	@param	job	Job structure.
2579	@param	cl	Class name.
2580	@param	ti	Title.
2581	@param	dp	Destination.
2582	@param	tp	Type (optional).
2583	@param	nptr	Current node.
2584	@return	1 on success, 0 on error.
2585*/
2586static
2587int
2588hbhtml_write_link_start(
2589  hb_job_t	*job,
2590  char const	*cl,
2591  dkChar const	*ti,
2592  dkChar const	*dp,
2593  dkChar const	*tp,
2594  hb_node_t	*nptr
2595)
2596{
2597  int		 back = 0;
2598  $? "+ hbhtml_write_link_start"
2599  if(dp) {
2600    back = 1;
2601    fputs(hbhtml_c8_kw[39], job->of);
2602    if(!(hbhtml_url_output_for_text(job, dp))) {
2603      back = 0;					$? "! ERROR"
2604    }
2605    fputs(hbhtml_c8_kw[42], job->of);
2606    if(cl) {
2607      fputs(hbhtml_c8_kw[43], job->of);
2608      if(EOF == fputs(cl, job->of)) { back = 0; }
2609      fputs(hbhtml_c8_kw[42], job->of);
2610    }
2611    if(ti) {
2612      fputs(hbhtml_c8_kw[44], job->of);
2613      if(hbtool_write_header_number(job, nptr)) {
2614        fputs(hbhtml_c8_kw[1], job->of);
2615      }
2616      if(!(hbhtml_output_for_text(job, ti))) {
2617        back = 0;				$? "! ERROR"
2618      }
2619      fputs(hbhtml_c8_kw[42], job->of);
2620    }
2621    if(tp) {
2622      fputs(hbhtml_c8_kw[45], job->of);
2623      if(!(hbhtml_output_for_text(job, tp))) {
2624        back = 0;				$? "! ERROR"
2625      }
2626      fputs(hbhtml_c8_kw[42], job->of);
2627    }
2628    fputs(hbhtml_c8_kw[40], job->of);
2629  } $? "- hbhtml_write_link_start %d", back
2630  return back;
2631}
2632
2633
2634
2635/**	Write closing link tag.
2636	@param	job	Job structure.
2637*/
2638static
2639void
2640hbhtml_write_link_end(hb_job_t	*job)
2641{
2642  fputs(hbhtml_c8_kw[41], job->of);
2643  $? "= hbhtml_write_link_end"
2644}
2645
2646
2647
2648
2649/**	Write a link to HTML output.
2650	@param	job	Job structure.
2651	@param	cl	Class name.
2652	@param	ti	Title.
2653	@param	dp	Destination.
2654	@param	tp	Type (optional).
2655	@param	tx	Link text.
2656	@param	nptr	Current node.
2657	@return	1 on success, 0 on error.
2658*/
2659static
2660int
2661hbhtml_write_link(
2662  hb_job_t	*job,
2663  char const	*cl,
2664  dkChar const	*ti,
2665  dkChar const	*dp,
2666  dkChar const	*tp,
2667  dkChar const	*tx,
2668  hb_node_t	*nptr
2669)
2670{
2671  int			 back	= 0;
2672  $? "+ hbhtml_write_link"
2673  if((tx) && (dp)) {
2674    back = 1;
2675    if(!(hbhtml_write_link_start(job, cl, ti, dp, tp, nptr))) {
2676      back = 0;				$? "! ERROR"
2677    }
2678    if(!(hbhtml_output_for_text(job, tx))) {
2679      back = 0;				$? "! ERROR"
2680    }
2681    hbhtml_write_link_end(job);
2682  } $? "- hbhtml_write_link %d", back
2683  return back;
2684}
2685
2686
2687
2688int
2689hbhtml_create_output_filename(
2690  dkChar	*dptr,
2691  size_t 	 sz,
2692  hb_job_t	*job,
2693  hb_node_t	*nptr
2694)
2695{
2696  dkChar const		*sptr;		/* Suffix for output file */
2697  dkChar		*p1;		/* Suffix position */
2698  size_t		 nsz;		/* Summary length name + new suffix */
2699  int			 back = 0;
2700  $? "+ hbhtml_create_output_filename dptr=PTR=%d sz=%u nptr=PTR:%d", TR_IPTR(dptr), (unsigned)sz, TR_IPTR(nptr)
2701  if (nptr) {
2702    dptr[0] = dkT('\0');
2703    if(0UL == nptr->objno) {		$? ". node 0"
2704      if(dk3str_len((job->kwnl)[8]) < sz) {
2705        dk3str_cpy(dptr, (job->kwnl)[8]);
2706        back = 1;
2707      } else {
2708        /* ERROR: Name too long! */
2709        dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, (job->kwnl)[8]);
2710      }
2711    } else {				$? ". other node"
2712      if(nptr->filename) {		$? ". have source file name"
2713        if(dk3str_len(nptr->filename) < sz) {	$? ". length ok"
2714          dk3str_cpy(dptr, nptr->filename);
2715	  p1 = dk3str_get_suffix(dptr);
2716	  if(p1) {
2717	    p1++;
2718	    *p1 = dkT('\0');
2719	    if(nptr->suffix) {
2720	      sptr = nptr->suffix;
2721	    } else {
2722	      sptr = (job->kwnl)[10];
2723	    }
2724	    nsz = dk3str_len(dptr) + dk3str_len(sptr);
2725	    if(nsz < sz) {
2726	      dk3str_cat(dptr, sptr);
2727	      back = 1;
2728	    } else {			$? "! file name too long"
2729	      /* ERROR: File name too long! */
2730              dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, nptr->filename);
2731	    }
2732	  } else {
2733	    if(nptr->suffix) {
2734	      sptr = nptr->suffix;
2735	    } else {
2736	      sptr = (job->kwnl)[10];
2737	    }
2738	    nsz = dk3str_len(dptr)+dk3str_len(sptr)+dk3str_len((job->kwnl)[9]);
2739	    if(nsz < sz) {
2740	      dk3str_cat(dptr, (job->kwnl)[9]);
2741	      dk3str_cat(dptr, sptr);
2742	      back = 1;
2743	    } else {			$? "! file name too long"
2744	      /* File name too long! */
2745              dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, nptr->filename);
2746	    }
2747	  }
2748        } else {				$? "! input file name to long"
2749          /* ERROR: Input file name too long! */
2750          dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, nptr->filename);
2751        }
2752      } else {				$? "! no source file name"
2753        /* ERROR: No source file name! */
2754        dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 24);
2755      }
2756    }
2757  } else {				$? "! missing nptr"
2758  }
2759  $? "- hbhtml_create_output_filename %d \"%!ds\"", back, dptr
2760  return back;
2761}
2762
2763
2764
2765/**	Write link to one node to navigation menu.
2766	@param	job	Job structure.
2767	@param	nptr	Node to link to.
2768	@param	bold	Flag: Make link bold.
2769	@return	1 on success, 0 on error.
2770*/
2771static
2772int
2773hbhtml_link_to_node(hb_job_t *job, hb_node_t *nptr, int bold)
2774{
2775  dkChar	 bu[DK3_MAX_PATH];	/* Buffer for destination name */
2776  hb_node_t	*jnode;			/* Link jump destination */
2777  dkChar 	*p1;			/* Link title (full) */
2778  dkChar	*p2;			/* Link title (short or full) */
2779  int		 back	= 0;
2780  $? "+ hbhtml_link_to_node"
2781  jnode = nptr;
2782  if(!(nptr->filename)) {
2783    jnode = nptr->jumpnode;
2784  }
2785  p1 = nptr->title;
2786  p2 = ((nptr->shorttitle) ? (nptr->shorttitle) : (nptr->title));
2787  if((p1) && (p2)) {
2788    if(jnode) {
2789      if(hbhtml_create_output_filename(bu, DK3_SIZEOF(bu,dkChar), job, jnode)) {
2790        fputs(hbhtml_c8_kw[36], job->of);
2791        back = hbhtml_write_link(
2792	  job, hbhtml_c8_kw[(bold) ? 144 : 38], p1, bu, NULL, p2, nptr
2793	);
2794        fputs(hbhtml_c8_kw[37], job->of);
2795      }
2796    } else {
2797      /* ERROR: No text node! */
2798      dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 52, 53, nptr->title);
2799    }
2800  } $? "- hbhtml_link_to_node %d", back
2801  return back;
2802}
2803
2804
2805
2806/**	Instead of writing a navigation menu list separator item,
2807	finish current list and start a new one.
2808	@param	job	Job structure.
2809*/
2810static
2811void
2812hbhtml_navi_menu_next_list(hb_job_t *job)
2813{
2814	fputs(hbhtml_c8_kw[32], job->of);
2815	fputs(hbhtml_c8_kw[31], job->of);
2816}
2817
2818
2819
2820/**	Write the menu entries for a node.
2821	@param	job	Job structure.
2822	@param	nptr	Node to write menu entries for.
2823	@param	sepreq	Flag: Menu separator required.
2824	@return	1 on success, 0 on error.
2825*/
2826static
2827int
2828hbhtml_menu_entries_for_node(hb_job_t *job, hb_node_t *nptr, int sepreq)
2829{
2830  hb_node_t		*subnode;		/* Current node */
2831  hb_node_t		*jnptr;			/* Jump node for subnode */
2832  int			 active;		/* Flag: Active node */
2833  int			 sepdone	= 0;	/* Flag: Separator done */
2834  int			 bold		= 0;	/* Flag: Make bold */
2835  int			 back		= 1;
2836  $? "+ hbhtml_menu_entries_for_node"
2837  if(nptr) {	$? ". current node \"%!ds\"", TR_STR(nptr->title)
2838    dk3sto_it_reset(nptr->i_subnodes);
2839    while(NULL != (subnode = (hb_node_t *)dk3sto_it_next(nptr->i_subnodes))) {
2840      jnptr = subnode;
2841      if(!(jnptr->filename)) { jnptr = jnptr->jumpnode; }
2842      if(sepreq) {
2843        if(!(sepdone)) {
2844	  sepdone = 1;
2845#if	VERSION_BEFORE_2020_07_16
2846	  fputs(hbhtml_c8_kw[33], job->of);
2847#else
2848	  hbhtml_navi_menu_next_list(job);
2849#endif
2850	}
2851      } $? ". current subnode \"%!ds\"", TR_STR(subnode->title)
2852      active = 1;
2853      bold = 0;
2854#if VERSION_BEFORE_20140109
2855      if(nptr->curchild) {
2856        if(((nptr->curchild)->objno) == (subnode->objno)) {
2857	  active = 0;
2858	}
2859      }
2860#else
2861      if(job->currentnode) {
2862        if(jnptr) {
2863	  if(jnptr->objno == (job->currentnode)->objno) {
2864	    active = 0;
2865	  }
2866	}
2867      }
2868      if(nptr->curchild) {
2869        if(((nptr->curchild)->objno) == (subnode->objno)) {
2870	  bold = 1;
2871	}
2872      }
2873#endif
2874      if(active) {
2875        hbhtml_link_to_node(job, subnode, bold);
2876      } else {
2877        hbhtml_link_to_current_node(job, subnode);
2878      }
2879    }
2880  } $? "- hbhtml_menu_entries_for_node %d", back
2881  return back;
2882}
2883
2884
2885
2886/**	Check whether a node has sub menu items.
2887	@param	ptr	Node to check.
2888	@return	1 if sub items are present, 0 otherwise.
2889*/
2890static
2891int
2892hbhtml_have_sub_menu_items(hb_node_t *ptr)
2893{
2894  int		 back = 0;
2895  $? "+ hbhtml_have_sub_menu_items"
2896  dk3sto_it_reset(ptr->i_subnodes);
2897  while(NULL != dk3sto_it_next(ptr->i_subnodes)) {
2898    back = 1;
2899  } $? "- hbhtml_have_sub_menu_items %d", back
2900  return back;
2901}
2902
2903
2904
2905/**	Process menu special command.
2906	@param	job	Job structure.
2907	@param	pa	Special command arguments.
2908	@return	1 on success, 0 on error (continue), -1 on error (exit).
2909*/
2910static
2911int
2912hbhtml_process_sp_menu(hb_job_t *job, dkChar *pa)
2913{
2914  dk3_key_value_t	 kv[10];	/* Key value pairs */
2915  hb_node_t		*mnptr;		/* Menu node pointer */
2916  size_t		 sz;		/* Size of kv */
2917  size_t		 i;		/* Traverse kv */
2918  int			 res;		/* Result from writing link */
2919  int			 sep	= 0;	/* Flag: Need separator */
2920  int			 tv	= 0;	/* Test value */
2921  int			 mnst	= 0;	/* Menu style (full) */
2922  int			 sub	= 1;	/* Include sub menu of current node */
2923  int			 back	= 1;
2924  $? "+ hbhtml_process_sp_menu \"%!ds\"", pa
2925  sz = DK3_SIZEOF(kv,dk3_key_value_t);
2926  if(dk3str_to_key_value(kv, &sz, pa, job->app)) {
2927    for(i = 0; i < sz; i++) {
2928      if(kv[i].key) {
2929        switch(dk3str_array_abbr(hbhtml_menu_keys, kv[i].key, dkT('$'), 0)) {
2930	  case 0: {
2931	    if(kv[i].val) {
2932	      tv = dk3str_array_abbr(hbhtml_menu_styles,kv[i].val,dkT('$'),0);
2933	      if(0 <= tv) {
2934	        mnst = tv;
2935	      } else {
2936	        /* ERROR: Illegal menu style */
2937		dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,25,26,kv[i].val);
2938	      }
2939	    }
2940	  } break;
2941	  case 1: {
2942	    if(kv[i].val) {
2943	      sub = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
2944	    } else {
2945	      sub = 1;
2946	    }
2947	  } break;
2948	  default: {
2949	    /* ERROR: Illegal key. */
2950	    dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,27,26,kv[i].key);
2951	  } break;
2952	}
2953      }
2954    }
2955  } else {
2956    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
2957  } $? ". mnst=%d   sub=%d", mnst, sub
2958  if(((job->currentnode)->options) & HB_NODE_OPT_IMPRESSUM) {
2959    /*	When we are writing the menu for the impressum page,
2960    	we print the top menu only.
2961    */
2962    mnst = 3;
2963    sub = 0;
2964  }	$? ". mnst=%d   sub=%d", mnst, sub
2965  if(((job->currentnode)->options) & HB_NODE_OPT_PRIVACY) {
2966    /*	When we are writing the menu for the privacy statement page,
2967    	we print the top menu only.
2968    */
2969    mnst = 3;
2970    sub = 0;
2971  }	$? ". mnst=%d   sub=%d", mnst, sub
2972  fputs(hbhtml_c8_kw[29], job->of);
2973  fputs(hbhtml_c8_kw[31], job->of);
2974  /*	Impressum node first, separator.
2975  */
2976  if(job->impressumnode) {		$? ". have impressum node"
2977    if(((job->currentnode)->options) & HB_NODE_OPT_IMPRESSUM) {
2978      if(!hbhtml_link_to_current_node(job, job->impressumnode)) {
2979        back = 0;				$? "! ERROR"
2980      }
2981    } else {
2982      if(!hbhtml_link_to_node(job, job->impressumnode, 0)) {
2983        back = 0;				$? "! ERROR"
2984      }
2985    }
2986    /* Separator */
2987    if (NULL == job->privacynode) {
2988      if (NULL == job->privacylink) {
2989#if	VERSION_BEFORE_2020_07_16
2990        fputs(hbhtml_c8_kw[33], job->of);
2991#else
2992	hbhtml_navi_menu_next_list(job);
2993#endif
2994      }
2995    }
2996  } else {
2997    if(job->impressumlink) {		$? ". have impressum link"
2998      fputs(hbhtml_c8_kw[36], job->of);
2999      res = hbhtml_write_link(
3000        job, hbhtml_c8_kw[38], (job->msg)[0], job->impressumlink,
3001	NULL, (job->msg)[0], NULL
3002      );
3003      fputs(hbhtml_c8_kw[37], job->of);
3004      if(!(res)) {
3005        back = 0;			$? "! ERROR"
3006      }
3007      /* Separator */
3008      if (NULL == job->privacynode) {
3009        if (NULL == job->privacylink) {
3010#if	VERSION_BEFORE_2020_07_16
3011          fputs(hbhtml_c8_kw[33], job->of);
3012#else
3013	  hbhtml_navi_menu_next_list(job);
3014#endif
3015        }
3016      }
3017    } else {				$? ". no impressum link"
3018    }
3019  }
3020  /*	Privacy node first, separator.
3021  */
3022  if(job->privacynode) {		$? ". have privacy node"
3023    if(((job->currentnode)->options) & HB_NODE_OPT_PRIVACY) {
3024      if(!hbhtml_link_to_current_node(job, job->privacynode)) {
3025        back = 0;				$? "! ERROR"
3026      }
3027    } else {
3028      if(!hbhtml_link_to_node(job, job->privacynode, 0)) {
3029        back = 0;				$? "! ERROR"
3030      }
3031    }
3032    /* Separator */
3033#if	VERSION_BEFORE_2020_07_16
3034    fputs(hbhtml_c8_kw[33], job->of);
3035#else
3036    hbhtml_navi_menu_next_list(job);
3037#endif
3038  } else {
3039    if(job->privacylink) {		$? ". have privacy link"
3040      fputs(hbhtml_c8_kw[36], job->of);
3041      res = hbhtml_write_link(
3042        job, hbhtml_c8_kw[38], (job->msg)[105], job->privacylink,
3043	NULL, (job->msg)[104], NULL
3044      );
3045      fputs(hbhtml_c8_kw[37], job->of);
3046      if(!(res)) {
3047        back = 0;			$? "! ERROR"
3048      }
3049      /* Separator */
3050#if	VERSION_BEFORE_2020_07_16
3051      fputs(hbhtml_c8_kw[33], job->of);
3052#else
3053      hbhtml_navi_menu_next_list(job);
3054#endif
3055    } else {				$? ". no privacy link"
3056    }
3057  }
3058  /*	Now real menu(s)
3059  	0=reverse, 1=full, 2=current, 3=top
3060  */
3061  mnptr = job->currentnode;
3062  if(mnptr) {		$? ". current node: \"%!ds\"", TR_STR(mnptr->title)
3063    switch(mnst) {
3064      case -1: case 0: case 2: {	$? ". reverse or current"
3065        if(sub) {
3066	  if(hbhtml_have_sub_menu_items(mnptr)) {	$? " current entry"
3067            /* Menu for mnptr without leading space */
3068	    $? ". menu from current entry"
3069	    hbhtml_menu_entries_for_node(job, mnptr, 0);
3070	    sep = 1;
3071	  }
3072        }
3073	mnptr = mnptr->parent;
3074	if(mnptr) {
3075	  /* Menu for mnptr with leading space */
3076	  $? ". menu from parent entry"
3077	  hbhtml_menu_entries_for_node(job, mnptr, sep);
3078          if(2 != mnst) {			$? ". grandparents"
3079	    do {
3080	      mnptr = mnptr->parent;
3081	      if(mnptr) {
3082	        /* Menu for mnptr with leading space */
3083		$? ". menu from further parent entry"
3084	        hbhtml_menu_entries_for_node(job, mnptr, 1);
3085	      }
3086	    } while(mnptr);
3087          }
3088	}
3089      } break;
3090      case 1: case 3: {		$? ". full or top"
3091        if(((job->currentnode)->options) & HB_NODE_OPT_IMPRESSUM) {
3092          mnptr = job->rootnode;
3093	  sub = 0;
3094        } else {
3095          while(mnptr->parent) { mnptr = mnptr->parent; }
3096        }
3097        /* Menu for mnptr without leading space */
3098	$? ". menu entries from root node"
3099        hbhtml_menu_entries_for_node(job, mnptr, 0);
3100        if(1 == mnst) {		$? ". full, continue with children"
3101          do {
3102	    mnptr = mnptr->curchild;
3103	    if(mnptr) {
3104	      if(mnptr->curchild) {
3105	        /* Menu for mnptr with leading space */
3106		$? ". menu entries form parents/grandparents of current node"
3107	        hbhtml_menu_entries_for_node(job, mnptr, 1);
3108	      } else {
3109	        if(sub) {
3110		  /* Menu entry from current node */
3111		  $? ". menu from current node"
3112		  hbhtml_menu_entries_for_node(job, mnptr, 1);
3113		}
3114	      }
3115	    }
3116	  } while(mnptr);
3117        }
3118      } break;
3119    }
3120  }
3121  fputs(hbhtml_c8_kw[32], job->of);
3122  fputs(hbhtml_c8_kw[30], job->of);	$? "- hbhtml_process_sp_menu %d", back
3123  return back;
3124}
3125
3126
3127
3128/**	Write image with link for one navigation icon.
3129	@param	job		Job structure.
3130	@param	iconfile	Icon file name.
3131	@param	dest		Destination URL.
3132	@param	titlea1		Title part 1 when icon link is active.
3133	@param	titlea2		Title part 2 when icon link is active.
3134	@param	titlena		Title when icon is not active.
3135	@param	act		Flag: Link is active.
3136	@return	1 on success, 0 on error.
3137*/
3138static
3139int
3140hbhtml_one_navi_icon(
3141  hb_job_t	*job,
3142  dkChar const	*iconfile,
3143  dkChar const	*dest,
3144  dkChar const	*titlea1,
3145  dkChar const	*titlea2,
3146  dkChar const	*titlena,
3147  int		 act
3148)
3149{
3150  int		 back = 1;
3151  unsigned long	 w = 0UL;	/* Icon width */
3152  unsigned long	 h = 0UL;	/* Icon height */
3153  $? "+ hbhtml_one_navi_icon"
3154  $? ". iconfile = \"%!ds\"", TR_STR(iconfile)
3155  $? ". dest     = \"%!ds\"", TR_STR(dest)
3156  $? ". titlea1  = \"%!ds\"", TR_STR(titlea1)
3157  $? ". titlea2  = \"%!ds\"", TR_STR(titlea2)
3158  $? ". titlena  = \"%!ds\"", TR_STR(titlena)
3159  $? ". act      = %d", act
3160  if(act) {			$? ". act"
3161    fputs(hbhtml_c8_kw[49], job->of);
3162    if(!(hbhtml_url_output_for_text(job, dest))) {
3163      back = 0;
3164    }				$? ". 1"
3165    fputs(hbhtml_c8_kw[50], job->of);
3166    if(!(hbhtml_output_for_text(job, titlea1))) {
3167      back = 0;
3168    }				$? ". 2"
3169    if(titlea2) {
3170      if(!(hbhtml_output_for_text(job, titlea2))) {
3171        back = 0;
3172      }
3173    }				$? ". 3"
3174    fputs(hbhtml_c8_kw[51], job->of);
3175    fputs(hbhtml_c8_kw[53], job->of);
3176    if(!(hbhtml_url_output_for_text(job, iconfile))) {
3177      back = 0;
3178    }				$? ". 4"
3179    fputs(hbhtml_c8_kw[54], job->of);
3180    if(!(hbhtml_output_for_text(job, titlea1))) {
3181      back = 0;
3182    }				$? ". 6"
3183    if(titlea2) {
3184      if(!(hbhtml_output_for_text(job, titlea2))) {
3185        back = 0;
3186      }
3187    }
3188    fputs(hbhtml_c8_kw[55], job->of);
3189    if(!(hbhtml_output_for_text(job, titlea1))) {
3190      back = 0;
3191    }				$? ". 6"
3192    if(titlea2) {
3193      if(!(hbhtml_output_for_text(job, titlea2))) {
3194        back = 0;
3195      }
3196    }
3197    fputs(hbhtml_c8_kw[56], job->of);
3198    if(hbimgdim_find(job, iconfile, &w, &h, 1)) {
3199      if((w) && (h)) {
3200        fprintf(job->of, hbhtml_c8_kw[67], w, h);
3201      }
3202    }
3203    fputs(hbhtml_c8_kw[3], job->of);
3204    fputs(hbhtml_c8_kw[52], job->of);	$? ". 7"
3205  } else {			$? ". not act"
3206    fputs(hbhtml_c8_kw[53], job->of);
3207    if(!(hbhtml_url_output_for_text(job, iconfile))) {
3208      back = 0;
3209    }					$? ". 8"
3210    fputs(hbhtml_c8_kw[54], job->of);
3211    if(!(hbhtml_output_for_text(job, titlea1))) {
3212      back = 0;
3213    }				$? ". 6"
3214    if(titlea2) {
3215      if(!(hbhtml_output_for_text(job, titlea2))) {
3216        back = 0;
3217      }
3218    }
3219    fputs(hbhtml_c8_kw[55], job->of);
3220    if(!(hbhtml_output_for_text(job, titlena))) {
3221      back = 0;
3222    }					$? ". 10"
3223    fputs(hbhtml_c8_kw[56], job->of);
3224    if(hbimgdim_find(job, iconfile, &w, &h, 1)) {
3225      if((w) && (h)) {
3226        fprintf(job->of, hbhtml_c8_kw[67], w, h);
3227      }
3228    }
3229    fputs(hbhtml_c8_kw[3], job->of);
3230  } $? "- hbhtml_one_navi_icon %d", back
3231  return back;
3232}
3233
3234
3235
3236/**	Process special command to build navigation div.
3237	@param	job	Job structure.
3238	@param	pa	Programm arguments (ignored).
3239	@param	p	Previous node.
3240	@param	n	Next node.
3241	@return	1 on success, 0 on error.
3242*/
3243static
3244int
3245hbhtml_process_sp_navigation(
3246  hb_job_t	*job,
3247  dkChar	*pa,
3248  hb_node_t	*p,
3249  hb_node_t	*n
3250)
3251{
3252  dk3_key_value_t	 kv[10];		/* Command parameters */
3253  dkChar		 bu[DK3_MAX_PATH];	/* Construct file name */
3254  hb_node_t		*nptr;			/* Current node */
3255  dkChar const		*itop	= NULL;		/* TOC icon name */
3256  dkChar const		*iprev	= NULL;		/* Previous icon name */
3257  dkChar const		*inext	= NULL;		/* Next icon name */
3258  dkChar const		*ihome	= NULL;		/* Home icon name */
3259  dkChar const		*ikind	= NULL;		/* Index icon name */
3260  dkChar const		*ta1;			/* Icon image title, node */
3261  dkChar const		*ta2;			/* Icon image title */
3262  dkChar const		*tna;			/* Icon image title start */
3263  size_t		 sz;			/* Number of command params */
3264  size_t		 szurl;			/* URL length */
3265  size_t		 i;			/* Traverse command params */
3266  int			 res;			/* Result create one icon */
3267  int			 kind	= 0;		/* Flag: Keyword index */
3268  int			 ptoc	= 0;		/* Flag: TOC */
3269  int			 back = 1;
3270  $? "+ hbhtml_process_sp_navigation"
3271  sz = DK3_SIZEOF(kv,dk3_key_value_t);
3272  if(dk3str_to_key_value(kv, &sz, pa, job->app)) {
3273    for(i = 0; i < sz; i++) {
3274      if(kv[i].key) {
3275        switch(dk3str_array_abbr(hbhtml_navi_keys, kv[i].key, dkT('$'), 0)) {
3276	  case 0: {
3277	    itop = kv[i].val;
3278	  } break;
3279	  case 1: {
3280	    iprev = kv[i].val;
3281	  } break;
3282	  case 2: {
3283	    inext = kv[i].val;
3284	  } break;
3285	  case 3: {
3286	    ikind = kv[i].val;
3287	  } break;
3288	  case 4: {
3289	    ihome = kv[i].val;
3290	  } break;
3291	}
3292      }
3293    }
3294  } else {
3295    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
3296  }
3297  nptr = job->currentnode;
3298  kind = (((job->options) & HB_JOB_OPT_CREATE_INDEX) ? 1 : 0);
3299  ptoc = (((job->options) & HB_JOB_OPT_CREATE_TOC) ? 1 : 0);
3300  while(
3301    (nptr)
3302    && (
3303      !(
3304        ((itop) || (!(ptoc)))
3305	&& (ihome)
3306	&& (iprev) && (inext)
3307	&& ((ikind) || (!(kind)))
3308      )
3309    )
3310  )
3311  {
3312    if(!(itop)) itop = nptr->icontoc;
3313    if(!(iprev)) iprev = nptr->iconprev;
3314    if(!(inext)) inext = nptr->iconnext;
3315    if(!(ikind)) ikind = nptr->iconindex;
3316    if(!(ihome)) ihome = nptr->iconhome;
3317    nptr = nptr->parent;
3318  }
3319  $? ". before corrections"
3320  if(!(itop))  itop  = (job->kwnl)[14];
3321  if(!(iprev)) iprev = (job->kwnl)[15];
3322  if(!(inext)) inext = (job->kwnl)[16];
3323  if(!(ikind)) ikind = (job->kwnl)[17];
3324  if(!(ihome)) ihome = (job->kwnl)[37];
3325  $? ". after corrections"
3326  if(
3327    ((itop) || (!(ptoc)))
3328    && (iprev) && (inext) && (ihome)
3329    && ((ikind) || (!(kind)))
3330  )
3331  {
3332    back = 1;
3333    if(itop) { if(!hbtool_add_file(job, itop)) { back = 0; } }
3334    if(iprev) { if(!hbtool_add_file(job, iprev)) { back = 0; } }
3335    if(inext) { if(!hbtool_add_file(job, inext)) { back = 0; } }
3336    if(ikind) { if(!hbtool_add_file(job, ikind)) { back = 0; } }
3337    if(ihome) { if(!hbtool_add_file(job, ihome)) { back = 0; } }
3338    fputs(hbhtml_c8_kw[46], job->of);
3339
3340    /* HOME */		$? ". home"
3341    ta1 = (job->msg)[1];
3342    ta2 = (job->rootnode)->title;
3343    tna = (job->msg)[2];
3344    res = hbhtml_create_output_filename(
3345      bu, DK3_SIZEOF(bu,dkChar), job, job->rootnode
3346    );
3347    if(res) {	$? ". fn ok \"%!ds\"", bu
3348      $? ". icon = \"%!ds\"", itop
3349      res = hbhtml_one_navi_icon(
3350        job, ihome, bu, ta1, ta2, tna,
3351	(
3352	  (0 != job->currentnode->havenavimenu)
3353	  ? (1)
3354	  : (((0UL == (job->currentnode)->objno) ? 0 : 1))
3355	)
3356      );
3357      if(!(res)) { back = 0; }
3358      $? ". navi icon completed"
3359    } else {	$? "! fn"
3360      back = 0;
3361    }
3362    /* Previous */	$? ". prev"
3363    ta1 = (job->msg)[3];
3364    ta2 = NULL;
3365    if(p) ta2 = p->title;
3366    tna = (job->msg)[4];
3367    if(p) {
3368      res = hbhtml_create_output_filename(
3369        bu, DK3_SIZEOF(bu,dkChar), job, p
3370      );
3371      if(res) {
3372        fputs(hbhtml_c8_kw[48], job->of);
3373	res = hbhtml_one_navi_icon(job, iprev, bu, ta1, ta2, tna, 1);
3374	if(!(res)) { back = 0; }
3375      } else {
3376        back = 0;
3377      }
3378    } else {
3379      fputs(hbhtml_c8_kw[48], job->of);
3380      res = hbhtml_one_navi_icon(job, iprev, NULL, ta1, ta2, tna, 0);
3381      if(!(res)) { back = 0; }
3382    }
3383    /* Next */		$? ". next"
3384    ta1 = (job->msg)[5];
3385    ta2 = NULL;
3386    if(n) ta2 = n->title;
3387    tna = (job->msg)[6];
3388    if(n) {
3389      res = hbhtml_create_output_filename(
3390        bu, DK3_SIZEOF(bu,dkChar), job, n
3391      );
3392      if(res) {
3393        fputs(hbhtml_c8_kw[48], job->of);
3394	res = hbhtml_one_navi_icon(job, inext, bu, ta1, ta2, tna, 1);
3395	if(!(res)) { back = 0; }
3396      } else {
3397        back = 0;
3398      }
3399    } else {
3400      fputs(hbhtml_c8_kw[48], job->of);
3401      res = hbhtml_one_navi_icon(job, inext, NULL, ta1, ta2, tna, 0);
3402      if(!(res)) { back = 0; }
3403    }
3404    /* TOC */		$? ". toc"
3405    if(ptoc) {
3406      ta1 = (job->msg)[93];
3407      ta2 = (job->rootnode)->title;
3408      tna = (job->msg)[94];
3409      res = hbhtml_create_output_filename(
3410        bu, DK3_SIZEOF(bu,dkChar), job, job->rootnode
3411      );
3412      if(res) {	$? ". fn ok \"%!ds\"", bu
3413        szurl = dk3str_len(bu);
3414	szurl += dk3str_len((job->kwnl)[31]);
3415	if(szurl < DK3_SIZEOF(bu,dkChar)) {
3416	  dk3str_cat(bu, (job->kwnl)[31]);
3417	} else {
3418	  back = 0;
3419	}
3420        $? ". icon = \"%!ds\"", itop
3421        fputs(hbhtml_c8_kw[48], job->of);
3422        res = hbhtml_one_navi_icon(job, itop, bu, ta1, ta2, tna, 1);
3423        if(!(res)) { back = 0; }
3424        $? ". navi icon completed"
3425      } else {	$? "! fn"
3426        back = 0;
3427      }
3428    }
3429    /* Key index */
3430    if(kind) {			$? ". kind"
3431      ta1 = (job->msg)[7];
3432      ta2 = NULL;
3433      tna = NULL;
3434      res = hbhtml_create_output_filename(
3435        bu, DK3_SIZEOF(bu,dkChar), job, job->rootnode
3436      );
3437      if(res) {
3438        if(dk3str_len(bu)+dk3str_len((job->kwnl)[13]) < DK3_SIZEOF(bu,dkChar)) {
3439	  dk3str_cat(bu, (job->kwnl)[13]);
3440	  fputs(hbhtml_c8_kw[48], job->of);
3441          res = hbhtml_one_navi_icon(job, ikind, bu, ta1, ta2, tna, 1);
3442          if(!(res)) { back = 0; }
3443	} else {
3444	  back = 0;
3445	}
3446      } else {
3447        back = 0;
3448      }
3449    }
3450    fputs(hbhtml_c8_kw[47], job->of);
3451    job->currentnode->havenavimenu = 1;
3452    $? ". finished"
3453  } else {			$? "! icons missing"
3454    $? ". itop  = \"%!ds\"", TR_STR(itop)
3455    $? ". iprev = \"%!ds\"", TR_STR(iprev)
3456    $? ". inext = \"%!ds\"", TR_STR(inext)
3457    $? ". ikind = \"%!ds\"", TR_STR(ikind)
3458    $? ". ihome = \"%!ds\"", TR_STR(ihome)
3459  }
3460  $? "- hbhtml_process_sp_navigation %d", back
3461  return back;
3462}
3463
3464
3465
3466/**	Process author special command.
3467	@param	job	Job structure.
3468	@return	1 on success, 0 on error (continue), -1 on error (exit).
3469*/
3470static
3471int
3472hbhtml_process_sp_author(hb_job_t *job)
3473{
3474  dkChar const		*ptr;		/* Author name */
3475  int			 back = 0;
3476  $? "+ hbhtml_process_sp_author"
3477  ptr = hbconf_author(job->currentnode);
3478  if(ptr) {
3479    if(hbhtml_output_for_text(job, ptr)) {
3480      back = 1;
3481    }
3482  } $? "- hbhtml_process_sp_author %d", back
3483  return back;
3484}
3485
3486
3487
3488/**	Process location special command.
3489	@param	job	Job structure.
3490	@return 1 on success, 0 on error (continue), -1 on error (exit).
3491*/
3492static
3493int
3494hbhtml_process_sp_location(hb_job_t *job)
3495{
3496  dkChar const		*ptr;		/* Location name */
3497  int			 back = 0;
3498  $? "+ hbhtml_process_sp_location"
3499  ptr = hbconf_location(job->currentnode);
3500  if(ptr) {
3501    if(hbhtml_output_for_text(job, ptr)) {
3502      back = 1;
3503    }
3504  } $? "- hbhtml_process_sp_location %d", back
3505  return back;
3506}
3507
3508
3509
3510/**	Process date special command.
3511	@param  job	Job structure.
3512	@param  pa	Program arguments.
3513	@return 1 on success, 0 on error (continue), -1 on error (exit).
3514*/
3515static
3516int
3517hbhtml_process_sp_date(hb_job_t *job, dkChar *pa)
3518{
3519  dk3_key_value_t	 kv[5];		/* Command parameters */
3520  size_t		 sz;		/* Number of command paramters */
3521  size_t		 i;		/* Traverse command paramters */
3522  int			 german = 0;	/* Flag: German style date */
3523  int			 back = 1;
3524  $? "+ hbhtml_process_sp_date \"%!ds\"", TR_STR(pa)
3525  sz = DK3_SIZEOF(kv,dk3_key_value_t);
3526  if(dk3str_to_key_value(kv,&sz,pa,job->app)) {	$? ". to key value"
3527    for(i = 0; i < sz; i++) {	$? ". i=%u", (unsigned)i
3528      if(kv[i].key) {	$? ". key=\"%!ds\"   val=\"%!ds\"", TR_STR(kv[i].key), TR_STR(kv[i].val)
3529        switch(dk3str_array_abbr(hbhtml_date_keys, kv[i].key, dkT('$'), 0)) {
3530	  case 0: {
3531	    if(kv[i].val) {
3532	      if(0 == dk3str_casecmp((job->kwnl)[11], kv[i].val)) {
3533	        german = 1;	$? ". german"
3534	      } else {
3535	        german = 0;	$? ". not german"
3536	      }
3537	    }
3538	  } break;
3539	  default: {
3540	    /* ERROR: Illegal attribute! */
3541	    dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,27,26,kv[i].key);
3542	    back = 0;		$? "! ERROR"
3543	  } break;
3544	}
3545      }
3546    }
3547  } else {		$? "! dk3str_to_key_value"
3548    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
3549  }
3550  if(german) {
3551    fprintf(
3552      job->of, "%02d.%02d.%04d",
3553      (job->starttime).D, (job->starttime).M, (job->starttime).Y
3554    );
3555  } else {
3556    fprintf(
3557      job->of, "%04d-%02d-%02d",
3558      (job->starttime).Y, (job->starttime).M, (job->starttime).D
3559    );
3560  } $? "- hbhtml_process_sp_date %d", back
3561  return back;
3562}
3563
3564
3565
3566/**	Process special command to show variable.
3567	@param	job	Job structure.
3568	@param	pa	Pointer to arguments.
3569	@return	1 on success, 0 on error.
3570*/
3571static
3572int
3573hbhtml_process_sp_variable(hb_job_t *job, dkChar *pa)
3574{
3575  dk3_key_value_t	 kv[5];		/* Argument pairs */
3576  dkChar const		*varname;	/* Variable name. */
3577  dkChar const		*varval;	/* Variable value */
3578  size_t		 sz;		/* Size of kv */
3579  size_t		 i;		/* Traverse kv */
3580  int			 urlm	= 0;	/* Flag: URL mode */
3581  int			 back	= 1;
3582  $? "+ hbhtml_process_sp_variable"
3583  sz = DK3_SIZEOF(kv,dk3_key_value_t);
3584  if(dk3str_to_key_value(kv,&sz,pa,job->app)) {
3585    if(sz > 0) {
3586      varname = kv[0].key;
3587      for(i = 1; i < sz; i++) {
3588        if(kv[i].key) {
3589	  if(dk3str_casecmp((job->kwnl)[12], kv[i].key) == 0) {
3590	    if(kv[i].val) {
3591	      urlm = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
3592	    } else {
3593	      urlm = 1;
3594	    }
3595	  } else {
3596	    back = 0;			$? "! ERROR"
3597	    /* ERROR: Illegal argument */
3598	    dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,27,26,kv[i].key);
3599	  }
3600	}
3601      }
3602      varval = hbnode_variable(job->currentnode, varname);
3603      if(varval) {
3604        if(urlm) {
3605	  if(!hbhtml_url_output_for_text(job, varval)) {
3606	    back = 0;			$? "! ERROR"
3607	  }
3608	} else {
3609	  if(!hbhtml_output_for_text(job, varval)) {
3610	    back = 0;			$? "! ERROR"
3611	  }
3612	}
3613      }
3614    }
3615  } else {
3616    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
3617  } $? "- hbhtml_process_sp_variable %d", back
3618  return back;
3619}
3620
3621
3622
3623/**	Process contents of one file.
3624	@param	job	Job structure.
3625	@param	p	Previous node.
3626	@param	n	Next node.
3627	@return	1 on success, 0 on error (can continue), -1 on error (exit).
3628*/
3629static
3630int
3631hbhtml_process_sp_contents(hb_job_t *job, hb_node_t *p, hb_node_t *n)
3632{
3633  int			 back = 1;
3634  job->spused = 0;
3635  job->shipused = 0;
3636  $? "+ hbhtml_process_sp_contents %lu", (job->currentnode)->objno
3637  if(job->currentnode) {		$? ". have current node"
3638    if((job->currentnode)->filename) {	$? ". have input file name"
3639      back = hbcont_process(job, p, n);
3640    } else {				$? "! no input file name"
3641      if(0UL == (job->currentnode)->objno) {	$? ". root node"
3642        back = hbcont_process(job, p, n);
3643      } else {					$? "! not root node"
3644      }
3645    }
3646  } $? "- hbhtml_process_sp_contents %d", back
3647  return back;
3648}
3649
3650
3651
3652/**	Open code tag for line number.
3653	@param	job	Job structure.
3654	@return	1 on success, 0 on error.
3655*/
3656static
3657int
3658hbhtml_coli_open(hb_job_t *job)
3659{
3660  int		back = 1;
3661  if (EOF == fputs(hbhtml_c8_kw[146], job->of)) { back = 0; }
3662  if (1000 > job->coli) {
3663    if (0 > fprintf(job->of, "%03d", job->coli)) { back = 0; }
3664    job->coli += 1;
3665    if (1000 == job->coli) {
3666      back = 0;
3667      /* WARNING: Too many code lines */
3668      dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 88);
3669    }
3670  } else {
3671    back = 0;
3672    fputs(hbhtml_c8_kw[149], job->of);
3673  }
3674  if (EOF == fputs(hbhtml_c8_kw[147], job->of)) { back = 0; }
3675  return back;
3676}
3677
3678
3679
3680/**	Close code tag for line number.
3681	@param	job	Job structure.
3682	@return	1 on success, 0 on error.
3683*/
3684static
3685int
3686hbhtml_coli_close(hb_job_t *job)
3687{
3688  int		 back = 1;
3689  if (EOF == fputs(hbhtml_c8_kw[148], job->of)) { back = 0; }
3690  return back;
3691}
3692
3693
3694
3695/**	Output dummy code line to avoid trimming emptry code tag.
3696	@param	job	Job structure.
3697	@return	1 on success, 0 on error
3698*/
3699static
3700int
3701hbhtml_coli_dummy(hb_job_t *job)
3702{
3703  int		 back = 1;
3704  if (!hbhtml_coli_open(job)) { back = 0; }
3705  if (EOF == fputs(hbhtml_c8_kw[150], job->of)) { back = 0; }
3706  if (!hbhtml_coli_close(job)) { back = 0; }
3707  return back;
3708}
3709
3710
3711
3712/**	Start of a code line found.
3713	Invoked from the input line handler.
3714	@param	job	Job structure.
3715	@return	1 on success, 0 on error.
3716*/
3717static
3718int
3719hbhtml_code_line_start(hb_job_t *job)
3720{
3721  int		 back = 1;
3722  if (((job->options) & HB_JOB_OPT_CODE) && (0 < job->coli)) {
3723    switch (job->cols) {
3724      case HB_COLIST_NONE: {
3725        if (!hbhtml_coli_open(job)) { back = 0; }
3726        job->cols = HB_COLIST_OPENED;
3727      } break;
3728      case HB_COLIST_NOT_OPENED: {
3729        if (!hbhtml_coli_open(job)) { back = 0; }
3730        job->cols = HB_COLIST_OPENED;
3731      } break;
3732#if 0
3733      case HB_COLIST_OPENED: {
3734      } break;
3735#endif
3736    }
3737  }
3738  return back;
3739}
3740
3741
3742
3743/**	Finalize a code line.
3744	Invoked from the input line handler.
3745	@param	job	Job structure.
3746	@return	1 on success, 0 on error.
3747*/
3748static
3749int
3750hbhtml_code_line_end(hb_job_t *job)
3751{
3752  int		 back = 1;
3753  if (((job->options) & HB_JOB_OPT_CODE) && (0 < job->coli)) {
3754    switch (job->cols) {
3755      case HB_COLIST_NONE: {
3756        job->cols = HB_COLIST_NOT_OPENED;
3757      } break;
3758      case HB_COLIST_NOT_OPENED: {
3759        if (!hbhtml_coli_dummy(job)) { back = 0; }
3760      } break;
3761      case HB_COLIST_OPENED: {
3762        if (!hbhtml_coli_close(job)) { back = 0; }
3763        job->cols = HB_COLIST_NOT_OPENED;
3764      } break;
3765    }
3766  }
3767  return back;
3768}
3769
3770
3771
3772/**	Start code line numbering.
3773	Invoked from the input line handler.
3774	Invoked from the code special command.
3775	@param	job	Job structure.
3776	@return	1 on success, 0 on error.
3777*/
3778static
3779int
3780hbhtml_start_code_line_numbers(hb_job_t *job)
3781{
3782  int		 back = 1;
3783  job->coli = 1;
3784  job->cols = HB_COLIST_NONE;
3785  return back;
3786}
3787
3788
3789
3790/**	End code line numbering if it was started.
3791	Invoked from the code special command.
3792	@param	job	Job structure.
3793	@return	1 on success, 0 on error.
3794*/
3795static
3796int
3797hbhtml_end_code_line_numbers(hb_job_t *job)
3798{
3799  int		 back = 1;
3800  if (((job->options) & HB_JOB_OPT_CODE) && (0 < job->coli)) {
3801    switch (job->cols) {
3802      case HB_COLIST_OPENED: {
3803        if (EOF == fputs(hbhtml_c8_kw[148], job->of)) { back = 0; }
3804      } break;
3805    }
3806  }
3807  job->coli = 0;
3808  job->cols = HB_COLIST_NONE;
3809  return back;
3810}
3811
3812
3813
3814/**	Process the code special command.
3815	@param	job	Job structure.
3816	@param	pa	Arguments
3817*/
3818static
3819void
3820hbhtml_process_sp_code(hb_job_t *job, dkChar *pa)
3821{
3822  dk3_key_value_t	kv[10];		/* Command paramters */
3823  size_t		sz;		/* Number of command parameters */
3824  size_t		i;		/* Process all command parameters */
3825  int			act = 0;	/* Action to take */
3826  int			lineno = 0;	/* Flag: Write line numbers */
3827
3828  $? "+ hbhtml_process_sp_code"
3829
3830  /*	Get configuration defaults
3831  */
3832  if ((job->options) & HB_JOB_OPT_CODE_LINE_NUMBERS) {
3833    lineno = 1;
3834  }
3835
3836  /*	Use command options
3837  */
3838  sz = DK3_SIZEOF(kv,dk3_key_value_t);
3839  if(dk3str_to_key_value(kv,&sz,pa,job->app)) {
3840    if(sz > 0) {
3841      for(i = 0; i < sz; i++) {
3842        if(kv[i].key) {
3843	  switch(dk3str_array_abbr(hbhtml_code_keys, kv[i].key, dkT('$'), 0)) {
3844	    case 0: case 1: {
3845	      if(kv[i].val) {
3846	        if(dk3str_is_bool(kv[i].val)) {
3847		  if(dk3str_is_on(kv[i].val)) {
3848		    act = 1;
3849		  } else {
3850		    act = 2;
3851		  }
3852		} else {
3853		  act = 1;
3854		}
3855	      } else {
3856	        act = 1;
3857	      }
3858	    } break;
3859	    case 2: case 3: {
3860	      if(kv[i].val) {
3861	        if(dk3str_is_bool(kv[i].val)) {
3862		  if(dk3str_is_on(kv[i].val)) {
3863		    act = 2;
3864		  } else {
3865		    act = 1;
3866		  }
3867		} else {
3868		  act = 2;
3869		}
3870	      } else {
3871	        act = 2;
3872	      }
3873	    } break;
3874	    case 4: {
3875	      act = 0;
3876	    } break;
3877	    case 5: {
3878	      if (kv[i].val) {
3879	        if (dk3str_is_bool(kv[i].val)) {
3880		  lineno = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
3881		} else {
3882		  /* Warning: Not a boolean */
3883	          dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
3884		}
3885	      } else {
3886	        lineno = 1;
3887	      }
3888	    } break;
3889	  }
3890	}
3891      }
3892    }
3893  } else {
3894    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
3895  }
3896
3897  /*	Finish open code line numbering.
3898  */
3899  (void)hbhtml_end_code_line_numbers(job);
3900
3901  /*	Turn code mode on or off.
3902  */
3903  switch(act) {
3904    case 0: {
3905      if((job->options) & HB_JOB_OPT_CODE) {
3906        job->options &= (~(HB_JOB_OPT_CODE));
3907      } else {
3908        job->options |= HB_JOB_OPT_CODE;
3909	if (lineno) {
3910	  (void)hbhtml_start_code_line_numbers(job);
3911	}
3912      }
3913    } break;
3914    case 1: {
3915      job->options |= HB_JOB_OPT_CODE;
3916      if (lineno) {
3917        (void)hbhtml_start_code_line_numbers(job);
3918      }
3919    } break;
3920    case 2: {
3921      job->options &= (~(HB_JOB_OPT_CODE));
3922    } break;
3923  } $? "- hbhtml_process_sp_code"
3924}
3925
3926
3927
3928#if 0
3929static
3930int
3931hbhtml_check_link_for_external(dkChar *link, hb_job_t *job)
3932{
3933  size_t	 sz;
3934  int		 back = 0;
3935  if(link) {
3936    sz = dk3str_len(link);
3937    if(sz >= dk3str_len((job->kwnl)[20])) {
3938      if(0 == dk3str_ncmp(link, (job->kwnl)[20], dk3str_len((job->kwnl)[20]))) {
3939        back = 1;
3940      }
3941    }
3942    if(sz >= dk3str_len((job->kwnl)[21])) {
3943      if(0 == dk3str_ncmp(link, (job->kwnl)[21], dk3str_len((job->kwnl)[21]))) {
3944        back = 1;
3945      }
3946    }
3947  }
3948  return back;
3949}
3950#endif
3951
3952
3953
3954int
3955hbhtml_check_link_for_external(dkChar const *link)
3956{
3957  dkChar const		*ptr;		/* Traverse string */
3958  dkChar		 c;		/* Current character */
3959  int			 stm3;		/* State machine for traversal */
3960  int			 i;		/* Input classification */
3961  int			 back = 0;
3962  $? "+ hbhtml_check_link_for_external \"%!ds\"", TR_STR(link)
3963  hbhtml_stm3_reset(&stm3);
3964  ptr = link;
3965  while(*ptr) {
3966    c = *(ptr++);
3967    i = STM3_I_ANY;
3968    switch(c) {
3969      case dkT(':'): {
3970        i = STM3_I_COLON;
3971      } break;
3972      case dkT('/'): {
3973        i = STM3_I_SLASH;
3974      } break;
3975      case dkT('_'): {
3976        i = STM3_I_ASCII;
3977      } break;
3978      default: {
3979        if((dkT('a') <= c) && (dkT('z') >= c)) {
3980	  i = STM3_I_ASCII;
3981	} else {
3982	  if((dkT('A') <= c) && (dkT('Z') >= c)) {
3983	    i = STM3_I_ASCII;
3984	  } else {
3985	    if((dkT('0') <= c) && (dkT('9') >= c)) {
3986	      i = STM3_I_ASCII;
3987	    }
3988	  }
3989	}
3990      } break;
3991    }
3992    hbhtml_stm3_step(&stm3, i);
3993  }
3994  if(STM3_ST_SUCCESS == stm3) {
3995    back = 1;
3996  } $? "- hbhtml_check_link_for_external %d", back
3997  return back;
3998}
3999
4000
4001
4002/**	Process special command to build a link.
4003	@param	job	Job structure.
4004	@param	pa	Program arguments.
4005	@return	1 on success, 0 on error (continue), -1 on error (exit).
4006*/
4007static
4008int
4009hbhtml_process_sp_a(hb_job_t *job, dkChar *pa)
4010{
4011  dk3_key_value_t	 kv[25];		/* Command parameters */
4012  hb_link_t		*linkptr;		/* Link data for URL */
4013  hb_node_t		*nptr;			/* Current node */
4014  dkChar		*text	= NULL;		/* Link text */
4015  dkChar		*title	= NULL;		/* Link title */
4016  dkChar		*accessk	= NULL;	/* Accesskey argument */
4017  dkChar		*charset	= NULL;	/* Charset argument */
4018  dkChar		*coords	= NULL;		/* Coords argument */
4019  dkChar		*href	= NULL;		/* href argument */
4020  dkChar		*hrefl	= NULL;		/* hreflang argument */
4021  dkChar		*name	= NULL;		/* name argument */
4022  dkChar		*onblur	= NULL;		/* onblur argument */
4023  dkChar		*onfocus = NULL;	/* onfocus argument */
4024  dkChar		*rel	= NULL;		/* rel argument */
4025  dkChar		*rev	= NULL;		/* rev argument */
4026  dkChar		*shape	= NULL;		/* shape argument */
4027  dkChar		*tabind	= NULL;		/* tabindex argument */
4028  dkChar		*target	= NULL;		/* target argument */
4029  dkChar		*type	= NULL;		/* type argument */
4030  dkChar		*clptr	= NULL;		/* class argument */
4031  dkChar		*idptr	= NULL;		/* id argument */
4032  dkChar		*style	= NULL;		/* style argument */
4033  size_t		 sz;			/* Number of parameters */
4034  size_t		 i;			/* Traverse parameters */
4035  unsigned		 oldopt	= 0;		/* Save options */
4036  int			 external = 0;		/* Flag: External link */
4037  int			 extspec = 0;		/* Flag: External specified */
4038  int			 titlespec = 0;		/* Flag: Title specified */
4039  int			 tifl	= 0;		/* Flag: Title from link */
4040  int			 back = 0;
4041  $? "+ hbhtml_process_sp_a"
4042  nptr = job->currentnode;
4043  sz = DK3_SIZEOF(kv,dk3_key_value_t);
4044  if(dk3str_to_key_value(kv,&sz,pa,job->app)) {
4045    if(sz > 0) {
4046      for(i = 0; i < sz; i++) {
4047        if(kv[i].key) {
4048	  switch(dk3str_array_abbr(hbhtml_a_keys, kv[i].key, dkT('$'), 0)) {
4049	    case 0: {	$? ". text"
4050	      text = kv[i].val;
4051	    } break;
4052	    case 1: {	$? ". title"
4053	      title = kv[i].val; titlespec = 1;
4054	    } break;
4055	    case 2: {	$? ". accesskey"
4056	      accessk = kv[i].val;
4057	    } break;
4058	    case 3: {	$? ". charset"
4059	      charset = kv[i].val;
4060	    } break;
4061	    case 4: {	$? ". coords"
4062	      coords = kv[i].val;
4063	    } break;
4064	    case 5: {	$? ". href"
4065	      href= kv[i].val;
4066	    } break;
4067	    case 6: {	$? ". hreflang"
4068	      hrefl = kv[i].val;
4069	    } break;
4070	    case 7: {	$? ". name"
4071	      name = kv[i].val;
4072	    } break;
4073	    case 8: {	$? ". onblur"
4074	      onblur = kv[i].val;
4075	    } break;
4076	    case 9: {	$? ". onfocus"
4077	      onfocus = kv[i].val;
4078	    } break;
4079	    case 10: {	$? ". rel"
4080	      rel = kv[i].val;
4081	    } break;
4082	    case 11: {	$? ". rev"
4083	      rev = kv[i].val;
4084	    } break;
4085	    case 12: {	$? ". shape"
4086	      shape = kv[i].val;
4087	    } break;
4088	    case 13: {	$? ". tabindex"
4089	      tabind = kv[i].val;
4090	    } break;
4091	    case 14: {	$? ". target"
4092	      target = kv[i].val;
4093	    } break;
4094	    case 15: {	$? ". type"
4095	      type = kv[i].val;
4096	    } break;
4097	    case 16: {	$? ". class"
4098	      clptr = kv[i].val;
4099	    } break;
4100	    case 17: {	$? ". id"
4101	      idptr = kv[i].val;
4102	    } break;
4103	    case 18: {	$? ". style"
4104	      style = kv[i].val;
4105	    } break;
4106	    case 19: {
4107	      if(kv[i].val) {
4108	        if(dk3str_is_bool(kv[i].val)) {
4109		  if(dk3str_is_on(kv[i].val)) {
4110		    external = 1; extspec = 1;
4111		  } else {
4112		    external = 0; extspec = 1;
4113		  }
4114		}
4115	      } else {
4116	        external = 1; extspec = 1;
4117	      }
4118	    } break;
4119	    default: {
4120	      dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,27,26,kv[i].key);
4121	    } break;
4122	  }
4123	}
4124      }
4125    }
4126  } else {
4127    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
4128  }
4129  if(href) {
4130    if(!(extspec)) {
4131      external = hbhtml_check_link_for_external(href);
4132    }
4133    if(titlespec) {
4134        if(!(title)) { title = text; }
4135	if(!(title)) { title = href; tifl = 1; }
4136    }
4137    back = 1;
4138  } else {
4139    if(name) {
4140      back = 1;
4141    }
4142  }
4143  /*	Checks ok, all required information available.
4144  */
4145  if(back) {
4146    /* Write <a...> */
4147    if(href) {
4148      /* <a href=" */
4149      fputs(hbhtml_c8_kw[39], job->of);
4150      if(!hbhtml_url_output_for_text(job, href)) { back = 0; }
4151    } else {
4152      /* <a name=" */
4153      fputs(hbhtml_c8_kw[74], job->of);
4154      if(!hbhtml_url_output_for_text(job, name)) { back = 0; }
4155    }
4156    if(target) {
4157      /* " target=" */
4158      fputs(hbhtml_c8_kw[75], job->of);
4159      if(!hbhtml_url_output_for_text(job, target)) { back = 0; }
4160    } else {
4161      if(external) {
4162        /* " target="_blank */
4163        fputs(hbhtml_c8_kw[108], job->of);
4164      }
4165    }
4166    if(type) {
4167      /* " type=" */
4168      fputs(hbhtml_c8_kw[76], job->of);
4169      if(!hbhtml_url_output_for_text(job, type)) { back = 0; }
4170    }
4171    /* " class=" */
4172    fputs(hbhtml_c8_kw[77], job->of);
4173    if(clptr) {
4174      if(!hbhtml_url_output_for_text(job, clptr)) { back = 0; }
4175    } else {
4176      if(external) {
4177        /* hb_a_e */
4178        fputs(hbhtml_c8_kw[101], job->of);
4179      } else {
4180        /* hb_a_i */
4181        fputs(hbhtml_c8_kw[100], job->of);
4182      }
4183    }
4184    if(idptr) {
4185      /* " id=" */
4186      fputs(hbhtml_c8_kw[78], job->of);
4187      if(!hbhtml_url_output_for_text(job, idptr)) { back = 0; }
4188    }
4189    if(style) {
4190      /* " style=" */
4191      fputs(hbhtml_c8_kw[79], job->of);
4192      if(!hbhtml_url_output_for_text(job, style)) { back = 0; }
4193    }
4194    if(hrefl) {
4195      /* " hreflang=" */
4196      fputs(hbhtml_c8_kw[80], job->of);
4197      if(!hbhtml_url_output_for_text(job, hrefl)) { back = 0; }
4198    }
4199    if(title) {
4200      /* " title=" */
4201      fputs(hbhtml_c8_kw[81], job->of);
4202      if ((0 != external) && (0 != job->markel) && (0 < strlen(job->elmbuf))) {
4203        /* &#x */
4204        fputs(hbhtml_c8_kw[156], job->of);
4205	fputs(job->elmbuf, job->of);
4206	/* ;&nbsp; */
4207	fputs(hbhtml_c8_kw[157], job->of);
4208      }
4209      /* ##### a title, Ausgabe des href als Title */
4210      oldopt = (job->currentnode)->options;
4211      if (0 != tifl) {
4212        (job->currentnode)->options &= (~(HB_NODE_OPT_REPLACE_AMPERSAND));
4213      }
4214      if(!hbhtml_output_for_text(job, title)) { back = 0; }
4215      (job->currentnode)->options = oldopt;
4216    } else {
4217      if ((0 != external) && (0 != job->markel) && (0 < strlen(job->elmbuf))) {
4218        /* " title=" */
4219        fputs(hbhtml_c8_kw[81], job->of);
4220	/* &#x */
4221	fputs(hbhtml_c8_kw[156], job->of);
4222	fputs(job->elmbuf, job->of);
4223	/* ;&nbsp; */
4224	fputs(hbhtml_c8_kw[157], job->of);
4225      }
4226    }
4227    if(onblur) {
4228      /* " onblur=" */
4229      fputs(hbhtml_c8_kw[82], job->of);
4230      if(!hbhtml_url_output_for_text(job, onblur)) { back = 0; }
4231    }
4232    if(onfocus) {
4233      /* " onfocus=" */
4234      fputs(hbhtml_c8_kw[83], job->of);
4235      if(!hbhtml_url_output_for_text(job, onfocus)) { back = 0; }
4236    }
4237    if(rel) {
4238      /* " rel=" */
4239      fputs(hbhtml_c8_kw[84], job->of);
4240      if(!hbhtml_url_output_for_text(job, rel)) { back = 0; }
4241    }
4242    if(rev) {
4243      /* " rev=" */
4244      fputs(hbhtml_c8_kw[85], job->of);
4245      if(!hbhtml_url_output_for_text(job, rev)) { back = 0; }
4246    }
4247    if(shape) {
4248      /* " shape=" */
4249      fputs(hbhtml_c8_kw[86], job->of);
4250      if(!hbhtml_url_output_for_text(job, shape)) { back = 0; }
4251    }
4252    if(tabind) {
4253      /* " tabindex=" */
4254      fputs(hbhtml_c8_kw[87], job->of);
4255      if(!hbhtml_url_output_for_text(job, tabind)) { back = 0; }
4256    }
4257    if(accessk) {
4258      /* " accesskey=" */
4259      fputs(hbhtml_c8_kw[88], job->of);
4260      if(!hbhtml_url_output_for_text(job, accessk)) { back = 0; }
4261    }
4262    if(charset) {
4263      /* " charset=" */
4264      fputs(hbhtml_c8_kw[89], job->of);
4265      if(!hbhtml_url_output_for_text(job, charset)) { back = 0; }
4266    }
4267    if(coords) {
4268      /* " coords=" */
4269      fputs(hbhtml_c8_kw[90], job->of);
4270      if(!hbhtml_url_output_for_text(job, coords)) { back = 0; }
4271    }
4272    /* " */
4273    fputs(hbhtml_c8_kw[5], job->of);
4274    /* > */
4275    fputs(hbhtml_c8_kw[3], job->of);
4276    if(href) {
4277      if(text) {
4278        if(!hbhtml_output_for_text(job, text)) { back = 0; }
4279      } else {
4280        /* ##### Link-Text, Ausgabe des href-Attributes als Text */
4281        /*	Save options
4282	*/
4283        oldopt = (job->currentnode)->options;
4284	/*
4285		Replace ampersands
4286	*/
4287	(job->currentnode)->options &= (~(HB_NODE_OPT_REPLACE_AMPERSAND));
4288	/*
4289		Write href text as hyperlink text
4290	*/
4291        if(!hbhtml_output_for_text(job, href)) { back = 0; }
4292	/*
4293		Restore options
4294	*/
4295	(job->currentnode)->options = oldopt;
4296      }
4297    }
4298    /* </a> */
4299    fputs(hbhtml_c8_kw[41], job->of);
4300    if((href) && (external) && (text)) {
4301      /* Save link and write link number */
4302      linkptr = (hb_link_t *)dk3sto_it_find_like(nptr->i_lbyurl, href, 1);
4303      if(linkptr) {
4304        fprintf(job->of, hbhtml_c8_kw[91], (1UL + linkptr->lno));
4305      } else {
4306        linkptr = hbnode_link_new(href, nptr->nextlink, job->app);
4307	if(linkptr) {
4308	  if(dk3sto_add(nptr->s_lbyurl, linkptr)) {
4309	    nptr->nextlink += 1UL;
4310	    if(dk3sto_add(nptr->s_lbylno, linkptr)) {
4311	      fprintf(job->of, hbhtml_c8_kw[91], (1UL + linkptr->lno));
4312	    } else {
4313	      back = 0;
4314	    }
4315	  } else {
4316	    back = 0;
4317	    hbnode_link_delete(linkptr);
4318	    linkptr = NULL;
4319	  }
4320	} else {
4321	  back = 0;
4322	}
4323      }
4324    }
4325    if((!(href)) && (name)) {
4326      if(text) {
4327        if(!hbhtml_output_for_text(job, text)) { back = 0; }
4328      }
4329    }
4330    if(href) {
4331      if(!hbtool_add_url(job, href)) { back = 0; }
4332    }
4333  } $? "- hbhtml_process_sp_a %d", back
4334  return back;
4335}
4336
4337
4338
4339/**	Process menuentry special command.
4340	@param	job	Job structure.
4341	@param	pa	Arguments.
4342	@return	1 on success, 0 on error (continue), -1 on error (exit).
4343*/
4344static
4345int
4346hbhtml_process_sp_menuentry(hb_job_t *job, dkChar *pa)
4347{
4348  dk3_key_value_t	 kv[20];	/* Command parameters */
4349  dkChar 		*items[DK3_SIZEOF(kv,dk3_key_value_t)];
4350  size_t		 sz;		/* Number of command parameters */
4351  size_t		 i;		/* Traverse command parameters */
4352  size_t		 n_items = 0;	/* number of items */
4353  size_t		 cr;		/* Current item index */
4354  int			 back	 = 0;
4355  $? "+ hbhtml_process_sp_menuentry"
4356  sz = DK3_SIZEOF(kv,dk3_key_value_t);
4357  if(dk3str_to_key_value(kv,&sz,pa,job->app)) {
4358    if(sz > 0) {
4359      for(i = 0; i < sz; i++) {
4360        if(kv[i].key) {
4361	  switch(dk3str_array_abbr(hbhtml_menuentry_keys,kv[i].key,dkT('$'),0))
4362	  {
4363	    case 0: {
4364	      if(kv[i].val) {
4365	        if(n_items < DK3_SIZEOF(kv,dk3_key_value_t)) {
4366		  items[n_items++] = kv[i].val;
4367		} else {
4368		  dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 41);
4369		}
4370	      }
4371	    } break;
4372	    default: {
4373	      dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,27,26,kv[i].key);
4374	      back = 0;
4375	    } break;
4376	  }
4377	}
4378      }
4379    }
4380  } else {
4381    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
4382  }
4383  if(n_items) {
4384    back = 1;
4385    if (0 != job->tabsum) {
4386      fputs(hbhtml_c8_kw[92], job->of);
4387      if(!hbhtml_output_for_text(job, (job->msg)[60])) { back = 0; }
4388      fputs(hbhtml_c8_kw[22], job->of);
4389    } else {
4390      fputs(hbhtml_c8_kw[158], job->of);
4391    }
4392    for(cr = 0; cr < n_items; cr++) {
4393      fputs(hbhtml_c8_kw[94], job->of);
4394      if(cr) {
4395        for(i = 0; i < (cr - 1); i++) {
4396	  fputs(hbhtml_c8_kw[98], job->of);
4397	}
4398	fputs(hbhtml_c8_kw[99], job->of);
4399	fprintf(job->of, hbhtml_c8_kw[96], (unsigned)(n_items - cr));
4400	if(!hbhtml_output_for_text(job, items[cr])) { back = 0; }
4401	fputs(hbhtml_c8_kw[97], job->of);
4402      } else {
4403        fprintf(job->of, hbhtml_c8_kw[96], (unsigned)n_items);
4404	if(!hbhtml_output_for_text(job, items[cr])) { back = 0; }
4405	fputs(hbhtml_c8_kw[97], job->of);
4406      }
4407      fputs(hbhtml_c8_kw[95], job->of);
4408    }
4409    fputs(hbhtml_c8_kw[93], job->of);
4410  } else {
4411    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 42);
4412  } $? "- hbhtml_process_sp_menuentry %d", back
4413  return back;
4414}
4415
4416
4417
4418/**	Process index special command.
4419	@param	job	Job structure.
4420	@param	pa	Program arguments.
4421	@return	1 on success, 0 on error (continue), -1 on error (exit).
4422*/
4423static
4424int
4425hbhtml_process_sp_index(hb_job_t *job, dkChar *pa)
4426{
4427  dk3_key_value_t	 kv[10];	/* Command parameters */
4428  hb_node_t		*nptr;		/* Current node */
4429  hb_index_entry_t	*indptr;	/* New index entry item */
4430  dkChar		*text	= NULL;	/* Text for index entry */
4431  dkChar		*name	= NULL;	/* Name for index entry */
4432  size_t		 sz;		/* Number of command paramters */
4433  size_t		 i;		/* Traverse command parameters */
4434  int			 refer	= 0;	/* Flag: Refer to existing entry */
4435  int			 back	= 0;
4436  $? "+ hbhtml_process_sp_index"
4437  nptr = job->currentnode;
4438  sz = DK3_SIZEOF(kv,dk3_key_value_t);
4439  if(dk3str_to_key_value(kv,&sz,pa,job->app)) {
4440    if(sz > 0) {
4441      for(i = 0; i < sz; i++) {
4442        if(kv[i].key) {
4443	  switch(dk3str_array_abbr(hbhtml_index_keys, kv[i].key, dkT('$'), 0)) {
4444	    case 0: {
4445	      text = kv[i].val;
4446	    } break;
4447	    case 1: {
4448	      name = kv[i].val;
4449	    } break;
4450	    case 2: {
4451	      if(kv[i].val) {
4452	        if(dk3str_is_bool(kv[i].val)) {
4453		  if(dk3str_is_on(kv[i].val)) {
4454		    refer = 1;
4455		  } else {
4456		    refer = 0;
4457		  }
4458		} else {
4459		  /* ERROR: Not a boolean */
4460		  dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
4461		}
4462	      } else {
4463	        refer = 1;
4464	      }
4465	    } break;
4466	  }
4467	}
4468      }
4469      if(text) {
4470        if((job->options) & HB_JOB_OPT_CREATE_INDEX) {
4471          indptr = hbindex_new(
4472	    text, nptr->outFileName, name, nptr->nextindex, job->app, nptr
4473	  );
4474	  if(indptr) {
4475	    if(dk3sto_add(job->s_index, indptr)) {
4476	      back = 1;
4477	      nptr->nextindex += 1UL;
4478	      if(!((name) && (refer))) {
4479	        fputs(hbhtml_c8_kw[74], job->of);
4480	        if(!hb_index_write_anchor(job, indptr)) { back = 0; }
4481	        fputs(hbhtml_c8_kw[51], job->of);
4482	        fputs(hbhtml_c8_kw[52], job->of);
4483	      }
4484	    } else {
4485	      hbindex_delete(indptr);
4486	    }
4487	  }
4488	} else {
4489	  back = 1;
4490	}
4491      } else {
4492        /* ERROR: Missing text attribute! */
4493	dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 48);
4494      }
4495    }
4496  } else {
4497    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
4498  } $? "- hbhtml_process_sp_index %d", back
4499  return back;
4500}
4501
4502
4503
4504/**	Handle one source code line.
4505	@param	obj	Job structure.
4506	@param	il	Input line.
4507	@return	1 on success, 0 on error (continue), -1 on error (exit).
4508*/
4509static
4510int
4511hbhtml_sourcefile_line_handler(void *obj, dkChar *il)
4512{
4513  hb_job_t	*job;
4514  int		 back = 1;
4515
4516  job = (hb_job_t *)obj;
4517  dk3str_delnl(il);
4518  $? "+ hbhtml_sourcefile_line_handler \"%!ds\"", il
4519  if (job->coli) {
4520    if (0 < dk3str_len(il)) {
4521      if (!hbhtml_coli_open(job)) { back = 0; }
4522      if(!hbhtml_output_for_text(job, il)) { back = 0; }
4523      if (!hbhtml_coli_close(job)) { back = 0; }
4524    } else {
4525      if (!hbhtml_coli_dummy(job)) { back = 0; }
4526    }
4527  } else {
4528    if(!hbhtml_output_for_text(job, il)) { back = 0; }
4529  }
4530  fputc('\n', job->of);
4531  $? "- hbhtml_sourcefile_line_handler %d", back
4532  return back;
4533}
4534
4535
4536
4537/**	Process sourcefile special command.
4538	@param	job	Job structure.
4539	@param	pa	Program arguemnts.
4540	@return	1 on success, 0 on error (continue), -1 on error (exit).
4541*/
4542static
4543int
4544html_process_sp_sourcefile(hb_job_t *job, dkChar *pa)
4545{
4546  dkChar		 bu[4096];	/* Buffer for source file lines */
4547  dk3_key_value_t	 kv[10];	/* Command parameters */
4548  dkChar		*file	= NULL;	/* Input file */
4549  dkChar		*type	= NULL;	/* Type argument */
4550  dkChar		*title	= NULL;	/* Title argument */
4551  size_t		 sz;		/* Number of command parameters */
4552  size_t		 i;		/* Traverse command parameters */
4553  int			 lineno = 0;	/* Flag: Write line numbers */
4554  int		 	 back = 0;
4555  int			 res;		/* Result from processing file */
4556  int			 oldcode;	/* Old code flag value */
4557  $? "+ html_process_sp_sourcefile"
4558  if ((job->options) & HB_JOB_OPT_CODE_LINE_NUMBERS) {
4559    lineno = 1;
4560  }
4561  sz = DK3_SIZEOF(kv,dk3_key_value_t);
4562  if(dk3str_to_key_value(kv, &sz, pa, job->app)) {
4563    if(sz > 0) {
4564      for(i = 0; i < sz; i++) {
4565        if(kv[i].key) {
4566	  switch(dk3str_array_abbr(hbhtml_sourcef_keys,kv[i].key,dkT('$'),0)) {
4567	    case 0: {
4568	      file = kv[i].val;
4569	    } break;
4570	    case 1: {
4571	      type = kv[i].val;
4572	    } break;
4573	    case 2: {
4574	      title = kv[i].val;
4575	    } break;
4576	    case 3: {
4577	      if (kv[i].val) {
4578	        if (dk3str_is_bool(kv[i].val)) {
4579		  lineno = ((dk3str_is_on(kv[i].val)) ? 1 : 0);
4580		} else {
4581		  /* WARNING: Not a boolean */
4582	          dk3app_log_i3(job->app, DK3_LL_WARNING, 146, 144, kv[i].val);
4583		}
4584	      } else {
4585	        lineno = 1;
4586	      }
4587	    } break;
4588	  }
4589	}
4590      }
4591    }
4592    if(file) {
4593      back = 1;
4594      if(hbtool_add_file(job, file)) {
4595        fputs(hbhtml_c8_kw[102], job->of);
4596	fputs(hbhtml_c8_kw[103], job->of);
4597	if(!hbhtml_url_output_for_text(job, file)) { back = 0; }
4598	fputs(hbhtml_c8_kw[81], job->of);
4599	if(title) {
4600	  if(!hbhtml_output_for_text(job, title)) { back = 0; }
4601	} else {
4602	  if(!hbhtml_output_for_text(job, file)) { back = 0; }
4603	}
4604	fputs(hbhtml_c8_kw[76], job->of);
4605	if(type) {
4606	  if(!hbhtml_output_for_text(job, type)) { back = 0; }
4607	} else {
4608	  fputs(hbhtml_c8_kw[104], job->of);
4609	}
4610	fputs(hbhtml_c8_kw[51], job->of);
4611	if(!hbhtml_output_for_text(job, file)) { back = 0; }
4612	fputs(hbhtml_c8_kw[52], job->of);
4613	fputs(hbhtml_c8_kw[105], job->of);
4614	fputs(hbhtml_c8_kw[106], job->of);
4615	oldcode = (((job->options) & HB_JOB_OPT_CODE) ? 1 : 0);
4616	job->options |= HB_JOB_OPT_CODE;
4617	job->coli = ((lineno) ? 1 : 0);
4618	res = dk3stream_process_filename_lines_app(
4619	  (void *)job, hbhtml_sourcefile_line_handler, file,
4620	  bu, DK3_SIZEOF(bu,dkChar),
4621	  dk3app_get_encoding(job->app),
4622	  dk3app_get_input_file_encoding(job->app),
4623	  job->app
4624	);
4625	job->coli = 0;
4626	if(!(res)) { back = 0; }
4627	if(oldcode) {
4628	  job->options |= HB_JOB_OPT_CODE;
4629	} else {
4630	  job->options &= (~(HB_JOB_OPT_CODE));
4631	}
4632	fputs(hbhtml_c8_kw[107], job->of);
4633	fputs(hbhtml_c8_kw[47], job->of);
4634      } else {
4635        back = 0;
4636      }
4637    } else {
4638      /* ERROR: No file name specified! */
4639      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 49);
4640    }
4641  } else {
4642    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
4643  } $? "- html_process_sp_sourcefile %d", back
4644  return back;
4645}
4646
4647
4648
4649/**	Process sourcefile special command.
4650	@param	job	Job structure.
4651	@return	1 on success, 0 on error (continue), -1 on error (exit).
4652*/
4653static
4654int
4655html_process_sp_htmlname(hb_job_t *job)
4656{
4657  dkChar	 buf[DK3_MAX_PATH];
4658  size_t	 szbuf	= DK3_SIZEOF(buf,dkChar);
4659  int		 back	= 0;
4660  $? "+ html_process_sp_htmlname"
4661  if (NULL != job->currentnode) {
4662    if (0 != hbhtml_create_output_filename(buf, szbuf, job, job->currentnode)) {
4663      back = hbhtml_url_output_for_text(job, buf);
4664      if (0 == back) {				$? "! failed to write text"
4665        job->exv = 1;
4666      }
4667    } else {					$? "! filename conversion"
4668      job->exv = 1;
4669    }
4670  } else {					$? "! no current node"
4671  }
4672  $? "- html_process_sp_htmlname %d", back
4673  return back;
4674}
4675
4676
4677
4678/**	Process sourcefile special command.
4679	@param	job	Job structure.
4680	@param	pa	Program arguemnts.
4681	@return	1 on success, 0 on error (continue), -1 on error (exit).
4682*/
4683static
4684int
4685html_process_sp_hint(hb_job_t *job, dkChar *pa)
4686{
4687  dk3_key_value_t	 kv[30];
4688  dkChar		*hint_text	= NULL;
4689  dkChar		*hint_title	= NULL;
4690  size_t		 sz;
4691  size_t		 i;
4692  int			 back		= 0;
4693
4694  sz = DK3_SIZEOF(kv,dk3_key_value_t);
4695  if (0 != dk3str_to_key_value(kv, &sz, pa, job->app)) {
4696    for (i = 0; i < sz; i++) {
4697      if (NULL != kv[i].key) {
4698        switch (dk3str_array_abbr(hbhtml_hint_keys, kv[i].key, dkT('$'), 0)) {
4699	  case 0: {
4700	    hint_text = kv[i].val;
4701	  } break;
4702	  case 1: {
4703	    hint_title = kv[i].val;
4704	  } break;
4705	  default: {
4706	    /* Warning: Unusable key */
4707	    dk3app_log_3(job->app, DK3_LL_WARNING, job->msg, 89, 90, kv[i].key);
4708	  } break;
4709	}
4710      }
4711    }
4712    if (NULL != hint_text) {
4713      back = 1;
4714      if (NULL != hint_title) {
4715        if (EOF == fputs(hbhtml_c8_kw[151], job->of)) {
4716	  back = 0;
4717	}
4718        if (0 ==hbhtml_output_for_text(job, hint_title)) {
4719	  back = 0;
4720	}
4721	if (EOF == fputs(hbhtml_c8_kw[152], job->of)) {
4722	  back = 0;
4723	}
4724        if (0 == hbhtml_output_for_text(job, hint_text)) {
4725	  back = 0;
4726	}
4727	if (EOF == fputs(hbhtml_c8_kw[153], job->of)) {
4728	  back = 0;
4729	}
4730      }
4731      else {
4732        dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 92);
4733	if (0 == hbhtml_output_for_text(job, hint_text)) {
4734	  back = 0;
4735	}
4736      }
4737    }
4738    else {
4739      /* ERROR: No text */
4740      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 91);
4741    }
4742  }
4743  else {
4744    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 40);
4745  }
4746  if (0 == back) {
4747    job->exv = 1;
4748  }
4749  return back;
4750}
4751
4752
4753
4754/**	Process a special command.
4755	@param	job		Job structure.
4756	@param	in_template	Flag: Processing a template.
4757	@param	p		Previous node.
4758	@param	n		Next node.
4759	@return	1 on success, 0 on error (continue), -1 on error (exit).
4760*/
4761static
4762int
4763hbhtml_process_special(
4764  hb_job_t	*job,
4765  int		 in_template,
4766  hb_node_t	*p,
4767  hb_node_t	*n
4768)
4769{
4770  dkChar		*pc;		/* Special command name */
4771  dkChar		*pa;		/* Start of command arguments */
4772  dkChar		*sc;		/* Start of sub command. */
4773  int			 ct;		/* Command identifier */
4774  int			 back = 0;
4775  $? "+ hbhtml_process_special"
4776  pc = dk3str_start(job->special, NULL);
4777  sc = NULL;
4778  if(pc) {
4779    pa = dk3str_next(pc, NULL);
4780    if(pa) {
4781      dk3str_chomp(pa, NULL);
4782    }
4783    sc = dk3str_chr(pc, dkT('.'));
4784    if(sc) { *(sc++) = dkT('\0'); }
4785    ct = dk3str_array_index(hbhtml_special_commands, pc, 0);
4786    $? ". ct = %d", ct
4787    switch(ct) {
4788      case 0: {		$? ". contents"
4789        if(in_template) {
4790	  back = hbhtml_process_sp_contents(job, p, n);
4791	} else {
4792	  /* ERROR: Contents special not allowed in contents! */
4793	  dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 28);
4794	}
4795      } break;
4796      case 1: {		$? ". title"
4797        back = hbhtml_process_sp_title(job);
4798        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4799      } break;
4800      case 2: {		$? ". image"
4801        back = hbhtml_process_sp_image(job, pa, sc);
4802        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4803      } break;
4804      case 3: {		$? ". menu"
4805        back = hbhtml_process_sp_menu(job, pa);
4806        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4807      } break;
4808      case 4: {		$? ". navigation"
4809        back = hbhtml_process_sp_navigation(job, pa, p, n);
4810        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4811      } break;
4812      case 5: {	/* links */
4813        /* #### Probably not */
4814	back = 1;
4815      } break;
4816      case 6: {		$? ". author"
4817        back = hbhtml_process_sp_author(job);
4818        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4819      } break;
4820      case 7: {		$? ". location"
4821        back = hbhtml_process_sp_location(job);
4822        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4823      } break;
4824      case 8: {		$? ". date"
4825        back = hbhtml_process_sp_date(job, pa);
4826        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4827      } break;
4828      case 9: {		$? ". variable"
4829        back = hbhtml_process_sp_variable(job, pa);
4830        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4831      } break;
4832      case 10: {
4833        hbhtml_process_sp_code(job, pa);
4834	back = 1;
4835      } break;
4836      case 11: {
4837        back = hbhtml_process_sp_a(job, pa);
4838        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4839      } break;
4840      case 12: {
4841        back = hbhtml_process_sp_menuentry(job, pa);
4842        if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4843      } break;
4844      case 13: {
4845        if(0 == in_template) {
4846          back = hbhtml_process_sp_index(job, pa);
4847	  if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4848	} else {
4849	  /* ERROR: Can not be used in template */
4850	  dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 50);
4851	}
4852      } break;
4853      case 14: {
4854        back = html_process_sp_sourcefile(job, pa);
4855	if(1 != back) { dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 39); }
4856      } break;
4857      case 15: {
4858        back = html_process_sp_htmlname(job);
4859      } break;
4860      case 16: {
4861        back = html_process_sp_hint(job, pa);
4862      } break;
4863      default: {	$? "! unknown command"
4864        back = 0;
4865	/* ERROR: Unknown special command. */
4866	dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,29,26,pc);
4867      } break;
4868    }
4869  } else {
4870    back = 1;	/* Empty special */
4871  }
4872  $? "- hbhtml_process_special %d", back
4873  return back;
4874}
4875
4876
4877
4878int
4879hbhtml_line_handler(void *obj, dkChar *il)
4880{
4881  dkChar		 xmsg[8];	/* Array for diagnostics */
4882  hb_file_processor_t	*proc;		/* Line processor */
4883  hb_job_t		*job;		/* Job */
4884  dkChar		*p1;		/* Start of input line */
4885  size_t		 pos;		/* Non-whitespace position */
4886  int			 i;		/* Input classification */
4887  int			 o;		/* Reaction from state machine */
4888  int			 ltl	= 0;	/* Flag: Error from shipout */
4889  int			 back	= 1;
4890  dkChar		 c;		/* Character to ship out */
4891  $? "+ hbhtml_line_handler \"%!ds\"", il
4892  pos = 0;
4893  proc = (hb_file_processor_t *)obj;
4894  job = proc->job;
4895  dk3str_delnl(il);
4896  hbhtml_stm1_reset(&(proc->stm));
4897  job->shipused = 0;
4898  job->spused = 0;
4899  p1 = il;
4900  while(*p1) {
4901    c = *p1;
4902    i = hbhtml_classify_for_stm1(c);
4903    o = hbhtml_stm1_step(&(proc->stm), i);
4904    switch(o) {
4905      case STM1_O_ERROR: {
4906        /* ERROR: Parse error */
4907	dk3app_log_3(job->app,DK3_LL_ERROR,job->msg,30,26,p1);
4908        back = -1;				$? "! FATAL ERROR"
4909      } break;
4910      case STM1_O_NOTHING: {
4911      } break;
4912      case STM1_O_SHIPOUT: {
4913        if (!hbhtml_code_line_start(job)) { back = 0; }
4914        if(!(hbhtml_shipout(job, c))) {
4915	  back = 0;				$? "! ERROR"
4916	  if(0 == ltl) {
4917	    ltl = 1;
4918	    /* ERROR: Line too long! */
4919	  }
4920	}
4921      } break;
4922      case STM1_O_BS_SHIPOUT: {
4923        switch(c) {
4924	  case dkT('n'): {
4925            if (!hbhtml_code_line_start(job)) { back = 0; }
4926	    if(!(hbhtml_shipout(job, dkT('\n')))) {
4927	      back = 0;				$? "! ERROR"
4928	      if(0 == ltl) {
4929	        ltl = 1;
4930	        /* ERROR: Line too long! */
4931	      }
4932	    }
4933	  } break;
4934	  case dkT('r'): {
4935            if (!hbhtml_code_line_start(job)) { back = 0; }
4936	    if(!(hbhtml_shipout(job, dkT('\r')))) {
4937	      back = 0;				$? "! ERROR"
4938	      if(0 == ltl) {
4939	        ltl = 1;
4940	        /* ERROR: Line too long! */
4941	      }
4942	    }
4943	  } break;
4944	  case dkT('t'): {
4945            if (!hbhtml_code_line_start(job)) { back = 0; }
4946	    if(!(hbhtml_shipout(job, dkT('\t')))) {
4947	      back = 0;				$? "! ERROR"
4948	      if(0 == ltl) {
4949	        ltl = 1;
4950	        /* ERROR: Line too long! */
4951	      }
4952	    }
4953	  } break;
4954	  case dkT('%'): case dkT('\\'): {
4955            if (!hbhtml_code_line_start(job)) { back = 0; }
4956	    if(!(hbhtml_shipout(job, c))) {
4957	      back = 0;				$? "! ERROR"
4958	      if(0 == ltl) {
4959	        ltl = 1;
4960	        /* ERROR: Line too long! */
4961	      }
4962	    }
4963	  } break;
4964	  case dkT('.'): {
4965	    /*	We can escape a leading dot.
4966	    */
4967	    if(1 < pos) {
4968	      xmsg[0] = dkT('\\');
4969	      xmsg[1] = c;
4970	      xmsg[2] = dkT('\0');
4971	      dk3app_log_3(job->app, DK3_LL_WARNING, job->msg, 63, 64, xmsg);
4972	    }
4973            if (!hbhtml_code_line_start(job)) { back = 0; }
4974	    if(!(hbhtml_shipout(job, c))) {
4975	      back = 0;				$? "! ERROR"
4976	      if(0 == ltl) {
4977	        ltl = 1;
4978	        /* ERROR: Line too long! */
4979	      }
4980	    }
4981	  } break;
4982	  case dkT('#'): {
4983	    /*	We can escape a leading raute.
4984	    */
4985	    if(1 < pos) {
4986	      xmsg[0] = dkT('\\');
4987	      xmsg[1] = c;
4988	      xmsg[2] = dkT('\0');
4989	      dk3app_log_3(job->app, DK3_LL_WARNING, job->msg, 63, 64, xmsg);
4990	    }
4991            if (!hbhtml_code_line_start(job)) { back = 0; }
4992	    if(!(hbhtml_shipout(job, c))) {
4993	      back = 0;				$? "! ERROR"
4994	      if(0 == ltl) {
4995	        ltl = 1;
4996	        /* ERROR: Line too long! */
4997	      }
4998	    }
4999	  } break;
5000	  default: {
5001	    xmsg[0] = dkT('\\');
5002	    xmsg[1] = c;
5003	    xmsg[2] = dkT('\0');
5004	    dk3app_log_3(job->app, DK3_LL_WARNING, job->msg, 63, 64, xmsg);
5005            if (!hbhtml_code_line_start(job)) { back = 0; }
5006	    if(!(hbhtml_shipout(job, c))) {
5007	      back = 0;				$? "! ERROR"
5008	      if(0 == ltl) {
5009	        ltl = 1;
5010	        /* ERROR: Line too long! */
5011	      }
5012	    }
5013	  } break;
5014	}
5015      } break;
5016      case STM1_O_PERCENT_SHIPOUT: {
5017        if (!hbhtml_code_line_start(job)) { back = 0; }
5018        if(!(hbhtml_shipout(job, dkT('%')))) {
5019	  back = 0;				$? "! ERROR"
5020	  if(0 == ltl) {
5021	    ltl = 1;
5022	  }
5023	}
5024	if(!(hbhtml_shipout(job, c))) {
5025	  back = 0;				$? "! ERROR"
5026	  if(0 == ltl) {
5027	    ltl = 1;
5028	  }
5029	}
5030      } break;
5031      case STM1_O_PERCENT: {
5032        if (!hbhtml_code_line_start(job)) { back = 0; }
5033        if(!(hbhtml_shipout(job, dkT('%')))) {
5034	  back = 0;				$? "! ERROR"
5035	  if(0 == ltl) {
5036	    ltl = 1;
5037	  }
5038	}
5039      } break;
5040      case STM1_O_SWITCH: {
5041        hbhtml_flush_shipout(job);
5042	job->spused = 0;
5043      } break;
5044      case STM1_O_SPECIAL: {
5045        if(!hbhtml_add_to_special(job, c)) {
5046	  back = 0;				$? "! ERROR"
5047	  if(0 == ltl) {
5048	    ltl = 1;
5049	  }
5050	}
5051      } break;
5052      case STM1_O_SPEC_BS: {
5053        switch(c) {
5054	  case dkT('n'): {
5055	    if(!hbhtml_add_to_special(job, dkT('\n'))) {
5056	      back = 0;				$? "! ERROR"
5057	      if(0 == ltl) {
5058	        ltl = 1;
5059	      }
5060	    }
5061	  } break;
5062	  case dkT('r'): {
5063	    if(!hbhtml_add_to_special(job, dkT('\r'))) {
5064	      back = 0;				$? "! ERROR"
5065	      if(0 == ltl) {
5066	        ltl = 1;
5067	      }
5068	    }
5069	  } break;
5070	  case dkT('t'): {
5071	    if(!hbhtml_add_to_special(job, dkT('\t'))) {
5072	      back = 0;				$? "! ERROR"
5073	      if(0 == ltl) {
5074	        ltl = 1;
5075	      }
5076	    }
5077	  } break;
5078	  default: {
5079	    if(!hbhtml_add_to_special(job, c)) {
5080	      back = 0;				$? "! ERROR"
5081	      if(0 == ltl) {
5082	        ltl = 1;
5083	      }
5084	    }
5085	  } break;
5086	}
5087      } break;
5088      case STM1_O_SPEC_PC_ADD: {
5089        if(!hbhtml_add_to_special(job, dkT('%'))) {
5090	  back = 0;				$? "! ERROR"
5091	  if(0 == ltl) {
5092	    ltl = 1;
5093	  }
5094	}
5095        if(!hbhtml_add_to_special(job, c)) {
5096	  back = 0;				$? "! ERROR"
5097	  if(0 == ltl) {
5098	    ltl = 1;
5099	  }
5100	}
5101      } break;
5102      case STM1_O_SPEC_PC: {
5103        if(!hbhtml_add_to_special(job, dkT('%'))) {
5104	  back = 0;				$? "! ERROR"
5105	  if(0 == ltl) {
5106	    ltl = 1;
5107	  }
5108	}
5109      } break;
5110      case STM1_O_EXECUTE: {
5111        if((job->spused) < (job->bs)) {
5112	  (job->special)[job->spused] = dkT('\0');
5113	  back = hbhtml_process_special(job,proc->in_template,proc->p,proc->n);
5114	} else {
5115	  back = 0;				$? "! ERROR"
5116	  if(0 == ltl) {
5117	    ltl = 1;
5118	  }
5119	}
5120        job->spused = 0;
5121	job->shipused = 0;
5122      } break;
5123    }
5124    p1++;
5125    if((dkT(' ') != c) && (dkT('\t') != c)) {
5126      pos++;
5127    }
5128  }
5129  switch(proc->stm) {
5130    case STM1_ST_BS: {
5131      if (!hbhtml_code_line_start(job)) { back = 0; }
5132      if(!(hbhtml_shipout(job, dkT('\\')))) {
5133        back = 0;
5134        if(0 == ltl) { ltl = 1; }
5135      }
5136    } break;
5137    case STM1_ST_PERCENT: {
5138      if (!hbhtml_code_line_start(job)) { back = 0; }
5139      if(!(hbhtml_shipout(job, dkT('%')))) {
5140        back = 0;
5141	if(0 == ltl) { ltl = 1; }
5142      }
5143    } break;
5144    case STM1_ST_SPECIAL: case STM1_ST_SPEC_BS: case STM1_ST_SPEC_PC: {
5145      /* ERROR: Unfinished special command! */
5146      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 62);
5147      back = 0;
5148    } break;
5149  }
5150  if(ltl) {
5151    dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 31, 26, il);
5152  }
5153  hbhtml_flush_shipout(job);
5154  if (!hbhtml_code_line_end(job)) { back = 0; }
5155  if (EOF == fputc('\n', job->of)) {
5156    back = 0;				$? "! ERROR Newline"
5157  }
5158  $? "- hbhtml_line_handler %d", back
5159  return back;
5160}
5161
5162
5163
5164/**	Write JS files to HTML header.
5165	@param	job	Job structure.
5166	@param	nptr	Node to write JS file list for.
5167	@param	htdt	Document type.
5168	@param	ptocjs	Flag: Write JS file for page toc.
5169	@return	1 on success, 0 on error.
5170*/
5171static
5172int
5173hbhtml_write_js_files(hb_job_t *job, hb_node_t *nptr, int htdt, int ptocjs)
5174{
5175  hb_link_t		*linkptr;	/* File name data for JavaScript file */
5176  int			 back = 1;
5177  $? "+ hbhtml_write_js_files"
5178  /*	Write parent JS files.
5179  */
5180  if(nptr->parent) {
5181    if(!((nptr->options) & HB_NODE_OPT_STOP_JS_INHERITANCE)) {
5182      hbhtml_write_js_files(job, nptr->parent, htdt, 0);
5183    }
5184  }
5185
5186  /*	Write node JS files.
5187  */
5188  if((nptr->s_jsfiles) && (nptr->i_jsfiles)) {
5189    dk3sto_it_reset(nptr->i_jsfiles);
5190    while(NULL != (linkptr = (hb_link_t *)dk3sto_it_next(nptr->i_jsfiles))) {
5191      if(linkptr->url) {
5192        fputs(hbhtml_c8_kw[121], job->of);
5193	if(!hbhtml_url_output_for_text(job, linkptr->url)) { back = 0; }
5194	fputs(
5195	  hbhtml_c8_kw[
5196	    (HTML_DOCTYPE_5 == htdt)
5197	    ? (
5198	      (0 != (LINK_FLAG_SCRIPT_ASYNC & (linkptr->flags)))
5199	      ? (167) : (166)
5200	    )
5201	    : (
5202	      (0 != (LINK_FLAG_SCRIPT_ASYNC & (linkptr->flags)))
5203	      ? (168) : (122)
5204	    )
5205	  ],
5206	  job->of
5207	);
5208      }
5209    }
5210  }
5211
5212  /*	Write script code to fold and unfold page table of contents
5213  */
5214  if (0 != (HB_NODE_OPT_PAGE_TOC & (nptr->options))) {
5215    if (0 != (HB_JOB_OPT_FOLD_PTOC & (job->options))) {
5216      if ((0 != ptocjs) && (NULL != job->ptocjsfile)) {
5217        job->ptocjsused = 1;
5218	/*
5219		script-tag
5220	*/
5221	fputs(
5222	  hbhtml_c8_kw[(HTML_DOCTYPE_5 == htdt) ? (170) : (169)],
5223	  job->of
5224	);
5225	/*	Script text
5226	*/
5227	if(!hbhtml_url_output_for_text(job, job->ptocjsfile)) { back = 0; }
5228	/*
5229		End script-tag
5230	*/
5231	fputs(hbhtml_c8_kw[166], job->of);
5232      }
5233    }
5234  }
5235  $? "- hbhtml_write_js_files"
5236  return back;
5237}
5238
5239
5240
5241/**	Write one head navigation entry.
5242	@param	job		Job structure.
5243	@param	nptr		Node (may be NULL for impressum or privacy).
5244	@param	url		URL to use if nptr is NULL.
5245	@param	titleind	Title index if nptr is NULL.
5246	@param	linkkwind	Link keyword index.
5247	@param	appendind	Index for keyword index.
5248*/
5249static
5250void
5251hbhtml_one_head_navigation(
5252  hb_job_t	*job,
5253  hb_node_t	*nptr,
5254  dkChar const	*url,
5255  size_t	 titleind,
5256  size_t	 linkkwind,
5257  size_t	 appendind
5258)
5259{
5260  dkChar	 bu[DK3_MAX_PATH];	/* Buffer for output file name */
5261  hb_node_t	*jnptr;			/* Jump node */
5262
5263  if(nptr) {
5264    jnptr = nptr;
5265    if(!(jnptr->filename)) { jnptr = jnptr->jumpnode; }
5266    if(hbhtml_create_output_filename(bu, DK3_SIZEOF(bu,dkChar), job, jnptr)) {
5267      fputs(hbhtml_c8_kw[123], job->of);
5268      fputs(hbhtml_c8_kw[linkkwind], job->of);
5269      fputs(hbhtml_c8_kw[124], job->of);
5270      if(!hbhtml_output_for_text(job, jnptr->title)) { job->exv = 1; }
5271      fputs(hbhtml_c8_kw[125], job->of);
5272      if(!hbhtml_url_output_for_text(job, bu)) { job->exv = 1; }
5273      if(appendind) {
5274        fputs(hbhtml_c8_kw[appendind], job->of);
5275      }
5276      fputs(hbhtml_c8_kw[126], job->of);
5277    } else {
5278      job->exv = 1;
5279    }
5280  } else {
5281    fputs(hbhtml_c8_kw[123], job->of);
5282    fputs(hbhtml_c8_kw[linkkwind], job->of);
5283    fputs(hbhtml_c8_kw[124], job->of);
5284    if(!hbhtml_output_for_text(job, (job->msg)[titleind])) { job->exv = 1; }
5285    fputs(hbhtml_c8_kw[125], job->of);
5286    if(!hbhtml_url_output_for_text(job, url)) { job->exv = 1; }
5287    if(appendind) {
5288      fputs(hbhtml_c8_kw[appendind], job->of);
5289    }
5290    fputs(hbhtml_c8_kw[126], job->of);
5291  }
5292}
5293
5294
5295
5296/**	Write navigation entries to HTML header.
5297	@param	job	Job structure.
5298	@param	node	Current node to write.
5299	@param	prev	Previous node.
5300	@param	next	Next node.
5301*/
5302static
5303void
5304hbhtml_write_head_navigation(
5305  hb_job_t	*job,
5306  hb_node_t	*node,
5307  hb_node_t	*prev,
5308  hb_node_t	*next
5309)
5310{
5311  hb_node_t	*nptr;	/* Current node to process */
5312
5313  /*	Navigation (links)
5314  	author (impressum), contents, index, top, up, first, prev, next, last
5315	link rel="..." title="..." href="..."
5316  */
5317  if(job->impressumnode) {
5318    nptr = job->impressumnode;
5319    hbhtml_one_head_navigation(job, nptr, NULL, 0, 127, 0);
5320  } else {
5321    if(job->impressumlink) {
5322      hbhtml_one_head_navigation(job, NULL, job->impressumlink, 61, 127, 0);
5323    }
5324  }
5325  if(job->privacynode) {
5326    nptr = job->privacynode;
5327    hbhtml_one_head_navigation(job, nptr, NULL, 0, 127, 0);
5328  } else {
5329    if(job->privacylink) {
5330      hbhtml_one_head_navigation(job, NULL, job->privacylink, 104, 127, 0);
5331    }
5332  }
5333  if(job->rootnode) {
5334    hbhtml_one_head_navigation(
5335      job, job->rootnode, NULL, 0, 128,
5336      (
5337      	(
5338      	  (0 != ((job->options) & HB_JOB_OPT_CREATE_TOC))
5339	  && (0 == ((job->options) & HB_JOB_OPT_CHM))
5340	)
5341	? 136 : 0
5342      )
5343    );
5344    hbhtml_one_head_navigation(
5345      job, job->rootnode, NULL, 0, 129,
5346      (
5347      	(
5348      	  (0 != ((job->options) & HB_JOB_OPT_CREATE_INDEX))
5349	  && (0 == ((job->options) & HB_JOB_OPT_CHM))
5350	)
5351	? 137 : 0
5352      )
5353    );
5354    hbhtml_one_head_navigation(job, job->rootnode, NULL, 0, 130, 0);
5355  }
5356  nptr = node;
5357  nptr = nptr->parent;
5358  if(nptr) {
5359    while((!(nptr->filename)) && (nptr->parent)) { nptr = nptr->parent; }
5360    if(nptr) {
5361      hbhtml_one_head_navigation(job, nptr, NULL, 0, 131, 0);
5362    }
5363  }
5364  if(job->firstnode) {
5365    hbhtml_one_head_navigation(job, job->firstnode, NULL, 0, 132, 0);
5366  }
5367  if(prev) {
5368    hbhtml_one_head_navigation(job, prev, NULL, 0, 133, 0);
5369  }
5370  if(next) {
5371    hbhtml_one_head_navigation(job, next, NULL, 0, 134, 0);
5372  }
5373  if(job->lastnode) {
5374    hbhtml_one_head_navigation(job, job->lastnode, NULL, 0, 135, 0);
5375  }
5376}
5377
5378
5379
5380/**	Write HTML header
5381	@param	job	Job structure.
5382	@param	nodeptr	Current node to write.
5383	@param	prev	Previous node.
5384	@param	next	Next node.
5385	@param	htdt	HTML doc type to use.
5386*/
5387static
5388void
5389hbhtml_write_header(
5390  hb_job_t *job, hb_node_t *nodeptr, hb_node_t *prev, hb_node_t *next, int htdt
5391)
5392{
5393  dkChar const	*ptr;		/* Multiple text elements */
5394  $? "+ hbhtml_write_header"
5395  fputs(hbhtml_c8_kw[15], job->of);
5396  /*
5397  	Meta charset, viewport
5398  */
5399  if (HTML_DOCTYPE_5 == htdt) {
5400    if (HB_CS_UTF_8 == job->cs) {
5401      fputs(hbhtml_c8_kw[163], job->of);
5402    }
5403    fputs(hbhtml_c8_kw[164], job->of);
5404  }
5405  /*	Title
5406  */
5407  fputs(hbhtml_c8_kw[19], job->of);
5408#if	1
5409  if (((job->options) & HB_JOB_OPT_NUM_IN_PAGE_HEADER)) {
5410    if (!((job->options) & HB_JOB_OPT_CHM)) {
5411      if (NULL != job->currentnode) {
5412        if((job->currentnode)->depth < job->headlevels) {
5413          if(hbtool_write_header_number(job, job->currentnode)) {
5414	    hbhtml_output_for_text(job, (job->kwnl[35]));
5415	  }
5416        }
5417      }
5418    }
5419  }
5420#endif
5421  hbhtml_output_for_text(job, hbconf_title(nodeptr));
5422  fputs(hbhtml_c8_kw[20], job->of);
5423  /*	Generator (comment)
5424  */
5425  if((nodeptr->options) & HB_NODE_OPT_USE_TIDY) {
5426    fputs(hbhtml_c8_kw[17], job->of);
5427  }
5428  /*	Generator (meta)
5429  */
5430  fputs(hbhtml_c8_kw[18], job->of);
5431  /*	Style (link)
5432  */
5433  ptr = hbconf_stylefile(nodeptr);
5434  if(ptr) {
5435    if(!hbtool_add_file(job, ptr)) { job->exv = 1; }
5436    fputs(hbhtml_c8_kw[(HTML_DOCTYPE_5 == htdt) ? (165) : (21)], job->of);
5437    hbhtml_url_output_for_text(job, ptr);
5438    fputs(hbhtml_c8_kw[22], job->of);
5439  }
5440  /*	Shortcut icon (link)
5441  */
5442  ptr = hbconf_favicon(nodeptr);
5443  if(ptr) {
5444    fputs(hbhtml_c8_kw[27], job->of);
5445    hbhtml_url_output_for_text(job, ptr);
5446    fputs(hbhtml_c8_kw[28], job->of);
5447  }
5448  /*	Navigation (links)
5449  */
5450  hbhtml_write_head_navigation(job, nodeptr, prev, next);
5451  /*	Character set (meta)
5452  */
5453  if (HTML_DOCTYPE_5 != htdt) {
5454    if(HB_CS_UTF_8 == job->cs) {
5455      fputs(hbhtml_c8_kw[24], job->of);
5456    } else {
5457      fputs(hbhtml_c8_kw[23], job->of);
5458    }
5459  }
5460  /*	Author (meta)
5461  */
5462  ptr = hbconf_author(nodeptr);
5463  if(ptr) {
5464    fputs(hbhtml_c8_kw[25], job->of);
5465    hbhtml_output_for_text(job, ptr);
5466    fputs(hbhtml_c8_kw[26], job->of);
5467  }
5468  /*	Meta description tag
5469  */
5470  if (NULL != nodeptr->metadesc) {
5471    fputs(hbhtml_c8_kw[154], job->of);
5472    hbhtml_output_for_text(job, nodeptr->metadesc);
5473    fputs(hbhtml_c8_kw[26], job->of);
5474  }
5475  /*	Meta keywords tag.
5476  */
5477  if (NULL != nodeptr->metakeyw) {
5478    fputs(hbhtml_c8_kw[155], job->of);
5479    hbhtml_output_for_text(job, nodeptr->metakeyw);
5480    fputs(hbhtml_c8_kw[26], job->of);
5481  }
5482  /*	JavaScript files.
5483  */
5484  (void)hbhtml_write_js_files(job, nodeptr, htdt, 1);
5485  /*
5486  	End header tag
5487  */
5488  fputs(hbhtml_c8_kw[16], job->of); $? "- hbhtml_write_header"
5489}
5490
5491
5492
5493/**	Line handler processing function.
5494	@param	obj	Handler object.
5495	@param	il	Input line to process.
5496	@return	1 on success, 0 on error (continue), -1 on error (exit).
5497*/
5498static
5499int
5500hbhtml_spcl_handler(void *obj, dkChar *il)
5501{
5502  hb_spc_reader_t	*pspcr;
5503  hb_job_t		*job	= NULL;
5504  hb_node_t		*nptr	= NULL;
5505  hb_link_t		*linkptr= NULL;
5506  dkChar		*vptr	= NULL;
5507  dkChar		*cptr	= NULL;
5508  size_t		 sl	=  0;
5509  int			 back	=  1;
5510  int			 idx	=  0;
5511  $? "+ hbhtml_spcl_handler \"%!ds\"", il
5512  pspcr = (hb_spc_reader_t *)obj;
5513  if (NULL != pspcr) {
5514    job = pspcr->job;
5515    nptr = pspcr->nodeptr;
5516    pspcr->lineno += 1UL;
5517    dk3app_set_source_line(job->app, pspcr->lineno);
5518    idx = hbcont_metadata_index(il);
5519    if (0 <= idx) {
5520      $? ". meta data found"
5521      dk3str_delnl(il);
5522      sl = hbcont_metadata_length((size_t)idx);
5523      if (0 < sl) {
5524        sl++;
5525	vptr = dk3str_start(&(il[sl]), NULL);
5526	$? ". value \"%!ds\"", vptr
5527      }
5528      switch (idx) {
5529        case 0: {			$? ". description"
5530	  if (NULL != vptr) {
5531	    $? ". value ok"
5532	    cptr = dk3str_dup_app(vptr, job->app);
5533	    if (NULL != cptr) {
5534	      $? ". allocation ok"
5535	      if (NULL != nptr->metadesc) {
5536	        /* Warning: Overwriting description text */
5537		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 97);
5538	        dk3mem_free(nptr->metadesc);
5539	      }
5540	      nptr->metadesc = cptr;
5541	    }
5542	  } else {
5543	    /* ERROR: Missing description text */
5544	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5545	    back = -1;
5546	  }
5547	} break;
5548	case 1: {			$? ". keywords"
5549	  if (NULL != vptr) {
5550	    $? ". value ok"
5551	    cptr = dk3str_dup_app(vptr, job->app);
5552	    if (NULL != cptr) {
5553	      $? ". allocation ok"
5554	      if (NULL != nptr->metakeyw) {
5555	        /* Warning: Overwriting keywords */
5556		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 98);
5557	        dk3mem_free(nptr->metakeyw);
5558	      }
5559	      nptr->metakeyw = cptr;
5560	    }
5561	  } else {
5562	    /* ERROR: Missing keywords text */
5563	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5564	    back = -1;
5565	  }
5566	} break;
5567	case 2: {			$? ". author"
5568	  if (NULL != vptr) {
5569	    cptr = dk3str_dup_app(vptr, job->app);
5570	    if (NULL != cptr) {
5571	      if (NULL != nptr->author) {
5572	        if (0UL != nptr->objno) {
5573		  dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 69);
5574		}
5575	        dk3mem_free(nptr->author);
5576	      }
5577	      nptr->author = cptr;
5578	    } else {
5579	      back = -1;
5580	    }
5581	  } else {
5582	    /* ERROR: Missing author name */
5583	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5584	    back = -1;
5585	  }
5586	} break;
5587	case 3: {			$? ". title"
5588	  if (NULL != vptr) {
5589	    cptr = dk3str_dup_app(vptr, job->app);
5590	    if (NULL != cptr) {
5591	      if (NULL != nptr->fulltitle) {
5592	        /* Warning: Overwriting title */
5593		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 70);
5594	        dk3mem_free(nptr->fulltitle);
5595	      }
5596	      nptr->fulltitle = cptr;
5597	    } else {
5598	      back = -1;
5599	    }
5600	  } else {
5601	    /* ERROR: Missing  title text */
5602	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5603	    back = -1;
5604	  }
5605	} break;
5606	case 4: {			$? ". short title"
5607	  if (NULL != vptr) {
5608	    cptr = dk3str_dup_app(vptr, job->app);
5609	    if (NULL != cptr) {
5610	      if (NULL != nptr->shorttitle) {
5611	        /* Warning: Overwriting short title */
5612		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 75);
5613	        dk3mem_free(nptr->shorttitle);
5614	      }
5615	      nptr->shorttitle = cptr;
5616	    } else {
5617	      back = -1;
5618	    }
5619	  } else {
5620	    /* ERROR: Missing short title text */
5621	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5622	    back = -1;
5623	  }
5624	} break;
5625	case 5: {			$? ". location"
5626	  if (NULL != vptr) {
5627	    cptr = dk3str_dup_app(vptr, job->app);
5628	    if (NULL != cptr) {
5629	      if (NULL != nptr->location) {
5630	        /* Warning: Overwriting location */
5631		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 99);
5632		dk3mem_free(nptr->location);
5633	      }
5634	      nptr->location = cptr;
5635	    } else {
5636	      back = -1;
5637	    }
5638	  } else {
5639	    /* ERROR: Missing location text */
5640	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5641	    back = -1;
5642	  }
5643	} break;
5644	case 6: {			$? ". page-toc"
5645	  if (NULL != vptr) {
5646	    if (0 != dk3str_is_bool(vptr)) {
5647	      if (0 != dk3str_is_on(vptr)) {
5648	        (job->currentnode)->options |= HB_NODE_OPT_PAGE_TOC;
5649	      } else {
5650	        (job->currentnode)->options &= (~(HB_NODE_OPT_PAGE_TOC));
5651	      }
5652	    } else {
5653	      dk3app_log_i3(job->app, DK3_LL_ERROR, 146, 144, vptr);
5654	      back = -1;
5655	    }
5656	  } else {
5657	    (job->currentnode)->options |= HB_NODE_OPT_PAGE_TOC;
5658	  }
5659	} break;
5660	case 7: {			$? ". replace-ampersand"
5661	  if (NULL != vptr) {
5662	    if (0 != dk3str_is_bool(vptr)) {
5663	      if (0 != dk3str_is_on(vptr)) {
5664	        (job->currentnode)->options |= HB_NODE_OPT_REPLACE_AMPERSAND;
5665	      } else {
5666	        (job->currentnode)->options &=
5667		(~(HB_NODE_OPT_REPLACE_AMPERSAND));
5668	      }
5669	    } else {
5670	      dk3app_log_i3(job->app, DK3_LL_ERROR, 146, 144, vptr);
5671	      back = -1;
5672	    }
5673	  } else {
5674	    (job->currentnode)->options |= HB_NODE_OPT_REPLACE_AMPERSAND;
5675	  }
5676	} break;
5677	case 8: {			$? ". write-position"
5678	  if (NULL != vptr) {
5679	    if (0 != dk3str_is_bool(vptr)) {
5680	      if (0 != dk3str_is_on(vptr)) {
5681	        (job->currentnode)->options |= HB_NODE_OPT_HEADER_CHAIN;
5682	      } else {
5683	        (job->currentnode)->options &= (~(HB_NODE_OPT_HEADER_CHAIN));
5684	      }
5685	    } else {
5686	      dk3app_log_i3(job->app, DK3_LL_ERROR, 146, 144, vptr);
5687	      back = -1;
5688	    }
5689	  } else {
5690	    (job->currentnode)->options |= HB_NODE_OPT_HEADER_CHAIN;
5691	  }
5692	} break;
5693	case 9: {			$? ". suffix"
5694	  if (NULL != vptr) {
5695	    cptr = dk3str_dup_app(vptr, job->app);
5696	    if (NULL != cptr) {
5697	      if (NULL != nptr->suffix) {
5698	        /* Warning: Overwriting suffix */
5699		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 77);
5700	        dk3mem_free(nptr->suffix);
5701	      }
5702	      nptr->suffix = cptr;
5703	    } else {
5704	      back = -1;
5705	    }
5706	  } else {
5707	    /* ERROR: Missing suffix */
5708	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5709	    back = -1;
5710	  }
5711	} break;
5712	case 10: {			$? ". shortcut-icon"
5713	  if (NULL != vptr) {
5714	    cptr = dk3str_dup_app(vptr, job->app);
5715	    if (NULL != cptr) {
5716	      dk3str_correct_filename(cptr);
5717	      hbtool_correct_backslash_to_slash(cptr);
5718	      if (NULL != nptr->favicon) {
5719	        /* Warning: Overwriting favicon */
5720		dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 82);
5721	        dk3mem_free(nptr->favicon);
5722	      }
5723	      nptr->favicon = cptr;
5724	      if (!(hbtool_add_file(job, cptr))) {
5725	        back = -1;
5726	      }
5727	      if(!hbtool_check_filename_encoding(job, cptr)) { back = -1; }
5728	    } else {
5729	      back = -1;
5730	    }
5731	  } else {
5732	    /* ERROR: Missing icon name */
5733	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5734	    back = -1;
5735	  }
5736	} break;
5737	case 11: {			$? ". js-file"
5738	  if (NULL != vptr) {
5739	    if (NULL == nptr->s_jsfiles) {
5740	      nptr->s_jsfiles = dk3sto_open_app(job->app);
5741	      if (NULL != nptr->s_jsfiles) {
5742	        dk3sto_set_comp(nptr->s_jsfiles, hbnode_link_compare, 2);
5743	      }
5744	    }
5745	    if (NULL == nptr->i_jsfiles) {
5746	      if (NULL != nptr->s_jsfiles) {
5747	        nptr->i_jsfiles = dk3sto_it_open(nptr->s_jsfiles);
5748	      }
5749	    }
5750	    if ((NULL != nptr->s_jsfiles) && (NULL != nptr->i_jsfiles)) {
5751	      linkptr = hbnode_link_new(vptr, nptr->nextjs, job->app);
5752	      if (NULL != linkptr) {
5753	        if(dk3sto_add(nptr->s_jsfiles, linkptr)) {
5754		  nptr->nextjs += 1UL;
5755		  if(!hbtool_add_url(job, vptr)) { back = -1; }
5756		} else {
5757		  hbnode_link_delete(linkptr);
5758		  back = -1;
5759		}
5760	      } else {
5761	        back = -1;
5762	      }
5763	    } else {
5764	      back = -1;
5765	    }
5766	  } else {
5767	    /* ERROR: Missing file name */
5768	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5769	    back = -1;
5770	  }
5771	} break;
5772	case 12: {			$? ". inherit-js"
5773	  if (NULL != vptr) {
5774	    if (0 != dk3str_is_bool(vptr)) {
5775	      if (0 != dk3str_is_on(vptr)) {
5776	        (job->currentnode)->options &=
5777		(~(HB_NODE_OPT_STOP_JS_INHERITANCE));
5778	      } else {
5779	        (job->currentnode)->options |= HB_NODE_OPT_STOP_JS_INHERITANCE;
5780	      }
5781	    } else {
5782	      dk3app_log_i3(job->app, DK3_LL_ERROR, 146, 144, vptr);
5783	      back = -1;
5784	    }
5785	  } else {
5786	    (job->currentnode)->options &= (~(HB_NODE_OPT_STOP_JS_INHERITANCE));
5787	  }
5788	} break;
5789	case 13: {			$? ". context-number"
5790	  long cn;
5791	  if (NULL != vptr) {
5792	    if (0 != dk3ma_l_from_string(&cn, vptr, NULL)) {
5793	      nptr->contnum = cn;
5794	    } else {
5795	      dk3app_log_i1(job->app, DK3_LL_ERROR, 141);
5796	      back = -1;
5797	    }
5798	  } else {
5799	    /* ERROR: Missing number */
5800	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 96);
5801	    back = -1;
5802	  }
5803	} break;
5804      }
5805    }
5806  }
5807  $? "- hbhtml_spcl_handler %d", back
5808  return back;
5809}
5810
5811
5812
5813/**	Read special comments from input file for node.
5814	@param	job	Job structure.
5815	@param	nodeptr	Node to read file for.
5816*/
5817static
5818void
5819hbhtml_read_special_comments(hb_job_t *job, hb_node_t *nodeptr)
5820{
5821  hb_spc_reader_t	 spcr;
5822  const dkChar		*oldsourcefile;
5823  unsigned long		 oldsourceline;
5824  int			 inenc;
5825  $? "+ hbhtml_read_special_comments"
5826  if (NULL != nodeptr->filename) {		$? ". have file name"
5827    spcr.job = job;
5828    spcr.nodeptr = nodeptr;
5829    spcr.lineno  = 0UL;
5830    inenc = dk3app_get_input_file_encoding(job->app);
5831    if(0 <= job->iecmd) { inenc = job->iecmd; }
5832    if(0 <= nodeptr->inenc) { inenc = nodeptr->inenc; }
5833    oldsourcefile = dk3app_get_source_file(job->app);
5834    oldsourceline = dk3app_get_source_line(job->app);
5835    dk3app_set_source_file(job->app, nodeptr->filename);
5836    dk3app_set_source_line(job->app, 0UL);
5837    (void)dk3stream_process_filename_lines_app(
5838      (void *)(&spcr), hbhtml_spcl_handler, nodeptr->filename,
5839      job->ilfile, job->bs,
5840      dk3app_get_encoding(job->app),
5841      inenc,
5842      job->app
5843    );
5844    dk3app_set_source_file(job->app, oldsourcefile);
5845    dk3app_set_source_line(job->app, oldsourceline);
5846  }
5847  $? "- hbhtml_read_special_comments"
5848}
5849
5850
5851
5852int
5853hbhtml_get_doctype(
5854  hb_job_t	*job,
5855  hb_node_t	*nodeptr
5856)
5857{
5858  int back = HTML_DOCTYPE_UNSPECIFIED;
5859  while ((HTML_DOCTYPE_UNSPECIFIED == back) && (NULL != nodeptr)) {
5860    back = nodeptr->htdt;
5861    nodeptr = nodeptr->parent;
5862  }
5863  if ((HTML_DOCTYPE_UNSPECIFIED == back) && (NULL != job)) {
5864    back = job->htdt;
5865  }
5866  if (HTML_DOCTYPE_UNSPECIFIED == back) {
5867    back = HTML_DOCTYPE_5;
5868  }
5869  return back;
5870}
5871
5872
5873
5874/**	Write one output file.
5875	@param	job	Job structure.
5876	@param	nodeptr	Current node to write.
5877	@param	fn	Output file name.
5878	@param	p	Previous node.
5879	@param	n	Next node.
5880*/
5881static
5882void
5883hbhtml_write_named_file(
5884  hb_job_t	*job,
5885  hb_node_t	*nodeptr,
5886  dkChar const	*fn,
5887  hb_node_t	*p,
5888  hb_node_t	*n
5889)
5890{
5891  hb_file_processor_t	 proc;		/* Template file processor */
5892  dkChar const		*templateName;	/* Template file name */
5893  dkChar const		*oldsourcefile;	/* Old source file name */
5894  dkChar const		*htlang;	/* HTML language */
5895  unsigned long		 oldsourceline;	/* Old source file line */
5896  int			 inenc;		/* Input encoding */
5897  int			 res;		/* Result from file processing */
5898  int			 htdt;		/* HTML doctype */
5899  $? "+ hbhtml_write_named_file"
5900  $? ". input  file \"%!ds\"", TR_STR(nodeptr->filename)
5901  $? ". output file \"%!ds\"", fn
5902  oldsourcefile = dk3app_get_source_file(job->app);
5903  oldsourceline = dk3app_get_source_line(job->app);
5904  dk3app_set_source_line(job->app, nodeptr->lineno);
5905  nodeptr->outFileName = fn;
5906  job->currentnode = nodeptr;
5907  job->of = NULL;
5908  templateName = hbconf_node_template(nodeptr);
5909  if(templateName) {
5910    dk3app_set_source_file(job->app, templateName);
5911    job->options &= (~(HB_JOB_OPT_CODE));
5912    job->of = dk3sf_fopen_app(fn, dkT("w"), job->app);
5913    if(job->of) {
5914      hbhtml_read_special_comments(job, nodeptr);
5915      htdt = hbhtml_get_doctype(job, nodeptr);
5916      if (HTML_DOCTYPE_NONE < htdt) {
5917        fputs(hbhtml_doctypes[htdt-1], job->of);
5918      }
5919      if (HTML_DOCTYPE_5 == htdt) {
5920        htlang = job->htmllang;
5921	if (NULL == htlang) {
5922	  htlang = dk3app_get_language(job->app);
5923	}
5924	if (NULL != htlang) {
5925	  fputs(hbhtml_c8_kw[161], job->of);
5926	  /*
5927	  	We assume the language is specified correctly
5928		using characters from the ASCII set only.
5929	  */
5930	  while (dkT('\0') != *htlang) { fputc((char)(*(htlang++)), job->of); }
5931	  fputs(hbhtml_c8_kw[162], job->of);
5932	}
5933	else {
5934	  fputs(hbhtml_c8_kw[11], job->of);
5935	}
5936      }
5937      else {
5938        fputs(hbhtml_c8_kw[11], job->of);
5939      }
5940      hbhtml_write_header(job, nodeptr, p, n, htdt);
5941      if (0 != (HB_JOB_OPT_CHM & (job->options))) {
5942        fputs(hbhtml_c8_kw[160], job->of);
5943      } else {
5944        fputs(hbhtml_c8_kw[13], job->of);
5945      }
5946      proc.job = job;
5947      proc.in_template = 1;
5948      proc.stm = 0;
5949      proc.p = p;
5950      proc.n = n;
5951      inenc = dk3app_get_input_file_encoding(job->app);
5952      if(0 <= job->iecmd) { inenc = job->iecmd; }
5953      res = dk3stream_process_filename_lines_app(
5954        (void *)(&proc), hbhtml_line_handler, templateName,
5955	job->iltemplate, job->bs,
5956	dk3app_get_encoding(job->app),
5957	inenc,
5958	job->app
5959      );
5960      fputs(hbhtml_c8_kw[14], job->of);
5961      fputs(hbhtml_c8_kw[12], job->of);
5962      if(1 != res) {
5963        job->exv = 1;			$? "! ERROR"
5964      }
5965      if(!(dk3sf_fclose_fn_app(job->of, fn, job->app))) {
5966        job->exv = 1;			$? "! ERROR"
5967      }
5968    } else {
5969      job->exv = 1;			$? "! ERROR"
5970    }
5971  } else {
5972    /* ERROR: No template! */
5973    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 19);
5974    job->exv = 1;			$? "! ERROR"
5975  }
5976  nodeptr->outFileName = NULL;
5977  dk3app_set_source_file(job->app, oldsourcefile);
5978  dk3app_set_source_line(job->app, oldsourceline);
5979  $? "- hbhtml_write_named_file"
5980}
5981
5982
5983
5984/**	Mark path to current node in parent nodes
5985	(set the curchild element).
5986	@param	nodeptr	Current node.
5987*/
5988static
5989void
5990hbhtml_mark_path(hb_node_t *nodeptr)
5991{
5992  hb_node_t	*cp;		/* Current node */
5993  $? "+ hbhtml_mark_path"
5994  cp = nodeptr;
5995  cp->curchild = NULL;
5996  while(cp) {
5997    if(cp->parent) { (cp->parent)->curchild = cp; }
5998    cp = cp->parent;
5999  } $? "- hbhtml_mark_path"
6000}
6001
6002
6003
6004void
6005hbhtml_write_file(hb_job_t *job, hb_node_t *nodeptr, hb_node_t *p, hb_node_t *n)
6006{
6007  dkChar	 bu[DK3_MAX_PATH];	/* File name buffer. */
6008  $? "+ hbhtml_write_file %lu \"%!ds\"", nodeptr->objno, TR_STR(nodeptr->title)
6009
6010  /*	Set things up for new file.
6011  */
6012  job->coli = 0;
6013  job->cols = HB_COLIST_NONE;
6014  job->options &= (~(HB_JOB_OPT_CODE));
6015
6016  /*	Process the file.
6017  */
6018  hbhtml_mark_path(nodeptr);
6019  if(hbhtml_create_output_filename(bu, DK3_SIZEOF(bu,dkChar), job, nodeptr)) {
6020    $? ". have output file name"
6021    hbhtml_write_named_file(job, nodeptr, bu, p, n);
6022  } else {
6023    job->exv = 1;		$? "! ERROR"
6024  }
6025
6026  $? "- hbhtml_write_file"
6027}
6028
6029
6030