11 function make_date
(d
) {
12 gsub("[ \t,]+", "-", d
)
16 function trigger_header
(hdate
, hsect
, htitle
) {
17 if(hdate
) date = make_date
(hdate
)
18 if(hsect
) sect = hsect
19 if(htitle
) title = htitle
20 if(sect
&& date
&& title
)
21 print ".TH", title
, sect
, date
23 # turns something like aa "b c" d "e f g"
24 # into a 0-indexed array with the members(aa, b c, d, e f g)
25 # returns length of array
26 function unquotewords
(wordsstr
, ao
, l
,a
,out
,inpara
,start
,i
) {
27 l =
split(wordsstr
, a
, "")
31 for(i =
1; i
<=l
; i
++) {
33 if(start == i
) print "ERR"
35 ao
[out
++] =
substr(wordsstr
, start
, i
-start
);
38 } else if(a
[i
] ==
"\"") {
40 ao
[out
++] =
substr(wordsstr
, start
, i
-start
);
44 if(start
!= i
) print "err"
50 if(i
> start
) ao
[out
++] =
substr(wordsstr
, start
, i
-start
);
54 function arr2str
(arr
, start
, len
, i
,str
) {
55 for(i=start
;i
<=len
;i
++) if(i in arr
) str=str arr
[i
] " ";
59 function cfunc
(str
, i
,l
,a
,out
,restore
) {
60 l = unquotewords
(str
, a
)
61 # first array index, 0, contains macro name still so we skip it
62 #print arr2str(a, 0, l)
67 for(i =
0; i
< l
; i
++) {
69 if(i ==
0) out = out
" "
70 else if(i ==
1) out = out
"("
71 if (i == l
- 1) out = out
")"
72 else if(i
> 1) out = out
", "
74 if(restore
) out = out restore
77 function brify
(str
,appendnl
, temp
) {
78 str =
".BR \"" substr(str
, index(str
, " ")+1) "\""
79 if(appendnl
) str = str
"\n"
82 function italify
(str
,appendnl
, temp
) {
83 str =
".I \"" substr(str
, index(str
, " ")+1) "\""
84 if(appendnl
) str = str
"\n"
87 function fields
(start
, last
, buf
,i
,sp
) {
89 for(i=start
;i
<=last
;i
++) {
90 sp = i
+1<=last ?
" " : ""
95 function update_var
(var_name
, complete_line
) {
97 current_var = var_name
98 groff_vars
[current_var
] =
substr(complete_line
, 4+length(var_name
)+2)
100 groff_vars
[current_var
] = groff_vars
[current_var
] complete_line
102 if(groff_vars
[current_var
] ~
/\\$
/) {
103 sub(/\\$
/, "\n", groff_vars
[current_var
])
109 function replace_vars
(line
, var
, p
, c
, start
, len
) {
110 # match(s,r) test whether s contains a substring matched by r
111 # return index or 0, sets RSTART and RLENGTH
112 while((p =
match(line
, /\\\
*([\
(\
[]{0,1}[A
-Za
-z0
-9_
]+)/))) {
113 c =
substr(line
, RSTART+2, 1)
114 if(c ==
"[" || c ==
"(") {
122 var =
substr(line
, RSTART+2+start
, len
)
125 if(substr(line
, RSTART+2+len
, 1) != "]")
126 print "ERROR: expected ']'"
129 if(!var in groff_vars
) print "ERROR: $var not in groff_vars"
130 if(groff_vars
[var
] ~
/\\\
*/) {
131 print "ERROR: groff variable recursion"
134 line =
substr(line
, 0, RSTART-1) groff_vars
[var
] substr(line
, RSTART+2+len
)
138 function groff_escape
(line
, nr, repl
, p
) {
139 # replace \N'34' with "
140 while((p =
match(line
, /\\N
\'([0-9]+)\'/))) {
141 nr =
0+substr(line
, RSTART+3, RLENGTH-4)
142 if(nr < 32 || nr > 127) {
143 print("ERROR: escapes < 32 || > 127 not implemented");
145 } else if(nr ==
34) {
148 repl =
sprintf("%c", nr)
149 line =
substr(line
, 0, RSTART-1) repl
substr(line
, RSTART+RLENGTH)
153 function shift
(n
, k
) {
155 k
+=
length($n
) + length(FS)
158 $
0 =
substr($
0, k
+ 1)
160 function print_error
(lineno
, text
) {
161 print "ERROR: @" lineno
": " text
169 line_no = line_no
+ 1
170 if($
1 ~
/^\.
/ && close_indent
) {
176 $
0 = groff_escape
(replace_vars
($
0))
180 if($
1 ==
".ie" && substr($
0, length($
0) - 2) != "\\{\\") {
181 if($
2 ==
"n" || $
2 ==
"!t" || $
2 ==
"e" || $
2 ==
"!o") {
183 $
0 =
substr($
0, length($
1 FS $
2 FS)+1)
188 } else if($
1 ==
".el" && substr($
0, length($
0) - 2) != "\\{\\") {
189 if(ie_taken
) $
0 =
"";
190 else $
0 =
substr($
0, length($
1 FS)+1)
192 } else if($
1 ==
".if" && substr($
0, length($
0) - 2) != "\\{\\") {
193 if($
2 ==
"n" || $
2 ==
"!t" || $
2 ==
"e" || $
2 ==
"!o")
194 $
0 =
substr($
0, length($
1 FS $
2 FS)+1);
202 } else if($
1 ==
".EX") {
204 $
0 =
".sp\n.RS\n.nf\n\\fB"
205 } else if(example_block
) {
208 $
0 =
"\\fP\n.fi\n.RE";
210 } else if(current_var
) {
211 #process multiline groff string vars.
212 update_var
(current_var
, $
0)
214 } else if(ignore_until
!= "") {
215 if($
0 == ignore_until
)
219 # https://www.lemoda.net/unix/troff-dictionary/index.html
220 } else if(in_macro
&& $
1 != "..") {
222 } else if(in_macro
&& $
1 ==
"..") {
225 } else if(!in_macro
&& $
1 ==
"..") {
226 print_error
(line_no
, ".. without .de")
227 } else if(!in_macro
&& $
1 ==
".de") {
231 } else if(multi_line_if
) {
232 if(substr($
0, length($
0) - 1) ==
"\\}")
233 multi_line_if = multi_line_if
- 1
235 } else if($
1 ==
".if" || $
1 ==
".ie" || $
1 ==
".el\\{\\" ||
236 $
1 ==
".el" || $
1 ==
"\\{\\") {
237 if(substr($
0, length($
0) - 2) ==
"\\{\\")
238 multi_line_if = multi_line_if
+ 1
241 # http://web.archive.org/web/20140327172811/http://mdocml.bsd.lv/mdoc.7.html
242 } else if($
1 ==
".Bl") {
244 $
0 =
".sp\n.RS\n.nf\n"
247 } else if ($
1 ==
".El") {
253 } else if($
1 ==
".Bd") {
254 # code block - dump directly until we hit .Ed
255 $
0 =
".sp\n.RS\n.nf\n\\fB"
256 } else if($
1 ==
".Ed") {
257 $
0 =
"\\fP\n.fi\n.RE"
258 } else if($
1 ==
".Ex") {
259 locname =
NF==
3?$
3:name
260 $
0 =
"The \n.BR " locname
"\nutility exits 0 on success, and >0 if an error occurs.\n"
261 } else if($
1 ==
".Dd") {
263 trigger_header
($
0, "", "")
265 } else if($
1 ==
".Dt") {
266 trigger_header
("", $
3, $
2)
268 } else if($
1 ==
".Dq" || $
1 ==
".Ql") {
269 if ($
NF ==
"." || $
NF ==
",") {
271 $
0 =
substr($
0, 0, length($
0) - 2)
273 $
0 =
"“" substr($
0, 5) "”" safe
274 } else if($
1 ==
".Dv") {
276 } else if($
1 ==
".Xr") {
278 } else if($
1 ==
".An") {
279 if($
2 ~
"^-") $
0 =
""
280 else $
0 =
substr($
0, 5)
281 } else if($
1 ==
".In") {
282 $
0 =
"\n.BR \"#include <" $
2 ">\"\n"
283 } else if($
1 ==
".Ft") {
285 } else if($
1 ==
".Fn") {
286 $
0 = brify
(cfunc
($
0), $
NF != ",")
287 } else if($
1 ==
".Fo") {
290 } else if($
1 ==
".Fa") {
292 fnblock = fnblock
" " substr($
0, 5)
295 l = unquotewords
(substr($
0, 5), foo
)
303 } else if($
1 ==
".Fc") {
304 $
0 = brify
(cfunc
(fnblock
), 1)
306 } else if($
1 ==
".Cm") {
308 for(i =
2; i
<=
NF; i
+=
2)
311 } else if($
1 ==
".Ic") {
313 } else if($
1 ==
".Tn" || $
1 ==
".Va" || $
1 ==
".Fx") {
315 } else if($
1 ==
".Po") {
317 } else if($
1 ==
".Pc") {
319 } else if($
1 ==
".Pq") {
321 for(i =
2; i
<=
NF; i
++)
322 if(!
(length($i
) ==
2 && $i ~
/[A
-Z
][a
-z
]/))
324 if(substr(out
, length(out
)) ==
" ")
325 out =
substr(out
, 0, length(out
) -1)
327 } else if($
1 ==
".Pf") {
329 } else if($
1 ==
".Pp") {
331 } else if($
1 ==
".Sm") {
332 spacing = $
2==
"on"?
1:0 #unused
334 } else if($
1 ==
".Oo" || $
1 ==
".Oc") {
335 # denotes .Op block start/end - ignore
337 } else if($
1 ==
".It" || $
1 ==
".Op") {
338 if(blockmode
&& $
1 ==
".It" && $
2 != "Pa") print "\n"
339 #if(blockmode) indent_after=1
340 out = $
1 ==
".Op" ?
"[" : "";
341 for(i =
2; i
<=
NF; i
++) {
342 if(i ==
2 && $i ==
"Fn" && $
1 ==
".It") {
344 out =
".Fn " substr($
0, 8)
345 out = brify
(cfunc
(out
),1)
348 } else if (i ==
2 && $i ==
"Pa" && $
1 ==
".It") {
349 sub(/^\.It Pa
/, ".I")
352 } else if (i ==
2 && $i ==
"Ev" && $
1 ==
".It") {
354 } else if (i ==
2 && $i ==
"Xo") {
357 } else if($i ==
"Fl" || $i ==
"Ar" || $i ==
"Cm") {
358 if($i ==
"Fl") str =
"BR -"
359 else if($i ==
"Ar") str =
"I "
362 if(out ==
"") { nl =
"" } else { nl =
"\n" }
363 out = out nl
"." str $i
;
364 if($i ==
"Fl") while($
(i
+1) ==
"|") {
365 out = out
" | -" $
(i
+2)
373 if($
1 ==
".Op") out = out
"\n]"
374 if(blockmode
&& $
1 ==
".It") out = out
"\n"
376 } else if($
1 ==
".Ar") {
379 } else if($
1 ==
".Fl") {
380 #if(blockmode) indent()
382 for(i =
2; i
<=
NF; i
++) {
385 } else if($
1 ==
".Nd") {
387 } else if($
1 ==
".Nm") {
388 if(name ==
"" && NF ==
2) {
392 sep =
NF > 1 ?
"\n" : ""
393 $
0 =
".BR \"" name
"\"" sep fields
(2, NF)
394 } else if($
1 ==
".At") {
396 } else if($
1 ==
".Bx") {
398 } else if($
1 ==
".Nx") {
400 } else if($
1 ==
".Ux") {
402 } else if($
1 ==
".St") {
404 else if($
2 ==
"-p1003.1-88")
405 $
0 =
"IEEE Std 1003.1-1988 (“POSIX.1”)"
406 else if($
2 ==
"-p1003.1-90")
407 $
0 =
"IEEE Std 1003.1-1990 (“POSIX.1”)"
408 else if($
2 ==
"-p1003.1-96")
409 $
0 =
"ISO/IEC 9945-1:1996 (“POSIX.1”)"
410 else if($
2 ==
"-p1003.1-2001")
411 $
0 =
"IEEE Std 1003.1-2001 (“POSIX.1”)"
412 else if($
2 ==
"-p1003.1-2004")
413 $
0 =
"IEEE Std 1003.1-2004 (“POSIX.1”)"
414 else if($
2 ==
"-p1003.1-2008")
415 $
0 =
"IEEE Std 1003.1-2008 (“POSIX.1”)"
416 else if($
2 ==
"-p1003.1")
417 $
0 =
"IEEE Std 1003.1 (“POSIX.1”)"
418 else if($
2 ==
"-p1003.1b")
419 $
0 =
"IEEE Std 1003.1b (“POSIX.1”)"
420 else if($
2 ==
"-p1003.1b-93")
421 $
0 =
"IEEE Std 1003.1b-1993 (“POSIX.1”)"
422 else if($
2 ==
"-p1003.1c-95")
423 $
0 =
"IEEE Std 1003.1c-1995 (“POSIX.1”)"
424 else if($
2 ==
"-p1003.1g-2000")
425 $
0 =
"IEEE Std 1003.1g-2000 (“POSIX.1”)"
426 else if($
2 ==
"-p1003.1i-95")
427 $
0 =
"IEEE Std 1003.1i-1995 (“POSIX.1”)"
428 else if($
2 ==
"-p1003.2-92")
429 $
0 =
"IEEE Std 1003.2-1992 (“POSIX.2”)"
430 else if($
2 ==
"-p1003.2a-92")
431 $
0 =
"IEEE Std 1003.2a-1992 (“POSIX.2”)"
432 else if($
2 ==
"-p1387.2-95")
433 $
0 =
"IEEE Std 1387.2-1995 (“POSIX.7.2”)"
434 else if($
2 ==
"-p1003.2")
435 $
0 =
"IEEE Std 1003.2 (“POSIX.2”)"
436 else if($
2 ==
"-p1387.2")
437 $
0 =
"IEEE Std 1387.2 (“POSIX.7.2”)"
438 else if($
2 ==
"-isoC")
439 $
0 =
"ISO/IEC 9899:1990 (“ISO C90”)"
440 else if($
2 ==
"-isoC-90")
441 $
0 =
"ISO/IEC 9899:1990 (“ISO C90”)"
442 else if($
2 ==
"-isoC-amd1")
443 $
0 =
"ISO/IEC 9899/AMD1:1995 (“ISO C90, Amendment 1”)"
444 else if($
2 ==
"-isoC-tcor1")
445 $
0 =
"ISO/IEC 9899/TCOR1:1994 (“ISO C90, Technical Corrigendum 1”)"
446 else if($
2 ==
"-isoC-tcor2")
447 $
0 =
"ISO/IEC 9899/TCOR2:1995 (“ISO C90, Technical Corrigendum 2”)"
448 else if($
2 ==
"-isoC-99")
449 $
0 =
"ISO/IEC 9899:1999 (“ISO C99”)"
450 else if($
2 ==
"-isoC-2011")
451 $
0 =
"ISO/IEC 9899:2011 (“ISO C11”)"
452 else if($
2 ==
"-iso9945-1-90")
453 $
0 =
"ISO/IEC 9945-1:1990 (“POSIX.1”)"
454 else if($
2 ==
"-iso9945-1-96")
455 $
0 =
"ISO/IEC 9945-1:1996 (“POSIX.1”)"
456 else if($
2 ==
"-iso9945-2-93")
457 $
0 =
"ISO/IEC 9945-2:1993 (“POSIX.2”)"
458 else if($
2 ==
"-ansiC")
459 $
0 =
"ANSI X3.159-1989 (“ANSI C89”)"
460 else if($
2 ==
"-ansiC-89")
461 $
0 =
"ANSI X3.159-1989 (“ANSI C89”)"
462 else if($
2 ==
"-ansiC-99")
463 $
0 =
"ANSI/ISO/IEC 9899-1999 (“ANSI C99”)"
464 else if($
2 ==
"-ieee754")
465 $
0 =
"IEEE Std 754-1985"
466 else if($
2 ==
"-iso8802-3")
467 $
0 =
"ISO 8802-3: 1989"
468 else if($
2 ==
"-iso8601")
470 else if($
2 ==
"-ieee1275-94")
471 $
0 =
"IEEE Std 1275-1994 (“Open Firmware”)"
472 else if($
2 ==
"-xpg3")
473 $
0 =
"X/Open Portability Guide Issue 3 (“XPG3”)"
474 else if($
2 ==
"-xpg4")
475 $
0 =
"X/Open Portability Guide Issue 4 (“XPG4”)"
476 else if($
2 ==
"-xpg4.2")
477 $
0 =
"X/Open Portability Guide Issue 4, Version 2 (“XPG4.2”)"
478 else if($
2 ==
"-xpg4.3")
479 $
0 =
"X/Open Portability Guide Issue 4, Version 3 (“XPG4.3”)"
480 else if($
2 ==
"-xbd5")
481 $
0 =
"X/Open Base Definitions Issue 5 (“XBD5”)"
482 else if($
2 ==
"-xcu5")
483 $
0 =
"X/Open Commands and Utilities Issue 5 (“XCU5”)"
484 else if($
2 ==
"-xsh5")
485 $
0 =
"X/Open System Interfaces and Headers Issue 5 (“XSH5”)"
486 else if($
2 ==
"-xns5")
487 $
0 =
"X/Open Networking Services Issue 5 (“XNS5”)"
488 else if($
2 ==
"-xns5.2")
489 $
0 =
"X/Open Networking Services Issue 5.2 (“XNS5.2”)"
490 else if($
2 ==
"-xns5.2d2.0")
491 $
0 =
"X/Open Networking Services Issue 5.2 Draft 2.0 (“XNS5.2D2.0”)"
492 else if($
2 ==
"-xcurses4.2")
493 $
0 =
"X/Open Curses Issue 4, Version 2 (“XCURSES4.2”)"
494 else if($
2 ==
"-susv2")
495 $
0 =
"Version 2 of the Single UNIX Specification"
496 else if($
2 ==
"-susv3")
497 $
0 =
"Version 3 of the Single UNIX Specification"
498 else if($
2 ==
"-svid4")
499 $
0 =
"System V Interface Definition, Fourth Edition (“SVID4”)"
500 } else if($
1 ==
".Pa" || $
1 ==
".Em" || $
1 ==
".Vt") {
502 } else if($
1 ==
".Bk" || $
1 ==
".Ek" || $
1 ==
".Xc" || $
1 ==
".Os") {
505 } else if($
1 ==
".D1" || $
1 ==
".Dl") {
507 } else if($
1 ==
".Ev") {
509 } else if($
1 ==
".Aq") {
511 if(NF > 2 && length($
2)==
2) cut
+=
3
514 } else if($
1 ==
".Qq") {
515 $
0 =
"\"" $
2 "\"" fields
(3, NF)
516 } else if($
1 ==
".Sq") {
517 $
0 =
"'" $
2 "'" fields
(3, NF)
518 } else if($
1 ==
".Sx" || $
1 ==
".Sy") {
520 } else if($
1 ==
".Sh") {
522 } else if($
1 ==
".Ss") {
524 # xman stuff starts here
525 } else if($
1 ==
".ZN") {
527 # groff stuff starts here
528 # http://web.cecs.pdx.edu/~trent/gnu/groff/groff.html
529 # lowercase stuff supported by man.c:
530 # nh hy nf fi sp br bp ad na ta
531 } else if($
1 ==
".ig") {
537 } else if($
1 ==
".ds") { #set a string variable.
540 } else if($
1 ==
".UR") {
543 sub("//", "://", url
)
545 } else if($
1 ==
".UE") {
549 } else if($
1 ==
".ds" ||
563 $
1 ==
".rr" || #remove number register ident
564 0) { #other groff junk
566 } else if($
1 ==
".KS" ||
575 0) { #other unknown stuff
577 # perl stuff starts here
578 # http://www.opensource.apple.com/source/perl/perl-24.1/perl/lib/Pod/Man.pm
579 } else if($
1 ==
".Vb") { #perl verbose section starts
580 $
0 =
".sp\n.RS\n.nf\n"
581 } else if($
1 ==
".Ve") { #perl verbose section ends
583 } else if($
1 ==
".tr" ||
587 0) { #other perl junk