os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/tools/man2help2.tcl
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 # man2help2.tcl --
     2 #
     3 # This file defines procedures that are used during the second pass of
     4 # the man page conversion.  It converts the man format input to rtf
     5 # form suitable for use by the Windows help compiler.
     6 #
     7 # Copyright (c) 1996 by Sun Microsystems, Inc.
     8 #
     9 # See the file "license.terms" for information on usage and redistribution
    10 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    11 # 
    12 # RCS: @(#) $Id: man2help2.tcl,v 1.12 2002/10/03 13:34:32 dkf Exp $
    13 # 
    14 
    15 # Global variables used by these scripts:
    16 #
    17 # state -	state variable that controls action of text proc.
    18 #				
    19 # topics -	array indexed by (package,section,topic) with value
    20 # 		of topic ID.
    21 #
    22 # keywords -	array indexed by keyword string with value of topic ID.
    23 #
    24 # curID - 	current topic ID, starts at 0 and is incremented for
    25 # 		each new topic file.
    26 #
    27 # curPkg -	current package name (e.g. Tcl).
    28 #
    29 # curSect -	current section title (e.g. "Tcl Built-In Commands").
    30 #
    31 
    32 # initGlobals --
    33 #
    34 # This procedure is invoked to set the initial values of all of the
    35 # global variables, before processing a man page.
    36 #
    37 # Arguments:
    38 # None.
    39 
    40 proc initGlobals {} {
    41     uplevel \#0 unset state
    42     global state chars
    43 
    44     set state(paragraphPending) 0
    45     set state(breakPending) 0
    46     set state(firstIndent) 0
    47     set state(leftIndent) 0
    48 
    49     set state(inTP) 0
    50     set state(paragraph) 0
    51     set state(textState) 0
    52     set state(curFont) ""
    53     set state(startCode) "{\\b "
    54     set state(startEmphasis) "{\\i "
    55     set state(endCode) "}"
    56     set state(endEmphasis) "}"
    57     set state(noFill) 0
    58     set state(charCnt) 0
    59     set state(offset) [getTwips 0.5i]
    60     set state(leftMargin) [getTwips 0.5i]
    61     set state(nestingLevel) 0
    62     set state(intl) 0
    63     set state(sb) 0
    64     setTabs 0.5i
    65 
    66 # set up international character table
    67 
    68     array set chars {
    69 	o^ F4
    70     }
    71 }
    72 
    73 
    74 # beginFont --
    75 #
    76 # Arranges for future text to use a special font, rather than
    77 # the default paragraph font.
    78 #
    79 # Arguments:
    80 # font -		Name of new font to use.
    81 
    82 proc beginFont {font} {
    83     global file state
    84 
    85     textSetup
    86     if {[string equal $state(curFont) $font]} {
    87 	return
    88     }
    89     endFont
    90     puts -nonewline $file $state(start$font)
    91     set state(curFont) $font
    92 }
    93 
    94 
    95 # endFont --
    96 #
    97 # Reverts to the default font for the paragraph type.
    98 #
    99 # Arguments:
   100 # None.
   101 
   102 proc endFont {} {
   103     global state file
   104 
   105     if {[string compare $state(curFont) ""]} {
   106 	puts -nonewline $file $state(end$state(curFont))
   107 	set state(curFont) ""
   108     }
   109 }
   110 
   111 
   112 # textSetup --
   113 #
   114 # This procedure is called the first time that text is output for a
   115 # paragraph.  It outputs the header information for the paragraph.
   116 #
   117 # Arguments:
   118 # None.
   119 
   120 proc textSetup {} {
   121     global file state
   122 
   123     if $state(breakPending) {
   124 	puts $file "\\line"
   125     }
   126     if $state(paragraphPending) {
   127 	puts $file [format "\\par\n\\pard\\fi%.0f\\li%.0f" \
   128 			$state(firstIndent) $state(leftIndent)]
   129 	foreach tab $state(tabs) {
   130 	    puts $file [format "\\tx%.0f" $tab]
   131 	}
   132 	set state(tabs) {}
   133 	if {$state(sb)} {
   134 	    puts $file "\\sb$state(sb)"
   135 	    set state(sb) 0
   136 	}
   137     }
   138     set state(breakPending) 0
   139     set state(paragraphPending) 0
   140 }
   141 
   142 
   143 # text --
   144 #
   145 # This procedure adds text to the current state(paragraph).  If this is
   146 # the first text in the state(paragraph) then header information for the
   147 # state(paragraph) is output before the text.
   148 #
   149 # Arguments:
   150 # string -		Text to output in the state(paragraph).
   151 
   152 proc text {string} {
   153     global file state chars
   154 
   155     textSetup
   156     set string [string map [list \
   157 	    "\\"	"\\\\" \
   158 	    "\{"	"\\\{" \
   159 	    "\}"	"\\\}" \
   160 	    "\t"	{\tab } \
   161 	    ''		"\\rdblquote " \
   162 	    ``		"\\ldblquote " \
   163 	    ] $string]
   164 
   165     # Check if this is the beginning of an international character string.
   166     # If so, look up the sequence in the chars table and substitute the
   167     # appropriate hex value.
   168 
   169     if {$state(intl)} {
   170 	if {[regexp {^'([^']*)'} $string dummy ch]} {
   171 	    if {[info exists chars($ch)]} {
   172 		regsub {^'[^']*'} $string "\\\\'$chars($ch)" string
   173 	    } else {
   174 		puts stderr "Unknown international character '$ch'"
   175 	    }
   176 	}
   177 	set state(intl) 0
   178     }
   179 
   180     switch $state(textState) {
   181 	REF { 
   182 	    if {$state(inTP) == 0} {
   183 		set string [insertRef $string]
   184 	    }
   185 	}
   186 	SEE { 
   187 	    global topics curPkg curSect
   188 	    foreach i [split $string] {
   189 		if {![regexp -nocase {^[a-z_0-9]+} [string trim $i] i ]} {
   190 		    continue
   191 		}
   192 		if {![catch {set ref $topics($curPkg,$curSect,$i)} ]} {
   193 		    regsub $i $string [link $i $ref] string
   194 		}
   195 	    }
   196 	}
   197 	KEY {
   198 	    return
   199 	}
   200     }
   201     puts -nonewline $file "$string"
   202 }
   203 
   204 
   205 
   206 # insertRef --
   207 #
   208 # This procedure looks for a string in the cross reference table and
   209 # generates a hot-link to the appropriate topic.  Tries to find the
   210 # nearest reference in the manual.
   211 #
   212 # Arguments:
   213 # string -		Text to output in the state(paragraph).
   214 
   215 proc insertRef {string} {
   216     global NAME_file curPkg curSect topics curID
   217     set path {}
   218     set string [string trim $string]
   219     set ref {}
   220     if {[info exists topics($curPkg,$curSect,$string)]} {
   221 	set ref $topics($curPkg,$curSect,$string)
   222     } else {
   223 	set sites [array names topics "$curPkg,*,$string"]
   224 	set count [llength $sites]
   225 	if {$count > 0} {
   226 	    set ref $topics([lindex $sites 0])
   227 	} else {
   228 	    set sites [array names topics "*,*,$string"]
   229 	    set count [llength $sites]
   230 	    if {$count > 0} {
   231 		set ref $topics([lindex $sites 0])
   232 	    }
   233 	}
   234     }
   235 
   236     if {($ref != {}) && ($ref != $curID)} {
   237 	set string [link $string $ref]
   238     }
   239     return $string
   240 }
   241 
   242 
   243 
   244 # macro --
   245 #
   246 # This procedure is invoked to process macro invocations that start
   247 # with "." (instead of ').
   248 #
   249 # Arguments:
   250 # name -		The name of the macro (without the ".").
   251 # args -		Any additional arguments to the macro.
   252 
   253 proc macro {name args} {
   254     global state file
   255     switch $name {
   256 	AP {
   257 	    if {[llength $args] != 3 && [llength $args] != 2} {
   258 		puts stderr "Bad .AP macro: .$name [join $args " "]"
   259 	    }
   260 	    newPara 3.75i -3.75i
   261 	    setTabs {1.25i 2.5i 3.75i}
   262 	    font B
   263 	    text [lindex $args 0]
   264 	    tab
   265 	    font I
   266 	    text [lindex $args 1]
   267 	    tab
   268 	    font R
   269 	    if {[llength $args] == 3} {
   270 		text "([lindex $args 2])"
   271 	    }
   272 	    tab
   273 	}
   274 	AS {
   275 	    # next page and previous page
   276 	}
   277 	br {
   278 	    lineBreak	
   279 	}
   280 	BS {}
   281 	BE {}
   282 	CE {
   283 	    puts -nonewline $::file "\\f0\\fs20 "
   284 	    set state(noFill) 0
   285 	    set state(breakPending) 0
   286 	    newPara ""
   287 	    set state(leftIndent) [expr {$state(leftIndent) - $state(offset)}]
   288 	    set state(sb) 80
   289 	}
   290 	CS {
   291 	    # code section
   292 	    set state(noFill) 1
   293 	    newPara ""
   294 	    set state(leftIndent) [expr {$state(leftIndent) + $state(offset)}]
   295 	    set state(sb) 80
   296 	    puts -nonewline $::file "\\f1\\fs18 "
   297 	}
   298 	DE {
   299 	    set state(noFill) 0
   300 	    decrNestingLevel
   301 	    newPara 0i
   302 	}
   303 	DS {
   304 	    set state(noFill) 1
   305 	    incrNestingLevel
   306 	    newPara 0i
   307 	}
   308 	fi {
   309 	    set state(noFill) 0
   310 	}
   311 	IP {
   312 	    IPmacro $args
   313 	}
   314 	LP {
   315 	    newPara 0i
   316 	    set state(sb) 80
   317 	}
   318 	ne {
   319 	}
   320 	nf {
   321 	    set state(noFill) 1
   322 	}
   323 	OP {
   324 	    if {[llength $args] != 3} {
   325 		puts stderr "Bad .OP macro: .$name [join $args " "]"
   326 	    }
   327 	    set state(nestingLevel) 0
   328 	    newPara 0i
   329 	    set state(sb) 120
   330 	    setTabs 4c
   331 	    text "Command-Line Name:"
   332 	    tab
   333 	    font B
   334 	    set x [lindex $args 0]
   335 	    regsub -all {\\-} $x - x
   336 	    text $x
   337 	    lineBreak
   338 	    font R
   339 	    text "Database Name:"
   340 	    tab
   341 	    font B
   342 	    text [lindex $args 1]
   343 	    lineBreak
   344 	    font R
   345 	    text "Database Class:"
   346 	    tab
   347 	    font B
   348 	    text [lindex $args 2]
   349 	    font R
   350 	    set state(inTP) 0
   351 	    newPara 0.5i
   352 	    set state(sb) 80
   353 	}
   354 	PP {
   355 	    newPara 0i
   356 	    set state(sb) 120
   357 	}
   358 	RE {
   359 	    decrNestingLevel
   360 	}
   361 	RS {
   362 	    incrNestingLevel
   363 	}
   364 	SE {
   365 	    font R
   366 	    set state(noFill) 0
   367 	    set state(nestingLevel) 0
   368 	    newPara 0i
   369 	    text "See the "
   370 	    font B
   371 	    set temp $state(textState)
   372 	    set state(textState) REF
   373 	    text options
   374 	    set state(textState) $temp
   375 	    font R
   376 	    text " manual entry for detailed descriptions of the above options."
   377 	}
   378 	SH {
   379 	    SHmacro $args
   380 	}
   381 	SO {
   382 	    SHmacro "STANDARD OPTIONS"
   383 	    set state(nestingLevel) 0
   384 	    newPara 0i
   385 	    setTabs {4c 8c 12c}
   386 	    font B
   387 	    set state(noFill) 1
   388 	}
   389 	so {
   390 	    if {$args != "man.macros"} {
   391 		puts stderr "Unknown macro: .$name [join $args " "]"
   392 	    }
   393 	}
   394 	sp {					;# needs work
   395 	    if {$args == ""} {
   396 		set count 1
   397 	    } else {
   398 		set count [lindex $args 0]
   399 	    }
   400 	    while {$count > 0} {
   401 		lineBreak
   402 		incr count -1
   403 	    }
   404 	}
   405 	ta {
   406 	    setTabs $args
   407 	}
   408 	TH {
   409 	    THmacro $args
   410 	}
   411 	TP {
   412 	    TPmacro $args
   413 	}
   414 	UL {					;# underline
   415 	    puts -nonewline $file "{\\ul "
   416 	    text [lindex $args 0]
   417 	    puts -nonewline $file "}"
   418 	    if {[llength $args] == 2} {
   419 		text [lindex $args 1]
   420 	    }
   421 	}
   422 	VE {}
   423 	VS {}
   424 	default {
   425 	    puts stderr "Unknown macro: .$name [join $args " "]"
   426 	}
   427     }
   428 }
   429 
   430 
   431 # link --
   432 #
   433 # This procedure returns the string for  a hot link to a different
   434 # context location.
   435 #
   436 # Arguments:
   437 # label -		String to display in hot-spot.
   438 # id -			Context string to jump to.
   439 
   440 proc link {label id} {
   441     return "{\\uldb $label}{\\v $id}"
   442 }
   443 
   444 
   445 # font --
   446 #
   447 # This procedure is invoked to handle font changes in the text
   448 # being output.
   449 #
   450 # Arguments:
   451 # type -		Type of font: R, I, B, or S.
   452 
   453 proc font {type} {
   454     global state
   455     switch $type {
   456 	P -
   457 	R {
   458 	    endFont
   459 	    if {$state(textState) == "REF"} {
   460 		set state(textState) INSERT
   461 	    }
   462 	}
   463 	C -
   464 	B {
   465 	    beginFont Code
   466 	    if {$state(textState) == "INSERT"} {
   467 		set state(textState) REF
   468 	    }
   469 	}
   470 	I {
   471 	    beginFont Emphasis
   472 	}
   473 	S {
   474 	}
   475 	default {
   476 	    puts stderr "Unknown font: $type"
   477 	}
   478     }
   479 }
   480 
   481 
   482 
   483 # formattedText --
   484 #
   485 # Insert a text string that may also have \fB-style font changes
   486 # and a few other backslash sequences in it.
   487 #
   488 # Arguments:
   489 # text -		Text to insert.
   490 
   491 proc formattedText {text} {
   492     global chars
   493 
   494     while {$text != ""} {
   495 	set index [string first \\ $text]
   496 	if {$index < 0} {
   497 	    text $text
   498 	    return
   499 	}
   500 	text [string range $text 0 [expr {$index-1}]]
   501 	set c [string index $text [expr {$index+1}]]
   502 	switch -- $c {
   503 	    f {
   504 		font [string index $text [expr {$index+2}]]
   505 		set text [string range $text [expr {$index+3}] end]
   506 	    }
   507 	    e {
   508 		text "\\"
   509 		set text [string range $text [expr {$index+2}] end]
   510 	    }
   511 	    - {
   512 		dash
   513 		set text [string range $text [expr {$index+2}] end]
   514 	    }
   515 	    | {
   516 		set text [string range $text [expr {$index+2}] end]
   517 	    }
   518 	    o {
   519 		text "\\'"
   520 		regexp {'([^']*)'(.*)} $text all ch text
   521 		text $chars($ch)
   522 	    }
   523 	    default {
   524 		puts stderr "Unknown sequence: \\$c"
   525 		set text [string range $text [expr {$index+2}] end]
   526 	    }
   527 	}
   528     }
   529 }
   530 
   531 
   532 # dash --
   533 #
   534 # This procedure is invoked to handle dash characters ("\-" in
   535 # troff).  It outputs a special dash character.
   536 #
   537 # Arguments:
   538 # None.
   539 
   540 proc dash {} {
   541     global state
   542     if {[string equal $state(textState) "NAME"]} {
   543     	set state(textState) 0
   544     }
   545     text "-"
   546 }
   547 
   548 
   549 # tab --
   550 #
   551 # This procedure is invoked to handle tabs in the troff input.
   552 # Right now it does nothing.
   553 #
   554 # Arguments:
   555 # None.
   556 
   557 proc tab {} {
   558     global file
   559 
   560     textSetup
   561     puts -nonewline $file "\\tab "
   562 }
   563 
   564 
   565 # setTabs --
   566 #
   567 # This procedure handles the ".ta" macro, which sets tab stops.
   568 #
   569 # Arguments:
   570 # tabList -	List of tab stops, each consisting of a number
   571 #			followed by "i" (inch) or "c" (cm).
   572 
   573 proc setTabs {tabList} {
   574     global file state
   575 
   576     set state(tabs) {}
   577     foreach arg $tabList {
   578 	set distance [expr {$state(leftMargin) \
   579 		+ ($state(offset) * $state(nestingLevel)) + [getTwips $arg]}]
   580 	lappend state(tabs) [expr {round($distance)}]
   581     }
   582 }
   583 
   584 
   585 
   586 # lineBreak --
   587 #
   588 # Generates a line break in the HTML output.
   589 #
   590 # Arguments:
   591 # None.
   592 
   593 proc lineBreak {} {
   594     global state
   595     textSetup
   596     set state(breakPending) 1
   597 }
   598 
   599 
   600 
   601 # newline --
   602 #
   603 # This procedure is invoked to handle newlines in the troff input.
   604 # It outputs either a space character or a newline character, depending
   605 # on fill mode.
   606 #
   607 # Arguments:
   608 # None.
   609 
   610 proc newline {} {
   611     global state
   612 
   613     if {$state(inTP)} {
   614     	set state(inTP) 0
   615 	lineBreak
   616     } elseif {$state(noFill)} {
   617 	lineBreak
   618     } else {
   619 	text " "
   620     }
   621 }
   622 
   623 
   624 # pageBreak --
   625 #
   626 # This procedure is invoked to generate a page break.
   627 #
   628 # Arguments:
   629 # None.
   630 
   631 proc pageBreak {} {
   632     global file curVer
   633     if {[string equal $curVer ""]} {
   634 	puts $file {\page}
   635     } else {
   636 	puts $file {\par}
   637 	puts $file {\pard\sb400\qc}
   638 	puts $file "Last change: $curVer\\page"
   639     }
   640 }
   641 
   642 
   643 # char --
   644 #
   645 # This procedure is called to handle a special character.
   646 #
   647 # Arguments:
   648 # name -		Special character named in troff \x or \(xx construct.
   649 
   650 proc char {name} {
   651     global file state
   652 
   653     switch -exact $name {
   654         \\o {
   655 	    set state(intl) 1
   656 	}
   657 	\\\  {
   658 	    textSetup
   659 	    puts -nonewline $file " "
   660 	}
   661 	\\0 {
   662 	    textSetup
   663 	    puts -nonewline $file " \\emspace "
   664 	}
   665 	\\\\ {
   666 	    textSetup
   667 	    puts -nonewline $file "\\\\"
   668 	}
   669 	\\(+- {
   670 	    textSetup
   671 	    puts -nonewline $file "\\'b1 "
   672 	}
   673 	\\% -
   674 	\\| {
   675 	}
   676 	\\(bu {
   677 	    textSetup
   678 	    puts -nonewline $file "·"
   679 	}
   680 	default {
   681 	    puts stderr "Unknown character: $name"
   682 	}
   683     }
   684 }
   685 
   686 
   687 # macro2 --
   688 #
   689 # This procedure handles macros that are invoked with a leading "'"
   690 # character instead of space.  Right now it just generates an
   691 # error diagnostic.
   692 #
   693 # Arguments:
   694 # name -		The name of the macro (without the ".").
   695 # args -		Any additional arguments to the macro.
   696 
   697 proc macro2 {name args} {
   698     puts stderr "Unknown macro: '$name [join $args " "]"
   699 }
   700 
   701 
   702 
   703 # SHmacro --
   704 #
   705 # Subsection head; handles the .SH macro.
   706 #
   707 # Arguments:
   708 # name -		Section name.
   709 
   710 proc SHmacro {argList} {
   711     global file state
   712 
   713     set args [join $argList " "]
   714     if {[llength $argList] < 1} {
   715 	puts stderr "Bad .SH macro: .SH $args"
   716     }
   717 
   718     # control what the text proc does with text
   719     
   720     switch $args {
   721 	NAME {set state(textState) NAME}
   722 	DESCRIPTION {set state(textState) INSERT}
   723 	INTRODUCTION {set state(textState) INSERT}
   724 	"WIDGET-SPECIFIC OPTIONS" {set state(textState) INSERT}
   725 	"SEE ALSO" {set state(textState) SEE}
   726 	KEYWORDS {set state(textState) KEY; return}
   727     }
   728 
   729     if {$state(breakPending) != -1} {
   730 	set state(breakPending) 1
   731     } else {
   732 	set state(breakPending) 0
   733     }
   734     set state(noFill) 0
   735     nextPara 0i
   736     font B
   737     text $args
   738     font R
   739     nextPara .5i
   740 }
   741 
   742 
   743 
   744 # IPmacro --
   745 #
   746 # This procedure is invoked to handle ".IP" macros, which may take any
   747 # of the following forms:
   748 #
   749 # .IP [1]			Translate to a "1Step" state(paragraph).
   750 # .IP [x] (x > 1)	Translate to a "Step" state(paragraph).
   751 # .IP				Translate to a "Bullet" state(paragraph).
   752 # .IP text count	Translate to a FirstBody state(paragraph) with special
   753 #					indent and tab stop based on "count", and tab after
   754 #					"text".
   755 #
   756 # Arguments:
   757 # argList -		List of arguments to the .IP macro.
   758 #
   759 # HTML limitations: 'count' in '.IP text count' is ignored.
   760 
   761 proc IPmacro {argList} {
   762     global file state
   763 
   764     set length [llength $argList]
   765     if {$length == 0} {
   766 	newPara 0.5i
   767 	return
   768     }
   769     if {$length == 1} {
   770 	newPara 0.5i -0.5i
   771 	set state(sb) 80
   772 	setTabs 0.5i
   773 	formattedText [lindex $argList 0]
   774 	tab
   775 	return
   776     }
   777     if {$length == 2} {
   778 	set count [lindex $argList 1]
   779 	set tab [expr $count * 0.1]i
   780 	newPara $tab -$tab
   781 	set state(sb) 80
   782 	setTabs $tab
   783 	formattedText [lindex $argList 0]
   784 	tab
   785 	return
   786     }
   787     puts stderr "Bad .IP macro: .IP [join $argList " "]"
   788 }
   789 
   790 
   791 # TPmacro --
   792 #
   793 # This procedure is invoked to handle ".TP" macros, which may take any
   794 # of the following forms:
   795 #
   796 # .TP x		Translate to an state(indent)ed state(paragraph) with the
   797 # 			specified state(indent) (in 100 twip units).
   798 # .TP		Translate to an state(indent)ed state(paragraph) with
   799 # 			default state(indent).
   800 #
   801 # Arguments:
   802 # argList -		List of arguments to the .IP macro.
   803 #
   804 # HTML limitations: 'x' in '.TP x' is ignored.
   805 
   806 proc TPmacro {argList} {
   807     global state
   808     set length [llength $argList]
   809     if {$length == 0} {
   810 	set val 0.5i
   811     } else {
   812 	set val [expr {([lindex $argList 0] * 100.0)/1440}]i
   813     }
   814     newPara $val -$val
   815     setTabs $val
   816     set state(inTP) 1
   817     set state(sb) 120
   818 }
   819 
   820 
   821 # THmacro --
   822 #
   823 # This procedure handles the .TH macro.  It generates the non-scrolling
   824 # header section for a given man page, and enters information into the
   825 # table of contents.  The .TH macro has the following form:
   826 #
   827 # .TH name section date footer header
   828 #
   829 # Arguments:
   830 # argList -		List of arguments to the .TH macro.
   831 
   832 proc THmacro {argList} {
   833     global file curPkg curSect curID id_keywords state curVer bitmap
   834 
   835     if {[llength $argList] != 5} {
   836 	set args [join $argList " "]
   837 	puts stderr "Bad .TH macro: .TH $args"
   838     }
   839     incr curID
   840     set name	[lindex $argList 0]		;# Tcl_UpVar
   841     set page	[lindex $argList 1]		;# 3
   842     set curVer	[lindex $argList 2]		;# 7.4
   843     set curPkg	[lindex $argList 3]		;# Tcl
   844     set curSect	[lindex $argList 4]		;# {Tcl Library Procedures}
   845     
   846     regsub -all {\\ } $curSect { } curSect	;# Clean up for [incr\ Tcl]
   847 
   848     puts $file "#{\\footnote $curID}"		;# Context string
   849     puts $file "\${\\footnote $name}"		;# Topic title
   850     set browse "${curSect}${name}"
   851     regsub -all {[ _-]} $browse {} browse
   852     puts $file "+{\\footnote $browse}"		;# Browse sequence
   853 
   854     # Suppress duplicates
   855     foreach i $id_keywords($curID) {
   856 	set keys($i) 1
   857     }
   858     foreach i [array names keys] {
   859 	set i [string trim $i]
   860 	if {[string length $i] > 0} {
   861 	    puts $file "K{\\footnote $i}"	;# Keyword strings
   862 	}
   863     }
   864     unset keys
   865     puts $file "\\pard\\tx3000\\sb100\\sa100\\fs24\\keepn"
   866     font B
   867     text $name
   868     tab
   869     text $curSect
   870     font R
   871     if {[info exists bitmap]} {
   872 	# a right justified bitmap
   873 	puts $file "\\\{bmrt $bitmap\\\}"
   874     }
   875     puts $file "\\fs20"
   876     set state(breakPending) -1
   877 }
   878 
   879 # nextPara --
   880 #
   881 # Set the indents for a new paragraph, and start a paragraph break
   882 #
   883 # Arguments:
   884 # leftIndent -		The new left margin for body lines.
   885 # firstIndent -		The offset from the left margin for the first line.
   886 
   887 proc nextPara {leftIndent {firstIndent 0i}} {
   888     global state
   889     set state(leftIndent) [getTwips $leftIndent]
   890     set state(firstIndent) [getTwips $firstIndent]
   891     set state(paragraphPending) 1
   892 }
   893 
   894 
   895 # newPara --
   896 #
   897 # This procedure sets the left and hanging state(indent)s for a line.
   898 # State(Indent)s are specified in units of inches or centimeters, and are
   899 # relative to the current nesting level and left margin.
   900 #
   901 # Arguments:
   902 # leftState(Indent) -		The new left margin for lines after the first.
   903 # firstState(Indent) -		The new left margin for the first line of a state(paragraph).
   904 
   905 proc newPara {leftIndent {firstIndent 0i}} {
   906     global state file
   907     if $state(paragraph) {
   908 	puts -nonewline $file "\\line\n"
   909     }
   910     if {$leftIndent != ""} {
   911 	set state(leftIndent) [expr {$state(leftMargin) \
   912 		+ ($state(offset) * $state(nestingLevel)) \
   913 		+ [getTwips $leftIndent]}]
   914     }
   915     set state(firstIndent) [getTwips $firstIndent]
   916     set state(paragraphPending) 1
   917 }
   918 
   919 
   920 # getTwips --
   921 #
   922 # This procedure converts a distance in inches or centimeters into
   923 # twips (1/1440 of an inch).
   924 #
   925 # Arguments:
   926 # arg -			A number followed by "i" or "c"
   927 
   928 proc getTwips {arg} {
   929     if {[scan $arg "%f%s" distance units] != 2} {
   930 	puts stderr "bad distance \"$arg\""
   931 	return 0
   932     }
   933     switch -- $units {
   934 	c	{
   935 	    set distance [expr {$distance * 567}]
   936 	}
   937 	i	{
   938 	    set distance [expr {$distance * 1440}]
   939 	}
   940 	default {
   941 	    puts stderr "bad units in distance \"$arg\""
   942 	    continue
   943 	}
   944     }
   945     return $distance
   946 }
   947 
   948 # incrNestingLevel --
   949 #
   950 # This procedure does the work of the .RS macro, which increments
   951 # the number of state(indent)ations that affect things like .PP.
   952 #
   953 # Arguments:
   954 # None.
   955 
   956 proc incrNestingLevel {} {
   957     global state
   958 
   959     incr state(nestingLevel)
   960     set oldp $state(paragraph)
   961     set state(paragraph) 0
   962     newPara 0i
   963     set state(paragraph) $oldp
   964 }
   965 
   966 # decrNestingLevel --
   967 #
   968 # This procedure does the work of the .RE macro, which decrements
   969 # the number of indentations that affect things like .PP.
   970 #
   971 # Arguments:
   972 # None.
   973 
   974 proc decrNestingLevel {} {
   975     global state
   976     
   977     if {$state(nestingLevel) == 0} {
   978 	puts stderr "Nesting level decremented below 0"
   979     } else {
   980 	incr state(nestingLevel) -1
   981     }
   982 }
   983