msb@sq.com (Mark Brader) (03/20/89)
To recap, the problem was to put a \(sq character aligned with the right margin on the last line of a proof using troff. Several solutions have been posted to the net, but all of them are deficient in one way or another. To really do this properly requires some knowledge of troff's built-in read-only number registers. The ones mentioned in this message are: \n(.i current indent level from .in \n(.j current adjustement mode from .ad/.na \n(.k width of text in output line currently being filled \n(.l current line length from .ll The .i, .k, and .l registers are in basic units, so they usually get a u suffix when they're being read back. There are actually two somewhat separate problems. One is making the \(sq be right-justified, and the other is getting it on the same line as the last line of the proof -- if it fits. There are several ways to get a character right-justified if it's on a line by itself, i.e., if you have just had a .br or equivalent. These include: .ad r \(sq .br .ad b and: .ti \n(.lu-\w'\(sq'u \(sq and: \h'\n(.lu-\n(.iu-\w"\(sq"u'\(sq and: \h'|\n(.lu-\n(.iu-\w"\(sq"u'\(sq and: .ta \n(.lu-\n(.iuR \(sq and: .lt \n(.lu .tl '''\(sq' .lt Personally I would like the first one, being the only one that can get away without referring to any built-in number registers -- except that it's not really complete, because it should save the adjustment setting from \n(.j first of all, and restore that state instead of doing .ad b at the end. In the sixth version, by the way, note that the line length for .tl lines is maintained separately (by .lt) from that for text lines, so a bare .tl is inadequate. And in the \h-based versions, note that the \(sq follows immediately after the \h escape sequence, so that there isn't an interword space in there to mess up the width calculations. The next problem is combining the right-justified \(sq with the last line of the proof. You can't simply take the end of the last sentence and tack onto it one of the \h-based or tab-based constructs shown above. The reason that these don't work is that troff insists on knowing, before it formats the paragraph, how large a motion is to be produced, but the amount of motion you want depends on how the paragraph happens to be filled. In the absence of traps you could do a .sp -1 and follow it with any of the above constructs. But normally there is at least a bottom-of-page trap, so the .sp -1 method won't work if the end of the proof falls at the end of the page. If the bottom-of-page trap is set *relative to* the bottom of the page, and there is no other trap nearby, then you can use the method I described in my other article posted today. This time it will look like this: .pl +1 (or better, buy sqtroff and use .vpt off) .mk ZZ .sp |\n(ZZu .ti \n(.lu-\w'\(sq'u \(sq .pl -1 (or .vpt on) This last is perhaps the most elegant solution in some sense, but there is another approach which does not require putting the \(sq on its own output line at all. This is: \h'\n(.lu-\n(.iu-\n(.ku-\w" \(sq"u'\(sq This works because the \n(.k is our window into the way troff is filling the paragraph... we have taken one of the solutions given above, and modified it by reducing the amount of motion by the amount of text already going on the line. There is a problem with this method too. The value of \n(.k does not include any allowance for the interword space after the text on the line. That's why we have to allow for it, with a space inside the \w escape sequence inside the \h escape sequence. But I actually gave *2* spaces there. Why? Because I assumed that the last thing in the proof would also end a sentence, and so, there will be a double interword space there. You don't care about the amount of interword space since you're about to \h all the way to the right, but troff doesn't know that, and it gives you a space whether you want it or not. Or two spaces, if it's at end-of-sentence. Directly attaching the whole construction to the last text line, or using \c to achieve the same effect, won't work because \n(.k is updated only when a word is completed. An alternative, perhaps better, is to modify the above escape sequence by removing the double space: \h'\n(.lu-\n(.iu-\n(.ku-\w" \(sq"u'\(sq --and then, if the preceding thing does look like an end-of-sentence to troff, *concealing* this by appending a \& escape sequence or a space to the last text line. (\& is a non-printing zero-width pseudo-character.) There is *still* a problem with *all* of these methods: what if there is no room on the line for the \(sq? My final suggestion is the following: .nr XX \n(.l-\n(.i-\n(.k-\w' \(sq'u .ie \n(XX>=0 \h'\n(XXu'\ \(sq .el \{. .ne 2 .ti \n(.lu-\w'\(sq'u \(sq .\} (Again, if the preceding thing looks like an end-of-sentence, you have to conceal it from troff.) What this does is to compute the motion that will be needed to put a \(sq at the end of the line with a minimum of two spaces before it, and if it fits, it does it; but if it doesn't fit, the final line of the proof is forced out (and forced onto the next page if there was room for only one line), and the \(sq is put on the following line. Mark Brader "'You wanted it to WORK? That costs EXTRA!' SoftQuad Inc., Toronto is probably the second-place security hole utzoo!sq!msb, msb@sq.com after simple carelessness." -- John Woods 416-963-8337, or from US, 800-387-2777