-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwrap.tcl
More file actions
1344 lines (1262 loc) · 40.6 KB
/
wrap.tcl
File metadata and controls
1344 lines (1262 loc) · 40.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!./tclsh.docsrc
#
# This script processes raw documentation source text into a near-final HTML
# form for display by browsers. The processing actions are described below.
#
# Invoke this command as follows:
set usage \
{ ./tclsh.docsrc wrap.tcl [mode pg_fname] $(DOC) $(SRC) $(DEST) <SOURCES>}
#
# where <SOURCES> is the remaining arguments naming input files,
# typically $(DOC)/pages/*.in expanded to a long list.
#
# When {mode pg_fname} is absent, a full scan and transformation of all
# named input files is done. This is appropriate for published builds.
#
# When mode eq "-update", only those input files which differ from
# their state after the previous build are scanned and transformed,
# and their names are left, one per line, in a file named by pg_fname.
# This is appropriate for doc development, prior to publishing results.
# The results have been tested for exact match to full build results.
# However, due to potential interactions between sources with embedded
# Tcl, this testing does not yet assure publication-ready results.
# Further, incremental builds do not leave behind the data used for
# the (traceability) matrix and evidence make targets.
#
# The $(DOC) and $(SRC) values are the names of directories containing
# the documentation source and program source. $(DEST) is the name of
# of the directory where generated HTML is written. <SOURCES> is a set
# of input files, source{1..N}.in, to be scanned and transformed, as
# necessary, into a set of output files, $(DEST)/source{1..N}.html .
#
# Changes made to the source files:
#
# * An appropriate header (containing the SQLite logo and standard
# menu bar) is prepended to the file.
#
# * Any <title>...</title> in the input is moved into the prepended
# header.
#
# * An appropriate footer is appended.
#
# * Scripts within <tcl>...</tcl> are evaluated. Output that
# is emitted from these scripts by "hd_puts" or "hd_resolve"
# procedures appears in place of the original script.
#
# * Hyperlinks within [...] are resolved.
#
# A two-pass algorithm is used. The first full pass collects the names
# of hyperlink targets, requirements text, and other global information.
# The second pass uses the data gathered on the first pass to generate
# the final output.
#
# Full builds are suitable for both creating HTML and extracting data
# related to requirements and evidence of their testing.
#
# Incremental builds are also suitable for creating (revised) HTML, but
# do not leave data behind to be used for requirements and evidence.
################################################################
# For purposes of debugging or statistics collection, certain environment
# variables can be set to influence creation of auxiliary output files
# and informative console output. These are:
# DOC_BUILD_STATS : If set to an integer, induces more console messages.
# DOC_BUILD_LINKLOG : If set, names DB where link defs and uses are logged.
# DOC_BUILD_TEMPDB : If set, names DB used for staging (default :memory:)
# DOC_BUILD_TOUCHES : If set, lists input files which will be treated as
# having been changed during incremental builds, even when unchanged.
# See what type of doc build this is, full or incremental.
set ::updating 0
if {$argc < 2} {
puts stderr $usage
exit 1
} elseif {[lindex $argv 0] eq "-update"} {
set ::updating 1
set ::pgfd [open [lindex $argv 1] w]
incr argc -2
set argv [lrange $argv 2 end]
}
if {$argc < 2} {
puts stderr $usage
exit 1
}
set ::DOC [lindex $argv 0]
set SRC [lindex $argv 1]
set DEST [lindex $argv 2]
set HOMEDIR [pwd] ;# Also remember our home directory.
# If a certain env-var is set, emit some diagnostics
if {[info exists ::env(DOC_BUILD_STATS)]} {
set ::doc_build_stats $::env(DOC_BUILD_STATS)
} else { set ::doc_build_stats 0 }
source [file dirname [info script]]/pages/fancyformat.tcl
source [file dirname [info script]]/document_header.tcl
source [file dirname [info script]]/pages/chronology.tcl
source [file dirname [info script]]/common_links.tcl
source [file dirname [info script]]/inc_update.tcl
# Some globals to help track where links originate, for incremental builds.
set ::currentInfile ""
set ::currentInfid 0
# Record errors for reporting after input files are processed.
# They are accumulated per input file for easier diagnosis.
array set ::accumulatedErrors {}
set ::errorCountTotal 0
# Record an error against ::currentInfile and return it
# (possibly for immediate output.)
proc record_error { errSay } {
incr ::errorCountTotal
if {![info exists ::accumulatedErrors($::currentInfile)]} {
set yaps [list]
} else {
set yaps $::accumulatedErrors($::currentInfile)
}
lappend yaps $errSay
set ::accumulatedErrors($::currentInfile) $yaps
return "ERROR: $errSay"
}
# Put accumulated errors to a stream. Done near exit time for visibility.
proc dump_errors { ostrm } {
foreach {infile} [lsort [array names ::accumulatedErrors]] {
set errCount [llength $::accumulatedErrors($infile)]
puts $ostrm "ERRORS from $infile ($errCount):"
foreach {errMsg} $::accumulatedErrors($infile) {
puts $ostrm " $errMsg"
}
}
}
# Open the SQLite database.
#
sqlite3 db [file join $::DOC docinfo.db]
db eval "ATTACH '[file join $::DOC history.db]' AS history"
# All builds start with certain tables empty and populate them.
# Incremental builds also empty the tables upon completion to
# avoid problems from their content likely being incomplete due
# to that build process not being designed to update the tables.
proc clear_db_partial {db} {
$db eval {
BEGIN;
DELETE FROM link;
DELETE FROM keyword;
DELETE FROM fragment;
DELETE FROM page;
DELETE FROM alttitle;
DROP TABLE IF EXISTS expage;
}
}
clear_db_partial db
# For full build, clear tables supporting incremental build.
if {!$::updating} { full_build_clear db }
# Setup transient tables for link/ref tracking in any case.
temp_links_setup db
# Load the syntax diagram linkage data
#
source $::DOC/art/syntax/syntax_linkage.tcl
# Gather hyperlink of 1 of 4 kinds. All link collection funnels through here.
proc gather_link {tag target kind {cfid 0}} {
if {$cfid == 0} { set cfid $::currentInfid }
if {$cfid != 0 && $::scan_pass < 3} {
# Only record links set during pages/*.in scan and process.
note_link db $tag $target $kind $cfid
}
switch $kind {
GLOBAL { set ::glink($tag) $target }
LOCAL { set ::llink($tag) $target }
BACK { lappend ::backlink($tag) $target }
PAGE { lappend ::pagelink($tag) $target }
}
if {$::doc_build_stats > 0} { incr ::gather_stats("$::scan_pass|$kind") }
}
# Collect a global link reference, recording what source it comes from.
# These must be known to be satisfied during an incremental doc build
# when they are from an uptodate file and defined in an outdated file.
#
proc glref_add {t} {
if {$::scan_pass > 2} return
note_link db $t 0 GLREF $::currentInfid
}
# Return text of an <img...> that will load a syntax diagram
#
proc hd_syntax_diagram {path} {
set name [file tail $path]
set src $path.svg
set in [open $::DOC/art/syntax/$name.pikchr rb]
set txt [read $in]
close $in
set svg [pikchr $txt]
set width [lindex $svg 1]
if {$width<=0} {
puts stderr [record_error "translating $::DOC/art/syntax/$name.pikchr"]
return "<pre>[lindex $svg 0]</pre>\n"
}
set style "style=\"max-width:${width}px\""
return "<div $style>[lindex $svg 0]</div>"
}
# Utility proc that removes excess leading and trailing whitespace.
#
proc hd_trim {txt} {
regsub -all {\n\s+} $txt "\n" txt
regsub -all {\s+\n} $txt "\n" txt
return [string trim $txt]
}
# Create and initialize the associative link arrays.
# For a complete docs build, these are empty until page processing.
# For an incremental docs rebuild, they are pre-populated from the
# key-value pairs gathered during previous builds for non-stale input.
array set ::glink {} ;# global links -- Used by name in *.in Tcl content
array set ::llink {} ;# local links
array set ::backlink {} ;# backlinks -- Used by name in *.in Tcl content
array set ::pagelink {} ;# pagelinks
# This is the first-pass implementation of procedure that renders
# hyperlinks. Do not even bother trying to do anything during the
# first pass. We have to collect keyword information before the
# hyperlinks are meaningful.
#
proc hd_resolve {text} {
hd_puts $text
}
# This is the second-pass implementation of the procedure that
# renders hyperlinks. Convert all hyperlinks in $text into
# appropriate <a href=""> markup.
#
# Links to keywords within the same main file are resolved using
# $::llink() if possible. All other links and links that could
# not be resolved using $::llink() are resolved using $::glink().
#
proc hd_resolve_2ndpass {text} {
while {[regexp {<pikchr>(.*?)</pikchr>} $text all srctxt]} {
set svg [pikchr $srctxt]
set w [lindex $svg 1]
if {$w>0} {
set rep "<div style=\"max-width:${w}px;\">[lindex $svg 0]</div>"
} else {
puts stderr [record_error \
"Pikchr translation error in $::infile\n[lindex $svg 0]\
Original input:\n$srctxt\n"]
break
}
regsub {<pikchr>.*?</pikchr>} $text $rep text
}
regsub -all {<(yy(non)?term)>} $text {<span class='\1'>} text
regsub -all {</yy(non)?term>} $text {</span>} text
regsub -all {\[(.*?)\]} $text \
"\175; hd_resolve_one \173\\1\175; hd_puts \173" text
eval "hd_puts \173$text\175"
}
proc hd_resolve_one {x} {
if {[string is integer $x] || [string length $x]==1} {
hd_puts \[$x\]
return
}
if {[string match {dateof:3.*} $x]} {
set x [string range $x 7 end]
if {[info exists ::dateofversion($x)]} {
hd_puts $::dateofversion($x)
} else {
puts stderr [record_error "*** unresolved date: '\[dateof:$x\]' ***"]
hd_puts "<font color='red'>0000-00-00</font>"
}
return
}
set x2 [split $x |]
set kw [string trim [lindex $x2 0]]
if {[llength $x2]==1} {
set content $kw
regsub {\([^)]*\)} $content {} kw
if {![regexp {^http} $kw]} {regsub {=.*} $kw {} kw}
} else {
set content [string trim [lindex $x2 1]]
}
if {![regexp {^https?:} $kw]} {
regsub -all {[^a-zA-Z0-9_.#/ -]} $kw {} kw
}
global hd
if {$hd(enable-main)} {
set fn $hd(fn-main)
if {[regexp {^https?:} $kw]} {
puts -nonewline $hd(main) \
"<a href=\"$kw\">$content</a>"
} elseif {[regexp {^[Tt]icket #(\d+)$} $kw all tktid]} {
set url http://www.sqlite.org/cvstrac/tktview?tn=$tktid
puts -nonewline $hd(main) \
"<a href=\"$url\">$content</a>"
} elseif {[info exists ::llink($fn:$kw)]} {
puts -nonewline $hd(main) \
"<a href=\"$hd(rootpath-main)$::llink($fn:$kw)\">$content</a>"
} elseif {[info exists ::glink($kw)]} {
puts -nonewline $hd(main) \
"<a href=\"$hd(rootpath-main)$::glink($kw)\">$content</a>"
glref_add $kw
} else {
puts stderr [record_error "unknown hyperlink target: $kw"]
puts -nonewline $hd(main) "<font color=\"red\">$content</font>"
}
if {$hd(fragment)!=""} {
backlink_add $kw $fn#$hd(fragment)
} else {
backlink_add $kw $fn
}
}
if {$hd(enable-aux)} {
if {[regexp {^https?:} $kw]} {
puts -nonewline $hd(aux) \
"<a href=\"$kw\">$content</a>"
} elseif {[regexp {^[Tt]icket #(\d+)$} $kw all tktid]} {
set url http://www.sqlite.org/cvstrac/tktview?tn=$tktid
puts -nonewline $hd(aux) \
"<a href=\"$url\">$content</a>"
} elseif {[info exists ::glink($kw)]} {
puts -nonewline $hd(aux) \
"<a href=\"$hd(rootpath-aux)$::glink($kw)\">$content</a>"
glref_add $kw
} else {
puts stderr [record_error "unknown hyperlink target: $kw"]
puts -nonewline $hd(aux) "<font color=\"red\">$content</font>"
}
if {$hd(aux-fragment)!=""} {
backlink_add $kw $hd(fn-aux)#$hd(aux-fragment)
} else {
backlink_add $kw $hd(fn-aux)
}
}
}
# Convert the keyword $kw into an appropriate relative URI
# This is a helper routine to hd_list_of_links
#
proc hd_keyword_to_uri {kw} {
global hd
if {[string match {*.html} $kw]} {return $kw}
if {$hd(enable-main)} {
set fn $hd(fn-main)
set res ""
if {[info exists ::llink($fn:$kw)]} {
set res "$hd(rootpath-main)$::llink($fn:$kw)"
} elseif {[info exists ::glink($kw)]} {
set res "$hd(rootpath-main)$::glink($kw)"
glref_add $kw
}
if {$res!=""} {
if {$hd(fragment)!=""} {
backlink_add $kw $fn#$hd(fragment)
} else {
backlink_add $kw $fn
}
}
return $res
}
if {$hd(enable-aux)} {
if {[info exists ::glink($kw)]} {
if {$hd(aux-fragment)!=""} {
backlink_add $kw $hd(fn-aux)#$hd(aux-fragment)
} else {
backlink_add $kw $hd(fn-aux)
}
glref_add $kw
return $hd(rootpath-aux)$::glink($kw)
}
}
return ""
}
# Output HTML/JS that displays the list $lx in multiple columns
# under the assumption that each column is $w pixels wide. The
# number of columns automatically adjusts to fill the available
# screen width.
#
# If $title is not an empty string, then it is used as a title for
# the list of links
#
# $lx is a list of triples. Each triple is {KEYWORD LABEL S}. The
# S determines a suffix added to each list element:
#
# 0: Add nothing (the default and common case)
# 1: Add the "(exp)" suffix
# 2: Strike through the text and do not hyperlink
# 3: Strike through the text and add ¹
# 4: Add ²
# 5: Add ³
#
proc hd_list_of_links {title w lx} {
global hd
set w [expr {$w/20}]em
hd_puts "<div class='columns' style='columns: ${w} auto;'>\n"
hd_puts "<ul style='padding-top:0;'>\n"
foreach x $lx {
foreach {kw lbl s} $x break
set suffix {}
set prefix {}
switch $s {
1 {set suffix "<small><i>(exp)</i></small>"}
2 {set suffix "</s>"; set prefix "<s>"; set kw ""}
3 {set suffix "¹</s>"; set prefix "<s>"}
4 {set suffix "²"}
5 {set suffix "³"}
}
if {$kw!=""} {
if {$hd(enable-main) && $hd(enable-aux)} {
set hd(enable-main) 0
set url [hd_keyword_to_uri $kw]
hd_puts "<li><a href='$url'>$prefix$lbl$suffix</a></li>\n"
set hd(enable-main) 1
set hd(enable-aux) 0
set url [hd_keyword_to_uri $kw]
hd_puts "<li><a href='$url'>$prefix$lbl$suffix</a></li>\n"
set hd(enable-aux) 1
} else {
set url [hd_keyword_to_uri $kw]
hd_puts "<li><a href='$url'>$prefix$lbl$suffix</a></li>\n"
}
} else {
hd_puts "<li>$prefix$lbl$suffix</li>\n"
}
}
hd_puts "</ul>\n"
hd_puts "</div>\n"
}
# Record the fact that all keywords given in the argument list should
# cause a jump to the current location in the current file.
#
# If only the main output file is open, then all references to the
# keyword jump to the main output file. If both main and aux are
# open then references from within the main file jump to the main file
# and all other references jump to the auxiliary file.
#
# This procedure is only active during the first pass when we are
# collecting hyperlink information. This procedure is redefined to
# be a no-op before the start of the second pass.
#
proc hd_keywords {args} {
global hd
if {$hd(fragment)==""} {
set lurl $hd(fn-main)
} else {
set lurl "#$hd(fragment)"
}
set fn $hd(fn-main)
if {[info exists hd(aux)]} {
set gurl $hd(fn-aux)
if {$hd(aux-fragment)!=""} {
append gurl "#$hd(aux-fragment)"
}
} else {
set gurl {}
if {$hd(fragment)!=""} {
set lurl $hd(fn-main)#$hd(fragment)
}
}
set override_flag 0
foreach a $args {
if {[regexp {^-+(.*)} $a all param] && ![regexp {^-D} $a]} {
switch $param {
"override" {
set override_flag 1
}
default {
puts stderr [record_error "unknown parameter: $a"]
}
}
continue
}
if {[regexp {^\*} $a]} {
set visible 0
set a [string range $a 1 end]
} else {
set visible 1
}
if {[regexp {^/--} $a]} {set a [string range $a 1 end]}
regsub -all {[^a-zA-Z0-9_.#/ -]} $a {} kw
if {[info exists ::glink($kw)]} {
if {[info exists hd(aux)] && $::glink($kw)==$hd(fn-aux)} {
db eval {DELETE FROM keyword WHERE kw=$kw}
} elseif {$override_flag==0} {
puts stderr [record_error \
"WARNING: duplicate keyword \"$kw\" - $::glink($kw) and $lurl"]
}
}
if {$gurl==""} {
gather_link $kw $lurl GLOBAL
db eval {INSERT OR IGNORE INTO keyword(kw,fragment,indexKw)
VALUES($a,$lurl,$visible)}
} else {
gather_link $kw $gurl GLOBAL
gather_link $fn:$kw $lurl LOCAL
db eval {INSERT OR IGNORE INTO keyword(kw,fragment,indexKw)
VALUES($a,$gurl,$visible)}
}
}
}
# Start a new fragment in the main file. Give the new fragment the
# indicated name. Any keywords defined after this point will refer
# to the fragment, not to the beginning of the file.
#
proc hd_fragment {name args} {
global hd
set hd(fragment) $name
puts $hd(main) "<a name=\"$name\"></a>"
if {$hd(enable-aux)} {
puts $hd(aux) "<a name=\"$name\"></a>"
set hd(aux-fragment) $name
}
eval hd_keywords $args
}
# Current output doc path sans tail.
proc out_dir {} {
global hd
if {$hd(enable-aux)} {
return $hd(rootpath-aux)
} else {
return $hd(rootpath-main)
}
}
# Pre-filtering and funneling for added backlinks.
proc backlink_add {t r} {
# Filter out self-references for obviousness.
if {$t eq $r} return
gather_link $t $r BACK
}
# Pre-filtering and funneling for added pagelinks.
proc pagelink_add {t r} {
# Do not add compendium refs to the compendia. Too obvious and useless.
if {[regexp {^doc_.*} $r]} return
if {$t eq $r} return
# Fixup (useless and duplicate-generating) relative-to-. page links.
set t [regsub {^\.\/} $t ""]
gather_link $t $r PAGE
}
# Write raw output to both the main file and the auxiliary.
# Only write after first pass to files that are enabled.
#
proc hd_puts {text} {
if {$::scan_pass < 2} return
global hd
if {$hd(enable-main)} {
set fn $hd(fn-main)
puts -nonewline $hd(main) $text
}
if {$hd(enable-aux)} {
set fn $hd(fn-aux)
puts -nonewline $hd(aux) $text
}
# Our post-pass pagelink processing based off the globals
# ::llink, ::glink, and ::backlink generated during hd_resolve
# processing doesn't catch links output directly with hd_puts.
# This code adds those links to our pagelink array, ::pagelink.
set refs [regexp -all -inline {href=\"(.*?)\"} $text]
foreach {href ref} $refs {
regsub {#.*} $ref {} ref2
regsub {http:\/\/www\.sqlite\.org\/} $ref2 {} ref3
if {[regexp {^checklists\/} $ref3]} continue
regsub {\.\.\/} $ref3 {} ref4
if {[regexp {^http} $ref4]} continue
if {$ref4==""} continue
if {[regexp {\.html$} $ref4]} {
pagelink_add $ref4 $fn
}
}
}
proc hd_putsnl {text} {
hd_puts $text\n
}
# Enable or disable the main output file.
#
proc hd_enable_main {boolean} {
global hd
set hd(enable-main) $boolean
}
# Enable or disable the auxiliary output file.
#
proc hd_enable_aux {boolean} {
global hd
set hd(enable-aux) $boolean
}
set hd(enable-aux) 0
# Open the main output file. $filename is relative to $::DEST.
#
proc hd_open_main {filename} {
global hd DEST
hd_close_main
set hd(fn-main) $filename
set hd(rootpath-main) [hd_rootpath $filename]
set hd(main) [open $DEST/$filename w]
set hd(enable-main) 1
set hd(enable-aux) 0
set hd(footer) {}
set hd(fragment) {}
pagelink_add $filename $filename
}
# If $filename is a path from $::DEST to a file, return a path
# from the directory containing $filename back to the directory $::DEST.
#
proc hd_rootpath {filename} {
set up {}
set n [llength [split $filename /]]
if {$n<=1} {
return {}
} else {
return [string repeat ../ [expr {$n-1}]]
}
}
# Close the main output file.
#
proc hd_close_main {} {
global hd
hd_close_aux
if {[info exists hd(main)]} {
puts $hd(main) $hd(mtime-msg)
puts $hd(main) $hd(footer)
close $hd(main)
unset hd(main)
set hd(rootpath-main) ""
}
}
# Open the auxiliary output file.
#
# Most documents have only a main file and no auxiliary. However, some
# large documents are broken up into smaller pieces where each smaller piece
# is an auxiliary file. There will typically be either many auxiliary files
# or no auxiliary files associated with each main file.
#
proc hd_open_aux {filename} {
global hd DEST
hd_close_aux
set hd(fn-aux) $filename
set hd(rootpath-aux) [hd_rootpath $filename]
set hd(aux) [open $DEST/$filename w]
set hd(enable-aux) 1
set hd(aux-fragment) {}
pagelink_add $filename $filename
# Add to list of outputs subject to caret removal.
if {$::updating && $::scan_pass == 2} {
puts $::pgfd $DEST/$filename
}
}
# Close the auxiliary output file
#
proc hd_close_aux {} {
global hd
if {[info exists hd(aux)]} {
puts $hd(aux) $hd(mtime-msg)
puts $hd(aux) $hd(footer)
close $hd(aux)
unset hd(aux)
unset hd(fn-aux)
set hd(enable-aux) 0
set hd(enable-main) 1
}
}
# Pages call this routine to suppress the bottom "Page Last Modified" message.
#
proc hd_omit_mtime {} {
global hd
set hd(mtime-msg) {}
}
# hd_putsin4 is like puts except that it removes the first 4 indentation
# characters from each line. It also does variable substitution in
# the namespace of its calling procedure.
#
proc putsin4 {fd text} {
regsub -all "\n " $text \n text
puts $fd [uplevel 1 [list subst -noback -nocom $text]]
}
# For each .in file, generate a globally unique object id sequence.
# This one is made from a PRNG seeded with a 128-bit hash of the .in filename.
# As each input's pass 2 starts, the PRNG is seeded deterministically so that
# generated files are created identically as long as their .in is unchanged.
# This allows incremental and full doc builds to produce identical outputs.
#
set ::id_prng ""
set ::id_part 0
proc seed_id {hv} {
set ::id_prng $hv
set ::id_part 0
}
# Return successive values from the sequence as the id.
#
proc hd_id {} {
set ixb $::id_part
set ixe [incr ::id_part 8]
set hvs [string range $::id_prng $ixb [expr $ixe - 1]]
if {$ixe >= 32} {
set ::id_prng [md5 "$::id_prng $::currentInfile"]
set ::id_part 0
}
return x$hvs
}
# Function to retrieve some SCM-related facts about a source file.
# Return list of normalizedFilename artifactId checkinTimestamp .
#
proc scm_facts_for {infn} {
set fname ""; set arid ""; set cits ""; set date ""; set ckin ""
try {
set finfo [exec fossil finfo -l -n 1 $infn]
foreach {hln vln} [lrange [split $finfo "\n"] 0 1] break
regexp {History for ([[:graph:]]+)$} $hln _ fname
regexp {^([-0-9]{10}) \[([0-9a-f]+)\]} $vln _ date ckin
regexp {artifact: +\[([0-9a-f]+)\]} $finfo _ arid
if {$ckin ne ""} {
foreach iln [split [exec fossil info $ckin] "\n"] {
if {[regexp {^hash:} $iln]} {
regexp {([-0-9]{10} [0-9:]{8}) UTC$} $iln _ cits
break
}
}
}
} on error erc {
if {[incr ::fossilFails] < 2} { puts stderr "fossil invoke fails: $erc" }
}
return [list $fname $arid $cits]
}
# A procedure to write the common header found on every HTML file on
# the SQLite website.
#
proc hd_header {title {srcfile {}}} {
global hd
set saved_enable $hd(enable-main)
if {$srcfile==""} {
set fd $hd(aux)
set path $hd(rootpath-aux)
} else {
set fd $hd(main)
set path $hd(rootpath-main)
}
puts $fd [document_header $title $path]
set hd(mtime-msg) {}
if {$srcfile!=""} {
set owd [pwd]
cd [file dir $srcfile]
foreach {fname arid cits} [scm_facts_for [file tail $srcfile]] break
cd $owd
if {$arid ne "" && $cits ne ""} {
set hd(mtime-msg) [hd_trim \
""]
if {[file exists DRAFT]} {
set hd(footer) [hd_trim {
<p align="center"><font size="6" color="red">*** DRAFT ***</font></p>
}]
} else {
set hd(footer) {}
}
}
} else {
set hd(enable-main) $saved_enable
}
}
# Insert a bubble syntax diagram into the output.
#
proc BubbleDiagram {name {anonymous_flag 0}} {
global hd
#if {!$anonymous_flag} {
# hd_resolve "<h4>\[$name:\]</h4>"
#}
hd_resolve "<p><b>\[$name:\]</b></p>"
set alt "alt=\"syntax diagram $name\""
if {$hd(enable-main)} {
puts $hd(main) "<div class='imgcontainer'>\n\
[hd_syntax_diagram $hd(rootpath-main)images/syntax/$name]\n\
</div>"
}
if {$hd(enable-aux)} {
puts $hd(aux) "<div class='imgcontainer'>\n\
[hd_syntax_diagram $hd(rootpath-aux)images/syntax/$name]\n\
</div>"
}
}
proc HiddenBubbleDiagram {name} {
global hd
set alt "alt=\"syntax diagram $name\""
hd_resolve "<p><b>\[$name:\]</b> "
if {$hd(enable-main)} {
set a [hd_id]
set b [hd_id]
puts $hd(main) \
"<button id='$a' onclick='hideorshow(\"$a\",\"$b\")'>show</button>\
</p>\n\
<div id='$b' style='display:none;' class='imgcontainer'>\n\
[hd_syntax_diagram $hd(rootpath-main)images/syntax/$name]\n\
</div>"
}
if {$hd(enable-aux)} {
set a [hd_id]
set b [hd_id]
puts $hd(aux) \
"<button id='$a' onclick='hideorshow(\"$a\",\"$b\")'>show</button>\
</p>\n\
<div id='$b' style='display:none;' class='imgcontainer'>\n\
[hd_syntax_diagram $hd(rootpath-aux)images/syntax/$name]\n\
</div>"
}
}
proc RecursiveBubbleDiagram_helper {class name openlist exclude} {
global hd syntax_linkage DEST
set alt "alt=\"syntax diagram $name\""
hd_resolve "<p><b>\[$name:\]</b>\n"
set a [hd_id]
set b [hd_id]
set openflag 0
set open2 {}
foreach x $openlist {
if {$x==$name} {
set openflag 1
} else {
lappend open2 $x
}
}
if {$openflag} {
puts $hd($class) \
"<button id='$a' onclick='hideorshow(\"$a\",\"$b\")'>hide</button></p>\n\
<div id='$b' class='imgcontainer'>\n\
[hd_syntax_diagram $hd(rootpath-$class)images/syntax/$name]"
} else {
puts $hd($class) \
"<button id='$a' onclick='hideorshow(\"$a\",\"$b\")'>show</button></p>\n\
<div id='$b' style='display:none;' class='imgcontainer'>\n\
[hd_syntax_diagram $hd(rootpath-$class)images/syntax/$name]"
}
if {[info exists syntax_linkage($name)]} {
foreach {cx px} $syntax_linkage($name) break
foreach c $cx {
if {[lsearch $exclude $c]>=0} continue
RecursiveBubbleDiagram_helper $class $c $open2 [concat $exclude $cx]
}
}
puts $hd($class) "</div>"
}
proc RecursiveBubbleDiagram {args} {
global hd
set show 1
set a2 {}
foreach name $args {
if {$name=="--initially-hidden"} {
set show 0
} else {
lappend a2 $name
}
}
if {$show} {
set showlist $a2
} else {
set showlist {}
}
set name [lindex $a2 0]
if {$hd(enable-main)} {
RecursiveBubbleDiagram_helper main $name $showlist $name
}
if {$hd(enable-aux)} {
RecursiveBubbleDiagram_helper aux $name $showlist $name
}
}
# Insert a See Also line for related bubble
# Record a requirement. This procedure is active only for the first
# pass. This procedure becomes a no-op for the second pass. During
# the second pass, requirements listing report generators can use the
# data accumulated during the first pass to construct their reports.
#
# If the "verbatim" argument is true, then the requirement text is
# rendered as is. In other words, the requirement text is assumed to
# be valid HTML with all hyperlinks already resolved. If the "verbatim"
# argument is false (the default) then the requirement text is rendered
# using hd_render which will find an expand hyperlinks within the text.
#
# The "comment" argument is non-binding commentary and explanation that
# accompanies the requirement.
#
proc hd_requirement {id text derivedfrom comment} {
global ALLREQ ALLREQ_DERIVEDFROM ALLREQ_COM
if {[info exists ALLREQ($id)]} {
puts stderr [record_error "duplicate requirement label: $id"]
}
set ALLREQ_DERIVEDFROM($id) $derivedfrom
set ALLREQ($id) $text
set ALLREQ_COM($id) $comment
}
# Read a block of requirements from an ASCII text file. Store the
# information obtained in a global variable named by the second parameter.
#
proc hd_read_requirement_file {filename varname} {
global hd_req_rdr
hd_reset_requirement_reader
set in [open $filename]
while {![eof $in]} {
set line [gets $in]
if {[regexp {^(HLR|UNDEF|SYSREQ) +([LHSU]\d+) *(.*)} $line _ type rn df]} {
hd_add_one_requirement $varname
set hd_req_rdr(rn) $rn
set hd_req_rdr(derived) $df
} elseif {[string trim $line]==""} {
if {$hd_req_rdr(body)==""} {
set hd_req_rdr(body) $hd_req_rdr(comment)
set hd_req_rdr(comment) {}
} else {
append hd_req_rdr(comment) \n
}
} else {
append hd_req_rdr(comment) $line\n
}
}
hd_add_one_requirement $varname
close $in
}
proc hd_reset_requirement_reader {} {
global hd_req_rdr
set hd_req_rdr(rn) {}
set hd_req_rdr(comment) {}
set hd_req_rdr(body) {}
set hd_req_rdr(derived) {}
}
proc hd_add_one_requirement {varname} {
global hd_req_rdr
set rn $hd_req_rdr(rn)
if {$rn!=""} {
if {$hd_req_rdr(body)==""} {
set hd_req_rdr(body) $hd_req_rdr(comment)
set hd_req_rdr(comment) {}
}
set b [string trim $hd_req_rdr(body)]
set c [string trim $hd_req_rdr(comment)]
set ::${varname}($rn) [list $hd_req_rdr(derived) $b $c]
lappend ::${varname}(*) $rn
}
hd_reset_requirement_reader
}
# Determine which of specified infiles to actually scan and transform,
# and populate the link arrays accordingly. (No stale links to remain.)
set infiles [lrange $argv 3 end]
if {$::updating} {
foreach {odfiles udfiles} [outdated_uptodate db $infiles] break
set nod [llength $odfiles]
set nud [llength $udfiles]
# Pre-populate ::glink, ::llink, ::backlink, ::pagelink arrays
# from the DB while omitting elements gleaned from the outdated inputs.
set lcount 0
foreach {g l b p ref_count} [fetch_links db $udfiles] {
array set ::glink $g
incr lcount [array size ::glink]
array set ::llink $l
incr lcount [array size ::llink]
array set ::backlink $b
incr lcount [array size ::backlink]
array set ::pagelink $p
incr lcount [array size ::pagelink]
}
puts "Reusing $lcount links from $nud files to process $nod files."
puts "Possibly resolving $ref_count link references from $nud files."
set infiles $odfiles
}
array set ::dateofversion {}
# Get date/version info used by chronology.in, which may remain unprocessed.
foreach {udvv} [chronology_info] {
foreach {uuid date vers vnum} $udvv break
set ::dateofversion($vers) $date
}
# Normalize the appendee associative arrays value lists and rebuild them to
# make generated crossref pages the same between full and incremental builds.
#
proc normalize_apparray {aaName} {
set blv [lsort -nocase -stride 2 [array get $aaName]]