├── .gitignore ├── tmac ├── pca-bib.tmac ├── pca-t2p-ix.tmac ├── pca-lsm.tmac ├── pca-verb.tmac ├── pca-t2p-eval-lua.tmac ├── pca-dc.tmac ├── pca.tmac ├── pca-t2p-toc.tmac ├── pca-eval-lua.tmac ├── pca-eval-js.tmac ├── pca-tag.tmac ├── pca-t2p-toc-lua.tmac ├── pca-t2p-man.tmac ├── pca-t2p-man-lua.tmac ├── pca-img.tmac ├── pca-eval-lisp.tmac ├── pca-eval.tmac ├── pca-t2p-ix-lua.tmac ├── pca-toc.tmac ├── pca-ix.tmac └── pca-t2p-info-lua.tmac ├── bin ├── mpca └── adoc2ms └── README.adoc /.gitignore: -------------------------------------------------------------------------------- 1 | #last modified 2020-11-07 2 | *.1 3 | *.log 4 | *.lua 5 | *.pdf 6 | *.png 7 | *.ps 8 | *.sav 9 | *.tmac.* 10 | *.txt.so 11 | *~ 12 | .troff* 13 | ?.ms 14 | [!io]*-Z-* 15 | [!io]*.html 16 | docs/*[!ls] 17 | -------------------------------------------------------------------------------- /tmac/pca-bib.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-17 2 | .\" created 2020-12-17 3 | . 4 | .if d BIB .nx 5 | . 6 | .de BIB 7 | .ie !\\n[BIBnum] .nr BIBnum 1 8 | .el .nr BIBnum \\n[BIBnum]+1 9 | .sp .5v 10 | .TAG \\$1 \\n[BIBnum] 11 | .LP 12 | [\\n[BIBnum]] 13 | .. 14 | -------------------------------------------------------------------------------- /bin/mpca: -------------------------------------------------------------------------------- 1 | # last modified 2021-11-08 2 | # created 2020-11-08 3 | 4 | f=${@:$(($#)):1} 5 | 6 | all_but_final_arg=${@:1:$(($#-1))} 7 | 8 | f_without_dir=${f##*/} 9 | 10 | f_basename=${f_without_dir%.*} 11 | 12 | soelim $f | 13 | preconv | 14 | groff -U -mpca $all_but_final_arg > $f_basename.ps 15 | -------------------------------------------------------------------------------- /tmac/pca-t2p-ix.tmac: -------------------------------------------------------------------------------- 1 | .\" last change 2020-12-12 2 | .\" Dorai Sitaram 3 | . 4 | .if !\n[.troff2page] .nx 5 | . 6 | .de IX 7 | . pca:set-up-index 8 | . nr pca:ix-location +1 9 | . TAG __troff2page_index_\\n[pca:ix-location] 10 | . write pca:idx-stream \\\\indexentry{\\$*|pca:ix-function}{\\n[pca:ix-location]} 11 | .. 12 | . 13 | .mso pca-t2p-ix-\*[pca-eval-lang].tmac 14 | -------------------------------------------------------------------------------- /tmac/pca-lsm.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-11-30 2 | . 3 | .if d pca:leading-spaces-macro .nx 4 | . 5 | .am par@reset 6 | .nr pca:obeylines 0 7 | .. 8 | . 9 | .de pca:leading-spaces-macro 10 | .ie \\n[pca:obeylines] .br 11 | .el \{\ 12 | .ns 13 | .LP 14 | .if d lsmhook .lsmhook 15 | .nr pca:obeylines 1 16 | .\} 17 | \h'\\n[lsn]n' 18 | .. 19 | . 20 | .lsm pca:leading-spaces-macro 21 | -------------------------------------------------------------------------------- /tmac/pca-verb.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2021-11-07 2 | .\" Dorai Sitaram 3 | . 4 | .if d EX .nx 5 | . 6 | .de EX 7 | . nr pca:EX-keep-within-page 0 8 | . if !'\\$1'' .nr pca:EX-keep-within-page 1 9 | . ie \\n[pca:EX-keep-within-page] .DS L verbatim 10 | . el .LD verbatim 11 | . ft C 12 | . if \\n[pca:EX-keep-within-page] .eo 13 | .. 14 | . 15 | .de EE 16 | . ec 17 | . DE 18 | .. 19 | -------------------------------------------------------------------------------- /tmac/pca-t2p-eval-lua.tmac: -------------------------------------------------------------------------------- 1 | .\" last change 2020-12-13 2 | . 3 | .if !\n[.troff2page] .nx 4 | . 5 | .ig ## 6 | defrequest('pca:eval-process-snippets', function() 7 | read_troff_line() 8 | local f = String_table['pca:eval-file']() 9 | if probe_file(f) then dofile(f) end 10 | end) 11 | 12 | defrequest('pca:eval-load-snippet-if-poss', function() 13 | read_troff_line() 14 | local f = String_table['pca:eval-snippet-file']() 15 | if probe_file(f) then troff2page_file(f) 16 | else flag_missing_piece 'eval' 17 | end 18 | end) 19 | .## 20 | -------------------------------------------------------------------------------- /tmac/pca-dc.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-12 2 | . 3 | .\" from www.tmac 4 | . 5 | .if \n[.troff2page] .nx 6 | . 7 | .de DC 8 | . in 0 9 | . if !d pca:dc-color .ds pca:dc-color black\" 10 | . if !'\\$3'' .ds pca:dc-color \\$3\" 11 | . ie n \ 12 | . nop \\$1\c 13 | . el \{\ 14 | . nr dummy \w'\\$1'u 15 | . nr pca:dc-height ((\\n[.v] + \\n[rst]) * \\n[.ps] / \\n[rst]) 16 | . char \[pca:dc-char] \m[\\*[pca:dc-color]]\s'\\n[pca:dc-height]u'\\$1 17 | . nop \v'\\n[.v]u'\\[pca:dc-char]\v'-\\n[.v]u'\c 18 | ' ti \w'\\[pca:dc-char]'u 19 | . \} 20 | . nop \\$2 21 | .. 22 | -------------------------------------------------------------------------------- /tmac/pca.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2021-11-06 2 | .\" Dorai Sitaram 3 | . 4 | .mso ms.tmac 5 | . 6 | .if d pca:section-postlude .nx 7 | . 8 | .mso pca-toc.tmac 9 | . 10 | .mso pca-tag.tmac 11 | . 12 | .mso .groffrc.tmac 13 | . 14 | .de IX 15 | . mso pca-ix.tmac 16 | . IX \\$* 17 | .. 18 | . 19 | .de BIB 20 | . rm BIB 21 | . mso pca-bib.tmac 22 | . BIB \\$* 23 | .. 24 | . 25 | .de EX 26 | . rm EX 27 | . mso pca-verb.tmac 28 | . EX \\$* 29 | .. 30 | . 31 | .de eval 32 | . mso pca-eval.tmac 33 | . eval 34 | .. 35 | . 36 | .if !d IMG \{\ 37 | . de IMG 38 | . rm IMG 39 | . mso pca-img.tmac 40 | . IMG \\$* 41 | . . 42 | .\} 43 | . 44 | .if !d DC \{\ 45 | . de DC 46 | . rm DC 47 | . mso pca-dc.tmac 48 | . DC \\$* 49 | . . 50 | .\} 51 | -------------------------------------------------------------------------------- /tmac/pca-t2p-toc.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2021-11-07 2 | .\" Dorai Sitaram 3 | . 4 | .if !\n[.troff2page] .nx 5 | . 6 | .am ToC 7 | . TAG __troff2page_toc_end 8 | .. 9 | . 10 | .de pca:write-toc-line 11 | . write pca:toc-stream .TOCLINE \\n[pca:toc-header-level] \\n[pca:toc-count] \\*[SN-STYLE] 12 | .. 13 | . 14 | .de TOCLINE 15 | . nr pca:this-tocentry-level \\$1 16 | . nr pca:this-tocentry-level -1 17 | . nr pca:toc-snippet-count \\$2 18 | . shift 2 19 | . ds pca:sec-num "\\$@ 20 | . nr pca:this-indent \\n[pca:DI]u 21 | . nr pca:this-indent (\\n[pca:this-indent]*\\n[pca:this-tocentry-level]) 22 | . nop \h'\\n[pca:this-indent]u' 23 | . nop \\*[pca:toc-create-link \\n[pca:toc-snippet-count]] 24 | . br 25 | .. 26 | . 27 | .mso pca-t2p-toc-\*[pca-eval-lang].tmac 28 | -------------------------------------------------------------------------------- /tmac/pca-eval-lua.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-12 2 | . 3 | .ds pca:eval-file \\*[AUXF].lua 4 | . 5 | .de pca:eval-process-snippets 6 | . sy if test -f \\*[pca:eval-file]; then lua \\*[pca:eval-file]; fi 7 | .. 8 | . 9 | .de pca:eval-start-snippets 10 | . write pca:eval-stream local troff 11 | . 12 | . write pca:eval-stream function pca_eval_lua_preamble(f) 13 | . write pca:eval-stream troff = io.open(f, 'w') 14 | . write pca:eval-stream end 15 | . 16 | . write pca:eval-stream function pca_eval_lua_postamble() 17 | . write pca:eval-stream troff:write('\\\\\\\\c\\\\n') 18 | . write pca:eval-stream troff:write('.ds pca:eval-status done\\\\n') 19 | . write pca:eval-stream troff:close() 20 | . write pca:eval-stream end 21 | .. 22 | . 23 | .de pca:eval-write-snippet 24 | . write pca:eval-stream pca_eval_lua_preamble('\\*[AUXF]-eval-\\n[pca:eval-count].tmp'); 25 | . eo 26 | . writem pca:eval-stream pca:eval-text 27 | . ec 28 | . write pca:eval-stream pca_eval_lua_postamble() 29 | .. 30 | . 31 | .if \n[.troff2page] .mso pca-t2p-eval-lua.tmac 32 | -------------------------------------------------------------------------------- /tmac/pca-eval-js.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-12 2 | . 3 | .ds pca:eval-file \\*[AUXF].js 4 | . 5 | .de pca:eval-process-snippets 6 | . sy if test -f \\*[pca:eval-file]; then nodejs \\*[pca:eval-file]; fi 7 | .. 8 | . 9 | .de pca:eval-start-snippets 10 | . write pca:eval-stream var troff; 11 | . write pca:eval-stream var pca_eval_js_fs = require('fs'); 12 | . 13 | . write pca:eval-stream function pca_eval_js_preamble(f) { 14 | . write pca:eval-stream troff = pca_eval_js_fs.createWriteStream(f); 15 | . write pca:eval-stream } 16 | . 17 | . write pca:eval-stream function pca_eval_js_postamble() { 18 | . write pca:eval-stream troff.write('\\\\\\\\c\\\\n'); 19 | . write pca:eval-stream troff.write('.ds pca:eval-status done\\\\n'); 20 | . write pca:eval-stream troff.end(); 21 | . write pca:eval-stream } 22 | .. 23 | . 24 | .de pca:eval-write-snippet 25 | . write pca:eval-stream pca_eval_js_preamble('\\*[AUXF]-eval-\\n[pca:eval-count].tmp'); 26 | . writem pca:eval-stream pca:eval-text 27 | . write pca:eval-stream pca_eval_js_postamble(); 28 | .. 29 | -------------------------------------------------------------------------------- /tmac/pca-tag.tmac: -------------------------------------------------------------------------------- 1 | .\" last change 2020-12-12 2 | .\" Dorai Sitaram 3 | . 4 | .\" Generalization of .TAG in groff's www.tmac, in order 5 | .\" to allow forward references 6 | . 7 | .\" troff2page already has it 8 | .if \n[.troff2page] .nx 9 | . 10 | .\" don't load more than once 11 | .if d pca:tag-set-up .nx 12 | . 13 | .if '\*[AUXF]'' .ds AUXF .trofftemp 14 | . 15 | .nr pca:tag-already-set-up 0 16 | . 17 | .ie \n[.U] \{\ 18 | . sy test -f \*[AUXF].aux 19 | . if !\n[systat] .so \*[AUXF].aux 20 | . \} 21 | .el .so \*[AUXF].aux 22 | . 23 | .de pca:tag-set-up 24 | . if \\n[pca:tag-already-set-up] .return 25 | . nr pca:tag-already-set-up 1 26 | . if !\\n[.U] .return 27 | . open pca:tag-stream \\*[AUXF].aux 28 | .. 29 | . 30 | .de TAG 31 | . pca:tag-set-up 32 | . \" if 2nd arg present, use that value instead of current pageno 33 | . ie '\\$2'' .ds pca:tag-value \\n[PN] 34 | . el .ds pca:tag-value \\$2 35 | . ds TAG:\\$1 \\*[pca:tag-value] 36 | . \" continue only in unsafe mode 37 | . if !\\n[.U] .return 38 | . write pca:tag-stream .ds TAG:\\$1 \\*[pca:tag-value] 39 | .. 40 | -------------------------------------------------------------------------------- /tmac/pca-t2p-toc-lua.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2021-11-07 2 | .\" Dorai Sitaram 3 | . 4 | .if !\n[.troff2page] .nx 5 | . 6 | .de pca:toc-hook 7 | .ig ## 8 | do 9 | local f = String_table['AUXF']() .. '.toc' 10 | if not probe_file(f) then flag_missing_piece 'toc' end 11 | if Last_page_number>0 and not Node_table['TAG:__troff2page_toc'] then 12 | flag_missing_piece 'toc-in-nav-bar' 13 | end 14 | end 15 | .## 16 | .TAG __troff2page_toc 17 | .. 18 | . 19 | .ig ## 20 | defstring('pca:toc-create-link', function(n) 21 | local f = String_table['AUXF']() .. '-snippet-' .. n .. '.tmp' 22 | local secnum = String_table['pca:sec-num']() 23 | local o = make_string_output_stream() 24 | --print('pca:toc-create-link reading', f) 25 | with_open_input_file(f, function(i) 26 | if secnum ~= '' then 27 | o:write(secnum, '\n') 28 | end 29 | while true do 30 | local x = i:read '*line' 31 | if not x then break end 32 | o:write(x, '\n') 33 | end 34 | end) 35 | return url_to_html('#TAG:__pca_sec_' .. n, o:get_output_stream_string()) 36 | end) 37 | .## 38 | -------------------------------------------------------------------------------- /tmac/pca-t2p-man.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-13 2 | .\" Dorai Sitaram 3 | . 4 | .if !\n[.troff2page] .nx 5 | . 6 | .if d pca:man-write-toc-line .nx 7 | . 8 | .de TH 9 | .de TH disabled 10 | .tm Calling .TH twice in man page 11 | .disabled 12 | . 13 | .TL 14 | \\$1 15 | .RT 16 | .ND "\\$3 17 | .TAG __troff2page_toc 18 | .so \\*[AUXF].toc 19 | .nr pca:man-enable-toc-entries 1 20 | .. 21 | . 22 | .de TOCLINE-PLAIN 23 | .nr pca:man-sec-count \\$1 24 | .shift 25 | .nr pca:man-tocentry-level \\$1 26 | .shift 27 | \h'\\n[pca:man-tocentry-level]m' 28 | \\*[url #TAG:__pca_sec_\\n[pca:man-sec-count] "\\$*"] 29 | .br 30 | .. 31 | . 32 | .de pca:man-write-toc-line 33 | .if !\\n[pca:man-enable-toc-entries] .return 34 | .if !\\n[pca:man-toc-opened-p] \{\ 35 | .nr pca:man-toc-opened-p 1 36 | .open pca:toc-stream \\*[AUXF].toc 37 | .\} 38 | .nr pca:toc-count +1 39 | .TAG __pca_sec_\\n[pca:toc-count] 40 | .write pca:toc-stream .TOCLINE-PLAIN \\n[pca:toc-count] \\$* 41 | .. 42 | . 43 | .de SH 44 | .pca:man-write-toc-line 0 \\$* 45 | .pca:man-orig-SH \\$* 46 | .. 47 | . 48 | .de SS 49 | .pca:man-write-toc-line 1 \\$* 50 | .pca:man-orig-SS \\$* 51 | .. 52 | . 53 | .mso pca-t2p-man-\*[pca-eval-lang].tmac 54 | -------------------------------------------------------------------------------- /tmac/pca-t2p-man-lua.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-13 2 | . 3 | .ig ## 4 | defrequest('pca:man-orig-SH', function() 5 | emit_section_header(1, {man_header_p=true}) 6 | end) 7 | 8 | defrequest('pca:man-orig-SS', function() 9 | emit_section_header(2, {man_header_p=true}) 10 | end) 11 | 12 | defrequest('EX', function() 13 | --print('doing EX') 14 | --read_troff_line() 15 | start_display 'L' 16 | emit(switch_font 'C') 17 | --Turn_off_escape_char_p = true 18 | end) 19 | 20 | defrequest('EE', function() 21 | --print('doing EE') 22 | --read_troff_line() 23 | Escape_char = '\\' 24 | --Turn_off_escape_char_p = false 25 | stop_display() 26 | end) 27 | 28 | nb_macro_package('man') 29 | 30 | if Last_page_number>0 and not Node_table['TAG:__troff2page_toc'] then 31 | flag_missing_piece 'toc' 32 | end 33 | 34 | local it = find_macro_file 'man.local' 35 | if it then troff2page_file(it) end 36 | 37 | function call_redefined_TH(args) 38 | local it = Macro_table.TH 39 | defrequest('TH', nil) 40 | if not it then 41 | print('Couldn\'t find .TH', table.unpack(args)) 42 | return 43 | end 44 | table.insert(args, 1, 'TH') 45 | flet({Macro_args = args}, function() 46 | execute_macro_body(it) 47 | end) 48 | end 49 | 50 | .## 51 | -------------------------------------------------------------------------------- /tmac/pca-img.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-13 2 | .\" Dorai Sitaram 3 | . 4 | .if \n[.troff2page] .nx 5 | . 6 | .de IMG 7 | . ds pca:image-align -C\" 8 | . ie '\\$1'-R' \{\ 9 | . ds pca:image-align -R\" 10 | . shift 11 | . \} 12 | . el \{\ 13 | . if '\\$1'-L' \{\ 14 | . ds pca:image-align -L\" 15 | . shift 16 | . \} 17 | . \} 18 | . if '\\$1'-C' .shift 19 | . 20 | . ds pca:image-file \\$1\" 21 | . shift 22 | . 23 | . ie '\\$1'' .nr pca:image-width 1i 24 | . el .nr pca:image-width \\$1 25 | . 26 | . nr pca:image-count +1 27 | . ds pca:image-unique-name .troff_image-\\n[pca:image-count]\" 28 | . 29 | . if \\n[.U] \{\ 30 | . ds pca:image-file-ext \\*[pca:image-file] 31 | . substring pca:image-file-ext -4 32 | . ie '\\*[pca:image-file-ext]'.svg' .sy inkscape \\*[pca:image-file] -o \\*[pca:image-unique-name].eps 33 | . el .ie '\\*[pca:image-file-ext]'.png' \{\ 34 | . sy convert \\*[pca:image-file] \\*[pca:image-unique-name].bmp 35 | . sy mkbitmap \\*[pca:image-unique-name].bmp -o \\*[pca:image-unique-name].pgm 36 | . sy potrace \\*[pca:image-unique-name].pgm -e -o \\*[pca:image-unique-name].eps 37 | . \} 38 | . el .ie '\\*[pca:image-file-ext]'.jpg' \{\ 39 | . sy convert \\*[pca:image-file] \\*[pca:image-unique-name].bmp 40 | . sy mkbitmap \\*[pca:image-unique-name].bmp -o \\*[pca:image-unique-name].pgm 41 | . sy potrace \\*[pca:image-unique-name].pgm -e -o \\*[pca:image-unique-name].eps 42 | . \} 43 | . el .sy convert \\*[pca:image-file] \\*[pca:image-unique-name].eps 44 | . \} 45 | . 46 | . in 0 47 | . PSPIC \\*[pca:image-align] \\*[pca:image-unique-name].eps \\n[pca:image-width]u 48 | .. 49 | -------------------------------------------------------------------------------- /tmac/pca-eval-lisp.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-12 2 | . 3 | .ds pca:eval-file \\*[AUXF].lisp 4 | . 5 | .de pca:eval-process-snippets 6 | . \" change this to suit your Common Lisp implementation 7 | . sy touch \\*[pca:eval-file] 8 | . sy if test ! -f \\*[pca:eval-file]; then :; \ 9 | elif test "$LISP" = abcl; then abcl --batch --noinform --load \\*[pca:eval-file]; \ 10 | elif test "$LISP" = allegro; then alisp -L \\*[pca:eval-file] -kill; \ 11 | elif test "$LISP" = clisp; then clisp -q \\*[pca:eval-file]; \ 12 | elif test "$LISP" = clozure; then ccl -l \\*[pca:eval-file] -e '(ccl:quit)'; \ 13 | elif test "$LISP" = cmucl; then lisp -quiet -load \\*[pca:eval-file] -eval '(ext:quit)'; \ 14 | elif test "$LISP" = ecl; then ecl -shell \\*[pca:eval-file]; \ 15 | elif test "$LISP" = gcl; then gcl -load \\*[pca:eval-file] -eval '(sys:quit)'; \ 16 | else sbcl --script \\*[pca:eval-file]; \ 17 | fi 18 | .. 19 | . 20 | .de pca:eval-start-snippets 21 | . write pca:eval-stream (defvar *pca-eval-lisp-stdout* *standard-output*) 22 | . write pca:eval-stream (defvar *troff*) 23 | . 24 | . write pca:eval-stream (defun pca-eval-lisp-preamble (f) 25 | . write pca:eval-stream (setq *troff* 26 | . write pca:eval-stream (open f :direction :output :if-exists :supersede))) 27 | . 28 | . write pca:eval-stream (defun pca-eval-lisp-postamble () 29 | . write pca:eval-stream (format t "\\\\\\\\c~%") 30 | . write pca:eval-stream (format t ".ds pca:eval-status done~%") 31 | . write pca:eval-stream (close *troff*)) 32 | .. 33 | . 34 | .de pca:eval-write-snippet 35 | . write pca:eval-stream (pca-eval-lisp-preamble "\\*[AUXF]-eval-\\n[pca:eval-count].tmp") 36 | . eo 37 | . writem pca:eval-stream pca:eval-text 38 | . ec 39 | . write pca:eval-stream (pca-eval-lisp-postamble) 40 | .. 41 | . 42 | .if \n[.troff2page] .mso pca-t2p-eval-lisp.tmac 43 | -------------------------------------------------------------------------------- /tmac/pca-eval.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-12 2 | .\" Dorai Sitaram 3 | . 4 | .\" don't load more than once 5 | . 6 | .if d pca:eval-process-snippets .nx 7 | . 8 | .if '\*[AUXF]'' .ds AUXF .trofftemp 9 | . 10 | .\" if pca-eval-lang is neither js, lisp, or lua, assume lua 11 | . 12 | .ie '\*[pca-eval-lang]'' .ds pca-eval-lang lua 13 | .el .ie '\*[pca-eval-lang]'js' .\" 14 | .el .ie '\*[pca-eval-lang]'lisp' .\" 15 | .el .ie '\*[pca-eval-lang]'lua' .\" 16 | .el .ds pca-eval-lang lua 17 | . 18 | .nr pca:eval-already-set-up 0 19 | . 20 | .de pca:eval-set-up 21 | . if \\n[pca:eval-already-set-up] .return 22 | . nr pca:eval-already-set-up 1 23 | . nr pca:eval-count 0 24 | . if !\\n[.U] .return 25 | . pca:eval-process-snippets 26 | . open pca:eval-stream \\*[pca:eval-file] 27 | . pca:eval-start-snippets 28 | .. 29 | . 30 | .ds pca:eval-status try 31 | . 32 | .de eval 33 | . eo 34 | . de pca:eval-text endeval 35 | .. 36 | . 37 | .de endeval 38 | . ec 39 | . pca:eval-so-snippet 40 | . if !\\n[.U] .return 41 | . pca:eval-write-snippet 42 | .. 43 | .\" is empty line needed at last line of .endeval? 44 | . 45 | .de pca:eval-rerun-needed 46 | . ds pca:eval-status rerun 47 | . if !\\n[.troff2page] .tm Rerun groff with -U 48 | .. 49 | . 50 | .de pca:eval-load-snippet-if-poss 51 | . nr pca:eval-systat 0 52 | . if \\n[.U] \{\ 53 | . sy test -f \\*[pca:eval-snippet-file] 54 | . nr pca:eval-systat \\n[systat] 55 | . \} 56 | . if !\\n[pca:eval-systat] \{\ 57 | . so \\*[pca:eval-snippet-file] 58 | . if '\\*[pca:eval-status]'done' .return 59 | . \} 60 | . pca:eval-rerun-needed 61 | .. 62 | . 63 | .de pca:eval-so-snippet 64 | . pca:eval-set-up 65 | . nr pca:eval-count +1 66 | . ds pca:eval-snippet-file \\*[AUXF]-eval-\\n[pca:eval-count].tmp 67 | . if !'\\*[pca:eval-status]'rerun' \{\ 68 | . pca:eval-load-snippet-if-poss 69 | . \} 70 | . if '\\*[pca:eval-status]'rerun' \[rh]eval\[lh] 71 | .. 72 | . 73 | .mso pca-eval-\*[pca-eval-lang].tmac 74 | -------------------------------------------------------------------------------- /tmac/pca-t2p-ix-lua.tmac: -------------------------------------------------------------------------------- 1 | .\" last change 2020-12-13 2 | .\" Dorai Sitaram 3 | . 4 | .de pca:call-makeindex-pre-hook 5 | . ig ## 6 | do 7 | local f = String_table.AUXF() .. '.ind' 8 | local create_bogus_ind_file_p = false 9 | if not probe_file(f) then 10 | do end 11 | elseif Source_changed_since_last_time_p and file_write_date(f) < Last_modification_time then 12 | flag_missing_piece 'index' 13 | os.execute('rm '..f) 14 | end 15 | end 16 | . ## 17 | . nr pca:ix-location 0 18 | .. 19 | . 20 | .de pca:call-makeindex-post-hook 21 | . ig ## 22 | do 23 | local f = String_table.AUXF()..'.ind' 24 | if not probe_file(f) then 25 | with_open_output_file(f, function(o) 26 | o:write('.TAG __troff2page_index\\n') 27 | o:write('.pca:cant-open-ind-file\\n') 28 | end) 29 | end 30 | end 31 | . ## 32 | .. 33 | . 34 | .de pca:cant-open-ind-file 35 | . ig ## 36 | flag_missing_piece 'index' 37 | do 38 | local f = String_table.AUXF() .. '.ind' 39 | File_postlude = function() 40 | os.remove(f) 41 | twarning("can't open %s: No such file or directory", f) 42 | end 43 | end 44 | . ## 45 | .. 46 | . 47 | .am pca:ix-item0 48 | .ig ## 49 | Pca_t2p_ix_pageno_mention_table = {} 50 | .## 51 | .. 52 | . 53 | .am pca:ix-item1 54 | .ig ## 55 | Pca_t2p_ix_pageno_mention_table = {} 56 | .## 57 | .. 58 | . 59 | .am pca:ix-item2 60 | .ig ## 61 | Pca_t2p_ix_pageno_mention_table = {} 62 | .## 63 | .. 64 | . 65 | .de pca:ix-hook 66 | .ig ## 67 | if Last_page_number>0 and not Node_table['TAG:__troff2page_index'] then 68 | flag_missing_piece 'index-in-nav-bar' 69 | end 70 | .## 71 | .TAG __troff2page_index 72 | .. 73 | . 74 | .ig ## 75 | Pca_t2p_ix_pageno_mention_table = {} 76 | 77 | defstring('pca:ix-function', function(ixno) 78 | local ix_tag = 'TAG:__troff2page_index_' .. ixno 79 | local ix_pageno = Node_table[ix_tag] 80 | local res = link_start(page_node_link(ix_pageno, ix_tag), true) .. 81 | verbatim(ix_pageno) 82 | local n = Pca_t2p_ix_pageno_mention_table[ix_pageno] 83 | if n then 84 | res = res .. ':' 85 | n = n+1 86 | Pca_t2p_ix_pageno_mention_table[ix_pageno] = n 87 | res = res .. toroman(n, true) 88 | else 89 | Pca_t2p_ix_pageno_mention_table[ix_pageno] = 1 90 | end 91 | res = res .. link_stop() 92 | return res 93 | end) 94 | .## 95 | -------------------------------------------------------------------------------- /tmac/pca-toc.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2021-11-14 2 | .\" Dorai Sitaram 3 | . 4 | .if d pca:section-postlude .nx 5 | . 6 | .if '\*[AUXF]'' .ds AUXF .trofftemp 7 | . 8 | .de ToC 9 | . nr pca:DI \\n(DI 10 | . if !\\n[pca:DI] .nr pca:DI 1.5m 11 | . if !\n[.troff2page] .nr pca:DI \\n[pca:DI]/2u 12 | . if d pca:toc-hook .pca:toc-hook 13 | . so \\*[AUXF].toc 14 | . nr pca:sec-triggers-tocentry-p 1 15 | . if !\\n[pca:toc-stream-open-p] \{\ 16 | . nr pca:toc-stream-open-p 1 17 | . open pca:toc-stream \\*[AUXF].toc 18 | . \} 19 | .. 20 | . 21 | .if !\n[.U] .nx 22 | . 23 | .de pca:sec-with-tocentry 24 | . nr pca:inside-section-p 0 25 | . nr pca:toc-header-level \\n[nh*hl] 26 | . nr pca:create-tocentry 1 27 | . ie !\\n[pca:sec-triggers-tocentry-p] .nr pca:create-tocentry 0 28 | . el .if (\\n[pca:toc-header-level] > \\n[GROWPS]) .nr pca:create-tocentry 0 29 | . if \\n[pca:create-tocentry] \{\ 30 | . nr pca:inside-section-p 1 31 | . nr pca:toc-count +1 32 | . di pca:toc-header-text 33 | . \} 34 | .. 35 | . 36 | .rn @SH pca:toc-orig-@SH 37 | .rn @NH pca:toc-orig-@NH 38 | . 39 | .de @SH 40 | . ds SN-STYLE \" 41 | . pca:toc-orig-@SH \\$* 42 | . ie '\\$1'' .nr nh*hl \\n[GROWPS] 43 | . el .nr nh*hl \\$1 44 | . pca:sec-with-tocentry 45 | .. 46 | . 47 | .de @NH 48 | . pca:toc-orig-@NH \\$* 49 | . pca:sec-with-tocentry 50 | .. 51 | . 52 | .als SH @SH 53 | .als NH @NH 54 | . 55 | .de pca:write-toc-line 56 | . nr pca:this-indent \\n[pca:DI]u 57 | . nr pca:this-indent (\\n[pca:this-indent]*\\n[pca:toc-header-level]) 58 | . write pca:toc-stream .IP "" \\n[pca:this-indent]u 59 | . write pca:toc-stream \\*[pca:toc-header-text] 60 | . write pca:toc-stream \\&... \\n[%] 61 | .. 62 | . 63 | .de pca:section-postlude 64 | . nr pca:inside-section-p 0 65 | . if !\n[.troff2page] .br 66 | . di 67 | . asciify pca:toc-header-text 68 | . chop pca:toc-header-text 69 | . if \n[.troff2page] \{\ 70 | . open pca:toc-snippet-stream \\*[AUXF]-snippet-\\n[pca:toc-count].tmp 71 | . write pca:toc-snippet-stream \\*[pca:toc-header-text] 72 | . close pca:toc-snippet-stream 73 | . \} 74 | . pca:write-toc-line 75 | . if d TAG .TAG __pca_sec_\\n[pca:toc-count] 76 | . ie \n[.troff2page] .so \\*[AUXF]-snippet-\\n[pca:toc-count].tmp 77 | . el .nop \\*[pca:toc-header-text] 78 | .. 79 | . 80 | .am ds@auto-end 81 | . if \\n[pca:inside-section-p] .pca:section-postlude 82 | .. 83 | . 84 | .if \n[.troff2page] .mso pca-t2p-toc.tmac 85 | -------------------------------------------------------------------------------- /tmac/pca-ix.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-12 2 | .\" Dorai Sitaram 3 | . 4 | .\" disable for nroff 5 | .if n .nx 6 | . 7 | .\" don't source more than once 8 | .if d pca:set-up-index .nx 9 | . 10 | .if '\*[AUXF]'' .ds AUXF .trofftemp 11 | . 12 | .\" Index 13 | . 14 | .nr pca:index-already-set-up 0 15 | . 16 | .de pca:ix-item0 17 | . in 0i 18 | .. 19 | . 20 | .de pca:ix-item1 21 | . in .25i 22 | .. 23 | . 24 | .de pca:ix-item2 25 | . in .5i 26 | .. 27 | . 28 | .de pca:call-makeindex 29 | . \" calls makeindex if .idx is present and .ind isn't 30 | . sy if test -f \\*[AUXF].idx; then mv \\*[AUXF].idx z\\*[AUXF].idx; fi 31 | . sy if test -f z\\*[AUXF].idx -a ! -f \\*[AUXF].ind; then makeindex -s \\*[AUXF].mst -r z\\*[AUXF].idx; fi 32 | . sy if test -f z\\*[AUXF].ind; then mv z\\*[AUXF].ind \\*[AUXF].ind; fi 33 | . sy rm -f z\\*[AUXF].idx z\\*[AUXF].ilg 34 | .. 35 | . 36 | .de pca:make-mst-file 37 | . open pca:mst-stream \\*[AUXF].mst 38 | . write pca:mst-stream preamble ". 39 | . write pca:mst-stream .if n .nx 40 | . write pca:mst-stream .if d pca:ix-hook .pca:ix-hook 41 | . write pca:mst-stream .de see 42 | . \" in the following 43 | . \" 8 \s in .de body 44 | . \" 4 \s when .write is called 45 | . \" 2 \s in the string written to 46 | . \" 1 \s when the string is unslashed 47 | . \" so use 4 \s when you want a character like newline 48 | . \" otherwise use 8 times the required nr of \s 49 | . write pca:mst-stream \\\\\\\\fIsee\\\\\\\\fR \\\\\\\\\\\\\\\\$1 50 | . write pca:mst-stream .. 51 | . write pca:mst-stream .de seealso 52 | . write pca:mst-stream \\\\\\\\fIsee also\\\\\\\\fR \\\\\\\\\\\\\\\\$1 53 | . write pca:mst-stream .." 54 | . \"write pca:mst-stream .ns" 55 | . write pca:mst-stream postamble "\\\\n" 56 | . write pca:mst-stream group_skip "\\\\n.sp .5v" 57 | . write pca:mst-stream item_0 "\\\\n.pca:ix-item0\\\\n\\\\\\\\&" 58 | . write pca:mst-stream item_1 "\\\\n.pca:ix-item1\\\\n\\\\\\\\&" 59 | . write pca:mst-stream item_01 "\\\\n.pca:ix-item1\\\\n\\\\\\\\&" 60 | . write pca:mst-stream item_x1 "\\\\n.pca:ix-item1\\\\n\\\\\\\\&" 61 | . write pca:mst-stream item_2 "\\\\n.pca:ix-item2\\\\n\\\\\\\\&" 62 | . write pca:mst-stream item_12 "\\\\n.pca:ix-item2\\\\n\\\\\\\\&" 63 | . write pca:mst-stream item_x2 "\\\\n.pca:ix-item2\\\\n\\\\\\\\&" 64 | . write pca:mst-stream delim_r "-" 65 | . write pca:mst-stream indent_space "" 66 | . write pca:mst-stream encap_prefix "\\\\\\\\*[" 67 | . write pca:mst-stream encap_infix " " 68 | . write pca:mst-stream encap_suffix "]" 69 | . close pca:mst-stream 70 | .. 71 | . 72 | .de pca:set-up-index 73 | . if \\n[pca:index-already-set-up] .return 74 | . nr pca:index-already-set-up 1 75 | . if d pca:call-makeindex-pre-hook .pca:call-makeindex-pre-hook 76 | . pca:call-makeindex 77 | . if d pca:call-makeindex-post-hook .pca:call-makeindex-post-hook 78 | . pca:make-mst-file 79 | . open pca:idx-stream \\*[AUXF].idx 80 | .. 81 | . 82 | .de IX 83 | . if !\\n[.U] .return 84 | . pca:set-up-index 85 | . write pca:idx-stream \\\\indexentry{\\$*}{\\n[%]} 86 | .. 87 | . 88 | .de INDX 89 | . so \\*[AUXF].ind 90 | .. 91 | . 92 | .if \n[.troff2page] .mso pca-t2p-ix.tmac 93 | -------------------------------------------------------------------------------- /bin/adoc2ms: -------------------------------------------------------------------------------- 1 | # convert .adoc to .ms 2 | # Dorai Sitaram 3 | # last modified 2021-11-14 4 | # created < 2016-02-11 5 | 6 | f= 7 | 8 | if test $# -ne 1; then 9 | echo adoc2ms needs exactly one arg 10 | exit 1 11 | else 12 | f=$1 13 | fi 14 | 15 | f_dir=${f%/*} 16 | 17 | if test "$f_dir" = "$f"; then 18 | f_dir="" 19 | else 20 | f_dir=$f_dir/ 21 | fi 22 | 23 | f_without_dir=${f##*/} 24 | 25 | f_basename=${f_without_dir%.*} 26 | 27 | f_dir_basename=$f_dir$f_basename 28 | 29 | asciidoctor $f 30 | 31 | cp $f_dir_basename.html $f_dir.dbg.html 32 | 33 | sed -i \ 34 | -e 's//%ImageForGroff% \1 \2/' \ 35 | $f_dir_basename.html 36 | 37 | sed -i \ 38 | -e 's/^
/\0%%VERSEBLOCK%%/' \ 39 | $f_dir_basename.html 40 | 41 | vi -es $f_dir_basename.html <$/ +1s/^.*$// 43 | x 44 | EOF 45 | 46 | pandoc $f_dir_basename.html -t ms -o $f_dir_basename.ms 47 | 48 | cp $f_dir_basename.ms $f_dir.dbg.ms 49 | 50 | # inside vi heredoc, mostly double backslashes, 51 | # exceptions: \( \) \< \> 52 | 53 | vi -es $f_dir_basename.ms </.TL/ 98 | "1,5 g/^\.TL/+1 s/$/\r.LP/ 99 | "%s/^\.SS\s/.SH 2\r/ 100 | %s/\\\\f\[\(.\)\]/\\\\f\1/g 101 | %s/\\\\fR\>/\\\\fP/g 102 | 103 | set para& 104 | set sect& 105 | 106 | set para+=TSpd 107 | 108 | " don't really want newlines inserted at sentence ends 109 | 110 | g/^\.[LP]P$/ norm gqip 111 | 112 | %s/^\.%verbatim%/./ 113 | 114 | x 115 | EOF 116 | 117 | # heredoc above doesn't seem to like outputting Unicode chars 118 | 119 | sed -i \ 120 | -e 's/\\\[cq\]/’/g' \ 121 | -e 's/\\\[lq\]/“/g' \ 122 | -e 's/\\\[rq\]/”/g' \ 123 | -e 's/\\\[em\]/—/g' \ 124 | -e 's/ / /g' \ 125 | -e 's/​//g' \ 126 | $f_dir_basename.ms 127 | 128 | if test $ADOC2MS2PS; then 129 | # if $ADOC2MS2PS is set, further convert the ms file to pdf 130 | test "$f_dir" != "" && cd $f_dir 131 | soelim $f_basename.ms | 132 | tbl | 133 | preconv | 134 | groff -U -mpca > $f_basename.ps 135 | fi 136 | -------------------------------------------------------------------------------- /tmac/pca-t2p-info-lua.tmac: -------------------------------------------------------------------------------- 1 | .\" last modified 2020-12-06 2 | .ig ## 3 | 4 | function html2info() 5 | if Rerun_needed_p then 6 | tlog 'Unable to create Info doc because aux files unresolved.\n' 7 | return 8 | else 9 | tlog('Converting %s.html to %s.info\n', Jobname, Jobname) 10 | end 11 | local tmp_html_file = Jobname .. '-Z-T.html' 12 | local tmp_info_file = Jobname .. '-Z-T.info' 13 | local info_file = Jobname .. '.info' 14 | local i = 0 15 | 16 | --print('Last_page_number=', Last_page_number) 17 | 18 | with_open_output_file(info_file, function(o) 19 | while true do 20 | if i>Last_page_number then break end 21 | copy_file_to_file(Jobname .. (i==0 and '' or ('-Z-H-' .. i)) .. '.html', tmp_html_file) 22 | o:write(string.char(0x1f), '\nFile: ', info_file) 23 | -- 24 | if i==0 then o:write ', Node: Top' end 25 | if i>0 then o:write(', Node: ', i) end 26 | -- 27 | if i==1 then o:write ', Prev: Top' end 28 | if i>1 then o:write(', Prev: ', i-1) end 29 | -- 30 | if i ~= Last_page_number then o:write(', Next: ', i+1) end 31 | 32 | o:write ', Up: Top\n' 33 | 34 | html2info_tweak_html_file(tmp_html_file, i) 35 | 36 | os.execute('lynx -dump -nolist ' .. tmp_html_file .. ' > ' .. tmp_info_file) 37 | os.remove(tmp_html_file) 38 | 39 | copy_file_to_stream(tmp_info_file, o) 40 | os.remove(tmp_info_file) 41 | 42 | i=i+1 43 | end 44 | end) 45 | --print('created infofile i') 46 | --copy_file_to_file(info_file, "junk.info") 47 | html2info_tweak_info_file(info_file) 48 | --print('tweaked infofile ii') 49 | end 50 | 51 | Unlikely_ascii_string = 'QBKZF@' -- gots to be ascii, not unicode! 52 | 53 | function sed(f, ...) 54 | local cmd = 'sed -i' 55 | local argv = {...} 56 | local argc = #argv 57 | for i=1,argc do 58 | local subcmd = argv[i] 59 | subcmd = subcmd:gsub('QQ', Unlikely_ascii_string) 60 | cmd = cmd .. " -e '" .. subcmd .. "'" 61 | end 62 | cmd = cmd .. ' ' .. f 63 | --print('osexecuting cmd=', cmd) 64 | os.execute(cmd) 65 | end 66 | 67 | function html2info_tweak_html_file(f, i) 68 | --print('doing html2info_tweak_html_file', f) 69 | sed(f, 70 | 71 | -- mark any verbatim instances of the Unlikely_ascii_string 72 | "s/QQ/&!/g", 73 | 74 | -- delete navigation lines 75 | "/^/d", 76 | 77 | -- mark beginning and end of ToC 78 | "s/^/QQINFO_MENU_BEGIN!* Menu:
/", 79 | 80 | -- 81 | "s/^/QQINFO_MENU_END!/", 82 | 83 | -- create Info menu entry for all doc-internal links within ToC 84 | string.format("/^/,/^/ s//* 0:: /g", Jobname), 85 | 86 | -- 87 | string.format("/^/,/^/ s//* \\1:: /g", 88 | Jobname), 89 | 90 | -- 91 | string.format("/^/,/^/ s/\"/* %s:: /g", i), 92 | 93 | -- disable all other page-internal links 94 | "s///g", 95 | 96 | -- mark all doc-internal links in the index (if any 97 | -- page "0" 98 | string.format("s//QQI!0::/g", Jobname), 99 | 100 | -- pages 1+ 101 | string.format("s//QQI!\\1::/g", Jobname), 102 | 103 | -- 104 | string.format("s//QQI!%s::/g", i), 105 | 106 | -- all other doc-internal links become Info *note's 107 | string.format("s//QQN!0::/g", Jobname), 108 | 109 | -- 110 | string.format("s//QQN!\\1::/g", Jobname), 111 | 112 | -- disable all doc-external links 113 | "s///g", 114 | 115 | -- delete all anchors 116 | "s///g", 117 | 118 | -- are all orphans now -- delete 119 | "s/<\\/a>//g") 120 | end 121 | 122 | function html2info_tweak_info_file(f) 123 | --print('doing html2info_tweak_info_file', f) 124 | sed(f, 125 | -- flushleft menu entries 126 | 127 | "/^\\s*QQINFO_MENU_BEGIN!/,/^\\s*QQINFO_MENU_END!/ s/^\\(\\s*\\)\\(\\*\\s\\+[^:]\\+::\\)/\\2\\1/", 128 | 129 | -- delete menu markers 130 | "s/^\\s*QQINFO_MENU_\\(BEGIN\\|END\\)!//", 131 | 132 | -- expand doc-internal notes (not index) 133 | "s/QQN!\\([0-9]\\+::\\)/*note \\1/g", 134 | 135 | -- expand doc-internal notes in index 136 | "s/QQI!\\([0-9]\\+\\)::\\1/*note \\1::/g", 137 | 138 | -- restore QQ 139 | "s/QQ!/QQ/g" 140 | ) 141 | end 142 | 143 | .## 144 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = mpca 2 | 3 | == Introduction 4 | 5 | The venerable typesetter groff is ubiquitous. With the standard 6 | package `ms`, it lets you create a wide variety of documents in 7 | plain text with your favorite text editor. It lacks some basic 8 | features, however, viz., cross references, table of contents, and 9 | index. 10 | 11 | The `mpca` macro package is an unobtrusive extension of the 12 | standard `ms` package that provides workable defaults for these 13 | basic needs which can be extended or overridden as needed. 14 | 15 | `mpca` may stand for “Macros for the Prevention of Cruelty to 16 | Authors”. 17 | 18 | == Installation 19 | 20 | Get the `mpca` package from GitHub: 21 | 22 | git clone https://github.com/ds26gte/mpca 23 | 24 | Inside the directory `mpca`, the subdirectory `tmac` contains the 25 | macro file `pca.tmac` and several subfiles `++pca-*.tmac++`. 26 | Either include this directory in your `GROFF_TMAC_PATH`, or add 27 | the `++*.tmac++` files here to some directory in your 28 | `GROFF_TMAC_PATH`. 29 | 30 | [TIP] 31 | -- 32 | If you’re just experimenting, you could simply copy the 33 | `++*.tmac++` files to your home directory. 34 | 35 | The `++pca-t2p-*.tmac++` files are used only if you process your 36 | documents with http://ds26gte.github.io/troff2page[troff2page]. 37 | They are not needed for `groff`. If you don’t plan to use 38 | troff2page, you don’t need to put them in `GROFF_TMAC_PATH`. The 39 | other `++pca*.tmac++` files are used by both groff and troff2page. 40 | -- 41 | 42 | == Invocation 43 | 44 | To use `mpca`, you can simply source `pca.tmac` at the head of 45 | your document: 46 | 47 | .mso pca.tmac 48 | 49 | Alternatively, you can use the `groff` command-line option `-m`, 50 | e.g., 51 | 52 | % groff -mpca doc.ms > doc.ps 53 | 54 | Other `groff` options can be added as usual. 55 | 56 | Let’s now go into the `mpca` features that you can use in your document. 57 | 58 | == Init file 59 | 60 | `mpca` will source a macro file `.groffrc.tmac` if it can find it in 61 | your `GROFF_TMAC_PATH` or home directory. 62 | 63 | `~/.groffrc.tmac` is a good place to put minor customizations that are 64 | relevant only to your system. (E.g., I find I sometimes need to 65 | slightly tweak the page-offset register (`PO`) to suit a 66 | particular printer — this tweak needn’t or shouldn’t be enshrined 67 | in my document.) 68 | 69 | == Verbatim display 70 | 71 | Use `.EX` to start and `.EE` to end a verbatim display, typically 72 | used for program listings, e.g., 73 | 74 | .EX 75 | function fact(n) 76 | if n == 0 then return 1 77 | else return n*fact(n - 1) 78 | end 79 | end 80 | .EE 81 | 82 | In this style of call, a display is allowed to cross page 83 | boundaries. You can't use the backslash (`\`) without triggering 84 | a troff escape. You also can't use a period (`.`) in the first 85 | column of any line in the display. This is typically not a 86 | problem if you consistently indent the body of your displays 87 | anyway. (Periods in the middle of lines are ok.) 88 | 89 | You _can_ use a leading period to effect styling changes. For 90 | example 91 | 92 | .EX 93 | function fact(n) 94 | .ft CI 95 | -- returns the factorial of n 96 | .ft C 97 | if n == 0 then return 1 98 | else return n*fact(n - 1) 99 | end 100 | end 101 | .EE 102 | 103 | Adding an argument to `.EX` (doesn't matter what, say `1`) lets 104 | you use backslash verbatim in the display, however this requires 105 | the display to fit in one page. (The reason is that groff's page boundary 106 | traps require the troff escape enabled.) 107 | 108 | In the general `.EX` (without argument), you can briefly 109 | turn off the troff escape with `.eo`, as long as you quickly 110 | (before danger of a page boundary) turn 111 | it back on with `.ec`. These lines of course use `.` in the 112 | first column and will not be themselves displayed. 113 | 114 | == Image 115 | 116 | The `.IMG` macro can be used to insert image files. The syntax 117 | follows that of the `.IMG` macro in the `www.tmac`, but (a) isn’t 118 | restricted to HTML output. E.g., 119 | 120 | .IMG t2p.png 121 | 122 | sources the image `t2p.png`. 123 | You can specify the image alignment with an optional first argument: `-L` 124 | for left, `-R` for right, `-C` for centered. If no alignment is 125 | specified, `-C` is assumed. 126 | 127 | .IMG -L t2p.png 128 | 129 | `.IMG` takes an optional final argument to specify the width of 130 | the image. The default is 1 inch. 131 | 132 | .IMG t2p.png 3i 133 | .IMG -L t2p.png 4i 134 | 135 | `.IMG` relies on the external programs `convert` (from 136 | ImageMagick); `mkbitmap` and `potrace` (both from the Potrace 137 | package); and Inkscape. (`mkbitmap` and `potrace` are needed 138 | for PNG images. Inkscape is needed for SVG 139 | images.) 140 | 141 | == Page cross-references 142 | 143 | The `.TAG` macro manages cross-references. E.g., 144 | 145 | .TAG sec_grofflua 146 | 147 | associates the label `TAG:sec_grofflua` with the number of the 148 | current page. The _string_ `\*[TAG:sec_grofflua]` is defined to 149 | typeset as that page number. Thus, in a hand-crafted table of 150 | contents, you could use 151 | 152 | Extending groff using Lua, \*[TAG:sec_grofflua] 153 | 154 | `.TAG` takes an optional second argument. The label is then 155 | associated with the text of the second argument instead of the 156 | current page number. 157 | 158 | NOTE: `mpca`’s `.TAG` overrides a similarly named macro in 159 | the file `www.tmac` in the groff distribution, which only 160 | allows backward references. 161 | 162 | IMPORTANT: `.TAG` requires two runs of `groff`. Please see the 163 | section on aux files. 164 | 165 | == Table of contents 166 | 167 | The `.ToC` macro inserts a table of contents (ToC). Add your own 168 | header, e.g., “Contents”, or “Table of Contents” (which is also 169 | the default value of the `ms` string `\*[TOC]`). 170 | 171 | `.ToC` does not require you to modify how you use your sectioning 172 | macros — it automatically draws its information from the 173 | distribution of the `.NH` and `.SH` macros within your document. 174 | It is thus a solution to the following statement from the groff 175 | manual: 176 | 177 | [quote] 178 | Altering the ‘NH’ macro to automatically build the table of contents 179 | is perhaps initially more difficult, but would save a great deal of time 180 | in the long run if you use ‘ms’ regularly. 181 | 182 | ToC entries are generated for the usual `ms` section headers (`.SH`, 183 | `.NH`). The _depth_ of the ToC is governed by the number register 184 | `GROWPS`: Only those `.SH`/`.NH` headers at a level less than or 185 | equal to `GROWPS` will go into the ToC. 186 | 187 | The number register `\n[pca:DI]` (default: .5 inch) determines 188 | how much subsections are indented in the ToC. 189 | 190 | == Index 191 | 192 | The `.IX` macro is used to generate index entries: 193 | 194 | .IX item to be indexed 195 | 196 | marks the text “item to be indexed” as an indexable item. The sorted index made 197 | from these entries can be sourced into the input document via 198 | 199 | .so \*[AUXF].ind 200 | 201 | Adding a section header on top is up to you. 202 | 203 | The sorted index is constructed using the external program 204 | `makeindex`. `makeindex` is included in TeX distributions, but 205 | you can also obtain it as 206 | http://stuff.mit.edu/afs/sipb/project/tex-dev/src/tar/makeindex.tar.gz[a 207 | standalone package]. 208 | 209 | The metacharacters `@`, `!`, `"`, and `|` can be used 210 | to respectively specify 211 | 212 | 1. alternate alphabetization, 213 | 2. subitems, 214 | 3. literal metacharacters, and 215 | 4. encapsulation of the page number. 216 | 217 | E.g., 218 | 219 | .IX m@-m, groff option 220 | 221 | identifies an index entry for “-m, groff option” but alphabetizes 222 | it as though it were “m” rather than something that starts with a 223 | hyphen. 224 | 225 | .IX groff!macro packages 226 | 227 | makes “macro packages” an indented index subentry under “groff”. 228 | 229 | Up to two ``!``s may be used. 230 | 231 | .IX groff!macro packages!ms 232 | 233 | produces “ms” as a subsubentry under “macro packages” under 234 | “groff”. 235 | 236 | .IX troff|see groff 237 | 238 | has the index entry for “troff” point to 239 | “groff” rather than have a page number of its own. 240 | 241 | If any of the metacharacters need to 242 | appear in the index entry as themselves, precede them with `"`. 243 | 244 | .IX set"!car 245 | 246 | creates an index entry for “set!car” rather than creating a 247 | subentry “car” under “set"”. 248 | 249 | [TIP] 250 | -- 251 | The syntax for `.IX` calls is essentially the same as for LaTeX, 252 | except that in groff we use 253 | 254 | .IX item 255 | 256 | where in LaTeX one would use 257 | 258 | \index{item} 259 | -- 260 | 261 | NOTE: For full details on index-entry syntax, consult the 262 | http://tex.loria.fr/bibdex/makeindex.pdf[makeindex 263 | documentation]. 264 | 265 | == Bibliography 266 | 267 | The `.BIB` macro is used to introduce a paragraph that is a simple bibliographic 268 | reference, e.g., 269 | 270 | .BIB utp 271 | David S. Landes, \fIRevolutions in Time: Clocks and the 272 | Making of the Modern World\fP, Belknap Press, 1983. 273 | 274 | If it is the __n__th such reference in the document, it is prefixed 275 | with “[_n_]” in the output. Furthermore, the label `TAG:utp` is 276 | associated with _n_, using the cross-reference mechanism 277 | described earlier, and can be used to cite the reference. E.g., 278 | 279 | For a history of the first portable device that, for better or 280 | worse, completely changed how we live, see Landes 281 | [\*[TAG:landes_clock]]. 282 | 283 | == Eval 284 | 285 | The macro `.eval` allows you to insert Lua, Common Lisp or JavaScript 286 | code in your document to guide its transformation via 287 | groff. In other words, it lets you you use Lua, CL, or JS to 288 | _extend_ groff instead of relying purely on groff macros. 289 | 290 | The code inside `.eval` is evaluated using the language specified 291 | by the string `pca-eval-lang`, which by default is `lua`. 292 | 293 | We will first describe the Lua version of `.eval`. 294 | 295 | === Lua 296 | 297 | `.eval` does only one thing: It allows you to place arbitrary 298 | Lua code until the following `.endeval`, and the text written to 299 | the stream `troff` this Lua code is substituted for the `.eval ... 300 | .endeval`. The usefulness of this tactic will be apparent from an 301 | example. Consider the following document, `tau.ms`: 302 | 303 | The ratio of the circumference of a circle to 304 | its radius is \(*t \(~= 305 | .eval 306 | -- the following prints tau, because cos(tau/2) = -1 307 | troff:write(2*math.acos(-1), '.\n') 308 | .endeval 309 | 310 | Run it through `mpca`: 311 | 312 | groff -z -U -mpca tau.ms 313 | 314 | The `-z` avoids generating ouput, because we’re not ready for it 315 | yet. The `-U` runs `groff` in “unsafe” mode, i.e., it allows the 316 | writing of aux files. 317 | 318 | You will find that the `groff` call produces the following 319 | message: 320 | 321 | Rerun groff with -U 322 | 323 | Call `groff` again as folows: 324 | 325 | groff -U -mpca tau.ms > tau.ps 326 | 327 | `tau.ps` will now look like: 328 | 329 | ==== 330 | The ratio of the circumference of a circle to 331 | its radius is τ ≈ 6.2831853071796. 332 | ==== 333 | 334 | Here’s how it works. The first `groff` call produces a Lua file 335 | `\*[AUXF].lua` that collects all the `.eval` code in the 336 | document. The second `groff` call invokes Lua to create an aux 337 | file for each `.eval` and sources it back into the document. 338 | 339 | It should be clear that Lua code via `.eval` can serve as a very 340 | powerful _second extension language_ for groff. For a more 341 | substantial example of `.eval`’s use see 342 | http://ds26gte.github.io/troff2page[the troff2page manual]. 343 | 344 | === Common Lisp 345 | 346 | To use Common Lisp inside `.eval`, set 347 | 348 | .ds pca-eval-lang lisp 349 | 350 | in your document before the first use of `.eval`. Thus, the 351 | `tau.ms` file, translated to Common Lisp, will now read: 352 | 353 | .ds pca-eval-lang lisp 354 | The ratio of the circumference of a circle to 355 | its radius is \(*t \(~= 356 | .eval 357 | ;the following prints tau, because cos(tau/2) = -1 358 | (princ (* 2 (acos -1)) *troff*) 359 | (princ "." *troff*) 360 | (terpri *troff*) 361 | .endeval 362 | 363 | NOTE: For the Common Lisp `.eval`, we write to the stream 364 | `++*troff*++` rather than `troff`. 365 | 366 | === JavaScript 367 | 368 | To use JavaScript inside `.eval`, set 369 | 370 | .ds pca-eval-lang js 371 | 372 | in your document before the first use of `.eval`. Thus, the 373 | `tau.ms` file, translated to JavaScript, will now read: 374 | 375 | .ds pca-eval-lang js 376 | The ratio of the circumference of a circle to 377 | its radius is \(*t \(~= 378 | .eval 379 | // the following prints tau, because cos(tau/2) = -1 380 | troff.write('' + 2*Math.acos(-1)); 381 | troff.write('.\n'); 382 | .endeval 383 | 384 | == Aux files 385 | 386 | `mpca` uses auxiliary (aux) files to implement its 387 | cross-referencing, ToC, indexing, and eval features. 388 | 389 | The troff string `\*[AUXF]` is used to construct the names of 390 | these auxiliary files. By default this is quietly set to `.trofftemp`. 391 | You can change it to something else (provided it satisfies 392 | your OS’s file-naming conventions) in your document before the first use of 393 | any macros that use or write aux files. 394 | 395 | Aux files are created in one run of `groff` and slurped back in 396 | during a second run. Thus `groff` needs to be run twice for the 397 | defined feature to take effect. Furthermore, the first run of 398 | `groff` must be run in “unsafe” mode (`groff` option `-U`) as 399 | `groff` won’t create external files in “safe” mode. 400 | 401 | == Using only some of mpca’s features 402 | 403 | TIP: You may ignore this section if you don’t mind loading all of 404 | the `mpca` features. 405 | 406 | You may pick and choose individual features of `mpca` 407 | without committing to the rest of it. 408 | To do this source one or more of the following 409 | macro files: 410 | `pca-eval.tmac` (eval), 411 | `pca-img.tmac` (images), 412 | `pca-ix.tmac` (index), 413 | `pca-sc.tmac` (thought break), 414 | `pca-tag.tmac` (cross-references), 415 | and 416 | `pca-toc.tmac` (ToC). 417 | E.g., 418 | 419 | .mso pca-eval.tmac 420 | 421 | If the feature uses aux files, you will need to run `groff` 422 | twice, once in unsafe mode, 423 | as described in the section on aux files. 424 | 425 | == Adding OpenType Fonts to groff 426 | 427 | See https://github.com/ds26gte/otf2groff. 428 | 429 | // last modified 2021-11-07 430 | --------------------------------------------------------------------------------