-
Notifications
You must be signed in to change notification settings - Fork 1
/
manweb
executable file
·456 lines (408 loc) · 14.6 KB
/
manweb
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
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
#!/usr/bin/env bash
# Created by argbash-init v2.10.0
# ARG_OPTIONAL_BOOLEAN([pdf],[p],[If passed, the script will not open the man page, it will just open it as a PDF.])
# ARG_OPTIONAL_BOOLEAN([where],[w],[Don't actually show the man page, but print the URL of the matching page, if found.])
# ARG_OPTIONAL_BOOLEAN([debug],[d],[Print debug information too],[off])
# ARG_OPTIONAL_BOOLEAN([try-local],[t],[If there's a local man page available, don't try to fetch from network],[on])
# ARG_OPTIONAL_SINGLE([output],[o],[The output file. If passed, the script will not open the man page, it will just save it to disk in roff format. When combined with the --pdf option, it will save the PDF to the provided filename.])
# ARG_OPTIONAL_SINGLE([pdf-viewer],[],[The command to open the PDF file (defaults to "xdg-open" or "open" if not provided)])
# ARG_POSITIONAL_SINGLE([name-or-url],[You can pass either the name of the man page, the URL to a manned.org page, or the URL to any raw man page in roff format (hosted at manned.org or not)])
# ARG_POSITIONAL_SINGLE([section],[],[""])
# ARG_DEFAULTS_POS([])
# ARG_HELP([ManWEB - Open a man page from Manned.org\n],[],[h],[help],[Prints this help])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.dev for more info
die()
{
local _ret="${2:-1}"
test "${_PRINT_HELP:-no}" = yes && print_help >&2
echo "$1" >&2
exit "${_ret}"
}
begins_with_short_option()
{
local first_option all_short_options='pwdtoh'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
# THE DEFAULTS INITIALIZATION - POSITIONALS
_positionals=()
_arg_name_or_url=
_arg_section=""
# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_pdf="off"
_arg_where="off"
_arg_debug="off"
_arg_try_local="on"
_arg_output=
_arg_pdf_viewer=
print_help()
{
printf '%s\n' "ManWEB - Open a man page from Manned.org
"
printf 'Usage: %s [OPTIONS] <name-or-url> [<section>]\n' "$0"
printf '\t%s\n' "<name-or-url>:"
printf '\t\t%s\n' "You can pass either the name of the man page, the url to a"
printf '\t\t%s\n' "manned.org page, or the URL to any raw man page in roff format"
printf '\t\t%s\n' "(hosted at manned.org or not)"
printf '\t%s\n' '-p, --pdf, --no-pdf:'
printf '\t\t%s\n' "if passed, the script will not open the man page on the terminal,"
printf '\t\t%s\n' "it will just open it as a pdf."
printf '\t\t%s\n' "(off by default)"
printf '\t%s\n' '-w, --where, --no-where:'
printf '\t\t%s\n' "don't actually show the man page, but print the url of the matching"
printf '\t\t%s\n' "page, if found."
printf '\t\t%s\n' "(off by default)"
printf '\t%s\n' '-d, --debug, --no-debug:'
printf '\t\t%s\n' "print debug information too."
printf '\t\t%s\n' "(off by default)"
printf '\t%s\n' '-t, --try-local, --no-try-local:'
printf '\t\t%s\n' "if there's a local man page available, don't try to fetch from"
printf '\t\t%s\n' "network."
printf '\t\t%s\n' "(on by default)"
printf '\t%s\n' '-o, --output:'
printf '\t\t%s\n' "the output file. if passed, the script will not open the man page,"
printf '\t\t%s\n' "it will just save it to disk in roff format. when combined with the"
printf '\t\t%s\n' "--pdf option, it will save the pdf to the provided filename."
printf '\t\t%s\n' "(no default)"
printf '\t%s\n' '--pdf-viewer:'
printf '\t\t%s\n' "the command to open the pdf file."
printf '\t\t%s\n' "(defaults to 'xdg-open' or 'open' if not provided)"
printf '\t\t%s\n' "(no default)"
printf '\t%s\n' '-h, --help:'
printf '\t\t%s\n' "prints this help."
}
parse_commandline()
{
_positionals_count=0
while test $# -gt 0
do
_key="$1"
case "$_key" in
-p|--no-pdf|--pdf)
_arg_pdf="on"
test "${1:0:5}" = "--no-" && _arg_pdf="off"
;;
-p*)
_arg_pdf="on"
_next="${_key##-p}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-p" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-w|--no-where|--where)
_arg_where="on"
test "${1:0:5}" = "--no-" && _arg_where="off"
;;
-w*)
_arg_where="on"
_next="${_key##-w}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-w" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-d|--no-debug|--debug)
_arg_debug="on"
test "${1:0:5}" = "--no-" && _arg_debug="off"
;;
-d*)
_arg_debug="on"
_next="${_key##-d}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-d" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-t|--no-try-local|--try-local)
_arg_try_local="on"
test "${1:0:5}" = "--no-" && _arg_try_local="off"
;;
-t*)
_arg_try_local="on"
_next="${_key##-t}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-t" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-o|--output)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_output="$2"
shift
;;
--output=*)
_arg_output="${_key##--output=}"
;;
-o*)
_arg_output="${_key##-o}"
;;
--pdf-viewer)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_pdf_viewer="$2"
shift
;;
--pdf-viewer=*)
_arg_pdf_viewer="${_key##--pdf-viewer=}"
;;
-h|--help)
print_help
exit 0
;;
-h*)
print_help
exit 0
;;
*)
_last_positional="$1"
_positionals+=("$_last_positional")
_positionals_count=$((_positionals_count + 1))
;;
esac
shift
done
}
handle_passed_args_count()
{
local _required_args_string="'name-or-url'"
test "${_positionals_count}" -ge 1 || _PRINT_HELP=yes die "FATAL ERROR: Not enough positional arguments - we require between 1 and 2 (namely: $_required_args_string), but got only ${_positionals_count}." 1
test "${_positionals_count}" -le 2 || _PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect between 1 and 2 (namely: $_required_args_string), but got ${_positionals_count} (the last one was: '${_last_positional}')." 1
}
assign_positional_args()
{
local _positional_name _shift_for=$1
_positional_names="_arg_name_or_url _arg_section "
shift "$_shift_for"
for _positional_name in ${_positional_names}
do
test $# -gt 0 || break
eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
shift
done
}
parse_commandline "$@"
handle_passed_args_count
assign_positional_args 1 "${_positionals[@]}"
# OTHER STUFF GENERATED BY Argbash
### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash
# vvv PLACE YOUR CODE HERE vvv
DEBUG=0
if [[ "$_arg_debug" == "on" ]]; then
DEBUG=1
fi
# Assume that we want silent output if --where was passed
SILENT=0
if [[ "$_arg_where" == "on" ]]; then
SILENT=1
fi
# Figure out if we're dealing with a name or a URL
if [[ "$_arg_name_or_url" =~ ^https?://.* ]]; then
# We're dealing with a URL
URL="$_arg_name_or_url"
if [[ "$URL" =~ ^https?://manned.org/.* ]]; then
# We're dealing with a manned.org URL
# If the URL contains a single path "/man/", we need to replace it with
# "/raw/"
if [[ "$URL" =~ .*/man/.* ]]; then
URL=$(sed -e 's/\/man\//\/raw\//g' <<< "$URL")
fi
fi
# Get name and section from the last part of the URL, after the last slash
# /NAME.SECTION
LAST_PATH=$(sed -e 's/.*\///g' <<< "$URL")
NAME=$(sed -e 's/\..*//g' <<< "$LAST_PATH")
SECTION=$(sed -e 's/.*\.//g' <<< "$LAST_PATH")
else
# We're dealing with a name
NAME="$_arg_name_or_url"
SECTION="$_arg_section"
# default to section one if not provided
if [[ -z "$SECTION" ]]; then
SECTION=1
fi
# if --try-local is provided and file exists locally render it or display
# location
if [[ "$_arg_try_local" == "on" ]]; then
# run man -w
MAN_ARGUMENTS="$NAME"
if [[ -n "$SECTION" ]]; then
MAN_ARGUMENTS="$SECTION $MAN_ARGUMENTS"
fi
COMMAND="man -w $MAN_ARGUMENTS 2>/dev/null"
if [[ "$DEBUG" == "1" ]]; then
echo "Running: $COMMAND"
fi
FILE=$(eval "$COMMAND")
# handle local file
if [[ -f "$FILE" ]]; then
if [[ "$SILENT" == "0" ]]; then
echo "File exists locally: $FILE"
fi
if [[ "$_arg_where" == "on" ]]; then
echo "$FILE"
exit 0
else
if [[ "$DEBUG" == "1" ]]; then
echo "Rendering to terminal with: man -l $FILE"
fi
man -l "$FILE"
exit 0
fi
fi
# handle no local file
if [[ "$SILENT" == "0" ]]; then
echo "File does not exist locally. Attempting to download"
fi
fi
fi
# same as the one used on man-db
declare -a section_search_order=("1" "8" "3" "2" "5" "4" "9" "6" "7")
# fetch from url
download_file() {
local FILE="/tmp/$1.$2"
# if the file already exists and it's not empty (thereby suggesting an error
# in the previous attempt) don't download it again
if [[ -f "$FILE" ]] && [[ -s "$FILE" ]]; then
if [[ "$DEBUG" == "1" ]]; then
echo "File already cached, skipping download: $FILE"
fi
return 0
fi
local URL="https://manned.org/raw/$1.$2"
if [[ "$DEBUG" == "1" ]]; then
echo "Downloading file: $URL"
fi
curl -f -A 'goggle/9.0 (x11; linux x86_64; rv:60.0) gecko/20100101 chrome/103.0.5060.71' -s "$URL" > "$FILE"
return $?
}
# if a section was explicitly provided
if [[ -n "$_arg_section" ]]; then
download_file $NAME $SECTION
if [[ "$?" == "22" ]]; then
echo "Online man page not found" 1>&2
exit 1
fi
FILE="/tmp/$NAME.$SECTION"
URL="https://manned.org/raw/$NAME.$SECTION"
else
# try every section in the provided order
for i in "${section_search_order[@]}"; do
if [[ "$DEBUG" == "1" ]]; then
echo "Trying section $i"
fi
SECTION=$i
download_file $NAME $SECTION
if [[ "$?" == "0" ]]; then
FILE="/tmp/$NAME.$SECTION"
URL="https://manned.org/raw/$NAME.$SECTION"
# If file is found in a section which is not section one, create a
# symbolic link from the page.1 to the found one so that successive
# invokation of the script will use the cached version
if [[ "$SECTION" != "1" ]]; then
ln -sf "$FILE" "/tmp/$NAME.1"
fi
break
elif [[ "$?" == "22" ]] && [[ "$SILENT" == "0" ]]; then
echo "Online man page not found" 1>&2
fi
done
fi
# exit on error
if [[ "$?" != "0" ]]; then
echo "Error downloading online man page" 1>&2
exit 1
fi
if [[ "$DEBUG" == "1" ]]; then
echo "File saved to: $FILE"
fi
if [[ "$SILENT" == "0" ]]; then
echo "Downloaded file: $URL"
fi
# Post-process file:
# For every line of the form ".so directory/name.section", download the
# required man page and insert its content in place of the line (non recursive)
so_lines=$(grep -E '^\.so .*$' "$FILE")
for so_line in "$so_lines"; do
# exit if the line is empty
if [[ -z "$so_line" ]]; then
continue
fi
if [[ "$DEBUG" == "1" ]]; then
echo "Processing .so line: $so_line"
fi
# so_file is eveything afte the last /
so_file=$(echo "$so_line" | sed -e 's/.*\///g')
so_file_name=$(echo "$so_file" | sed -e 's/\..*//g')
so_file_section=$(echo "$so_file" | sed -e 's/.*\.//g')
# download the file
download_file "$so_file_name" "$so_file_section"
# replace the line with the content of the file:
# 1. print all lines up to the line
# 2. print the content of the file
# 3. print all lines after the line
sed '\=+^'"${so_line}"'$=q' "$FILE" | sed '$d' > /tmp/manpage
cat "/tmp/$so_file_name.$so_file_section" >> /tmp/manpage
sed -E '1,\=^'"${so_line}"'$=d' "$FILE" >> /tmp/manpage
mv /tmp/manpage "$FILE"
done
# if --where is provided, just print the URL and exit
if [[ "$_arg_where" == "on" ]]; then
echo "$URL"
exit 0
fi
# if requested, render to terminal
# render if neithr --pdf, --output or --where are provided
if [[ "$_arg_pdf" == "off" && -z "$_arg_output" && "$_arg_where" == "off" ]]; then
if [[ "$DEBUG" == "1" ]]; then
echo "Rendering to terminal with: man -l $FILE"
fi
man -l "$FILE"
exit 0
fi
# if requested, generate pdf
if [[ "$_arg_pdf" == "on" ]]; then
# first, check if bin manpdf exists
if [[ -x "$(command -v manpdf)" ]]; then
# if requested, save to file
if [[ -n "$_arg_output" ]]; then
OUTPUT="$_arg_output"
if [[ "$DEBUG" == "1" ]]; then
echo "Saving to pdf with: manpdf --no-open-pdf --output $OUTPUT $FILE"
fi
manpdf --no-open-pdf --output "$OUTPUT" "$FILE"
echo "Saved to $OUTPUT"
exit 0
fi
# if pdf-viewer is provided, add it as an option to manpdf
manpdf_options=""
if [[ -n "$_arg_pdf_viewer" ]]; then
manpdf_options="--pdf-viewer $_arg_pdf_viewer"
fi
man_command="manpdf $manpdf_options $FILE"
if [[ "$DEBUG" == "1" ]]; then
echo "Rendering to pdf with: $man_command"
fi
eval "$man_command"
exit 0
else
echo "Manpdf not found. Please install it to render to pdf" 1>&2
exit 1
fi
fi
# if requested, save to file (roff)
if [[ -n "$_arg_output" ]]; then
OUTPUT="$_arg_output"
if [[ "$DEBUG" == "1" ]]; then
echo "Saving to file with: cp $FILE $OUTPUT"
fi
cp "$FILE" "$OUTPUT"
echo "Saved to $OUTPUT"
echo "You can open it by running: man -l $OUTPUT"
exit 0
fi
# ] <-- needed because of Argbash