1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
|
#!/bin/sh
[ -n "$BASH" ] || exec bash -p $0 $@
# Script to process man pages output by doxygen.
# We need to use bash for its associative array facility.
# (`bash -p` prevents import of functions from the environment).
# Args: none or 2 being man7 page name & relative path of source with \mainpage
declare -A renamed_page
done_synopsis=false
main(){
set -e
# make_man7 has no dependencies or dependants so kick it off now
[ $# -ne 2 ] || make_man7 $@ &
pushd man/man3 >/dev/null; rm -f _*
count_real_pages
rename_real_pages
# Nothing depends on make_symlinks so background it
make_symlinks &
post_process $@
wait
}
count_real_pages(){
page_count=0
#
# Count "real" man pages (i.e. not generated by MAN_LINKS)
# MAN_LINKS pages are 1-liners starting .so
# Method: list files in descending order of size,
# looking for the first 1-liner
#
for i in $(ls -S)
do head -n1 $i | grep -E -q '^\.so' && break
page_count=$(($page_count + 1))
done
first_link=$(($page_count + 1))
}
rename_real_pages(){
for i in $(ls -S | head -n$page_count)
do
j=$(ed -s $i <<////
/Functions/+1;.#
/^\\.RI/;.#
.,.s/^.*\\\\fB//
.,.s/\\\\.*//
.,.w /dev/stdout
Q
////
).3
mv -f $i $j
renamed_page[$i]=$j
done
}
make_symlinks(){
for j in $(ls -S | tail -n+$first_link)
do ln -sf ${renamed_page[$(cat $j | cut -f2 -d/)]} $j
done
}
post_process(){
#
# DIAGNOSTIC / DEVELOPMENT CODE
# set -x and restrict processing to keep_me: un-comment to activate
# Change keep_me as required
#
#keep_me=nfq_icmp_get_hdr.3
#do_diagnostics
# Record doxygen version
i=$(doxygen --version)
doxymajor=$(echo $i|cut -f1 -d.)
doxyminor=$(echo $i|cut -f2 -d.)
# Decide if we need to fix rendering of verbatim "\n"
[ $doxymajor -eq 1 -a $doxyminor -lt 9 ] &&
fix_newlines=true || fix_newlines=false
# Decide if we need to fix double-to-single-quote conversion
[ $doxymajor -eq 1 -a $doxyminor -ge 9 -a $doxyminor -lt 13 ] &&
fix_quotes = true || fix_quotes=false
# Work through the "real" man pages
for target in $(ls -S | head -n$page_count)
do grep -Eq "^\\.SH \"Function Documentation" $target || continue
{
del_bogus_synopsis
$done_synopsis || del_modules
fix_name_line
move_synopsis
del_empty_det_desc
del_def_at_lines
fix_double_blanks
[ $# -ne 2 ] || insert_see_also $@
# Work around doxygen bugs (doxygen version-specific)
# Best effort: \" becomes \'
# Only do lines with some kind of printf,
# since other single quotes might be OK as-is.
$fix_quotes && sed -i '/printf/s/'\''/"/g' $target
# Fix rendering of verbatim "\n" (in code snippets)
$fix_newlines && sed -i 's/\\n/\\\\n/' $target
}&
done
}
make_man7(){
target=$(grep -Ew INPUT doxygen.cfg | rev | cut -f1 -d' ' | rev)/$2
mypath=$(dirname $0)
# Build up temporary source in temp.c
# (doxygen only makes man pages from .c files).
ed -s $target << ////
1,/\\\\mainpage/d
0i
/**
* \\defgroup $1 $1 overview
.
/\\*\\//+1,\$d
a
/**
* @{
*
* $1 - DELETE_ME
*/
int $1(void)
{
return 0;
}
/**
* @}
*/
.
wq temp.c
////
# Create temporary doxygen config in doxytmp
grep -Ew PROJECT_NUMBER doxygen.cfg >doxytmp
cat >>doxytmp <<////
PROJECT_NAME = $1
ABBREVIATE_BRIEF =
FULL_PATH_NAMES = NO
TAB_SIZE = 8
OPTIMIZE_OUTPUT_FOR_C = YES
EXAMPLE_PATTERNS =
ALPHABETICAL_INDEX = NO
SEARCHENGINE = NO
GENERATE_LATEX = NO
INPUT = temp.c
GENERATE_HTML = NO
GENERATE_MAN = YES
MAN_EXTENSION = .7
////
doxygen doxytmp >/dev/null
# Remove SYNOPSIS line if there is one
target=man/man7/$1.7
mygrep "SH SYNOPSIS" $target
[ $linnum -eq 0 ] || delete_lines $linnum $((linnum+1))
# doxygen 1.8.9.1 and possibly newer run the first para into NAME
# (i.e. in this unusual group). There won't be a SYNOPSIS when this happens
if grep -Eq "overview$1" $target; then
echo "Re-running doxygen $(doxygen --version)"
ed -s temp.c << ////
2a
* \\manonly
.PP
.SH "Detailed Description"
.PP
\\endmanonly
.
wq
////
doxygen doxytmp >/dev/null
fi
rm temp.c doxytmp
}
# Insert top-level "See also" of man7 page in man3 page
insert_see_also(){
mygrep "Detailed Description" $target
[ $linnum -ne 0 ] || mygrep "Function Documentation" $target
[ $linnum -ne 0 ] || { echo "NO HEADER IN $target" >&2; return; }
ed -s $target <<////
${linnum}i
.SH "See also"
\\fB${1}\\fP(7)
.
wq
////
}
fix_double_blanks(){
linnum=1
#
# Older versions of man display a blank line on encountering "\fB\fP";
# newer versions of man do not.
# doxygen emits "\fB\fP" on seeing "\par" on a line by itself.
# "\par" gives us double-spacing in the web doc, which we want, but double-
# spacing looks odd in a man page so remove "\fB\fP".
#
while [ $linnum -ne 0 ]
do mygrep \\\\fB\\\\fP $target
[ $linnum -eq 0 ] || delete_lines $linnum $linnum
done
}
del_def_at_lines(){
linnum=1
while [ $linnum -ne 0 ]
do mygrep '^Definition at line (\\fB)?[[:digit:]]*(\\fP)? of file' $target
[ $linnum -eq 0 ] || delete_lines $(($linnum - 1)) $linnum
done
}
# Only invoked if you un-comment the 2 diagnostic / development lines above
do_diagnostics(){
mv $keep_me xxx
rm *.3
mv xxx $keep_me
page_count=1
set -x
}
del_empty_det_desc(){
mygrep "^\\.SH \"Function Documentation" $target
i=$linnum
mygrep "^\\.SH \"Detailed Description" $target
[ $linnum -ne 0 ] || return 0
[ $(($i - $linnum)) -eq 3 ] || return 0
# A 1-line Detailed Description is also 3 lines long,
# but the 3rd line is not empty
i=$(($i -1))
[ $(tail -n+$i $target | head -n1 | wc -c) -le 2 ] || return 0
delete_lines $linnum $i
}
move_synopsis(){
mygrep "SH SYNOPSIS" $target
[ $linnum -ne 0 ] || return 0
i=$linnum
# If this is a doxygen-created synopsis, leave it.
# (We haven't inserted our own one in the source yet)
mygrep "^\\.SS \"Functions" $target
[ $i -gt $linnum ] || return 0
ed -s $target <<////
/^\\.SS \"Functions\"/;.d
.ka
/^\\.SH SYNOPSIS/;/^[[:space:]]*\$/-1m'a-1
/\"Function Documentation\"/-1;.d
wq
////
}
fix_name_line(){
all_funcs=""
# Search a shortened version of the page in case there are .RI lines later
mygrep "^\\.SH \"Function Documentation" $target
head -n$linnum $target >../$target.tmp
while :
do foundline=$(grep -En "^\\.RI" ../$target.tmp 2>/dev/null | head -n1)
[ "$foundline" ] || break
linnum=$(echo $foundline | cut -f1 -d:)
# Discard this entry (and all previous lines)
ed -s ../$target.tmp <<////
1,${linnum}d
wq
////
func=$(echo $foundline | cut -f2 -d\\ | cut -c3-)
[ -z "$all_funcs" ] && all_funcs=$func ||\
all_funcs="$all_funcs, $func"
done
# For now, assume name is at line 5
desc=$(head -n5 $target | tail -n1 | cut -f3- -d" ")
ed -s $target <<////
5c
$all_funcs \\- $desc
.
wq
////
rm ../$target.tmp
}
# Prior to doxygen 1.8.20 there was a "Modules" entry which became part of the
# "bogus" synopsis. Doxygen 1.11.0 replaces "Modules" with "Topics" still as
# part of the "bogus" synopsis and so cleaned up by del_bogus_synopsis().
del_modules(){
grep -Eq "^\\.SS \"Modules" $target || return 0
ed -s $target <<////
/^\\.SS \"Modules/,/^\\.SS \"Functions/-1d
wq
////
}
del_bogus_synopsis(){
[ $(grep -E 'SH SYNOPSIS' $target | wc -l) -eq 2 ] || return 0
#
# doxygen 1.8.20 inserts its own SYNOPSIS line but there is no mention
# in the documentation or git log what to do with it.
# So get rid of it
#
ed -s $target <<////
/SH SYNOPSIS/,/^\\.SS \"Functions/-1d
wq
////
done_synopsis=true
}
# Delete lines $1 through $2 from $target
delete_lines(){
ed -s $target <<////
$1,$2d
wq
////
}
mygrep(){
linnum=$(grep -En "$1" $2 2>/dev/null | head -n1 | cut -f1 -d:)
[ $linnum ] || linnum=0
}
main $@
|