1#!/bin/sh
2# Generate a book from man pages.
3
4# Copyright (C) 2006 Marc Vertes
5
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2, or (at your option)
9# any later version.
10
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19# 02111-1307, USA.
20
21# release 1.7.1
22
23man() {
24	cat << \EOT
25NAME
26  bookman - Generate a book from man pages
27SYNOPSIS
28  bookman [-pPxn] [-o outfile] [-a author] [-d date] [-r release]
29  [-t title] [-v volume] [-c coverfile] [manfile]
30DESCRIPTION
31  bookman compiles a set of man pages files specified by manfile
32  arguments, or if no manfile is given, filenames are read from standard
33  input.
34OPTIONS
35  -p		PDF output format.
36  -P		Postscript output format.
37  -x		X11 previewing, using gxditview(1).
38  -n		no format, output is direct gtroff intermediate format.
39  -o outfile    Output in file outfile. Default is standard output.
40  -a author	Set the author, on the cover page.
41  -d date	Set the date on the cover page.
42  -r release	Set the book name and release on the cover page.
43  -t title	Set the title on the cover page.
44  -v volume	Specify the name of the volume.
45  -c coverfile	Uses the file coverfile to generate the cover page,
46		i.e. all pages preceding the table of content. coverfile
47		must be in groff_ms(7) format.
48ENVIRONMENT
49  SOURCE_DATE_EPOCH  Unix timestamp that is used for date in header instead
50                     of current date.
51
52EXAMPLE
53  To build a reference manual from section 2 man, do:
54
55    $ cd /usr/share/man/man2
56    $ bookman -p -t 'Unix Reference Manual' * >book.pdf
57
58SEE ALSO
59  man(1), mandoc(7), groff_ms(7), groff(1), troff(1), grops(1),
60  gxditview(1), ps2pdf(1).
61AUTHOR
62  Marc Vertes <mvertes@free.fr>
63EOT
64}
65
66post="grops"
67
68while getopts :a:c:d:mno:pPr:t:v:x opt
69do
70	case $opt in
71	(a) author=$OPTARG;;
72	(c) cover=$OPTARG;;
73	(d) date=$OPTARG;;
74	(m) man; exit;;
75	(n) post=cat;;
76	(o) outfile=$OPTARG;;
77	(p) post='grops | ps2pdf -';;
78	(P) post=grops;;
79	(x) post='gxditview -';;
80	(r) release=$OPTARG;;
81	(t) title=$OPTARG;;
82	(v) volume=$OPTARG;;
83	(*) man; exit;;
84	esac
85done
86shift $(($OPTIND - 1))
87
88# Check for dependencies
89 groff -ms /dev/null || {
90			printf "ERROR: You need install groff.\n" >&2
91			exit 1
92			}
93
94ps2pdf 2>&1 | grep -q Usage || {
95			printf "ERROR: You need install ghostscript.\n" >&2
96			exit 1
97			}
98
99# Compatibility wrapper for BSD/GNU date, for parsing dates
100if date -j >/dev/null 2>&1; then
101  pdate() { date -u -j -f '@%s' "$@"; }
102else
103  pdate() { date -u -d "$@"; }
104fi
105
106if [ -n "$SOURCE_DATE_EPOCH" ]; then
107  date=$(LC_ALL=C pdate "@$SOURCE_DATE_EPOCH" +'%d %B %Y')
108fi
109date=${date:-$(LC_ALL=C date -u +'%d %B %Y')}
110
111[ $1 ] || set -- $(while read REPLY; do echo "$REPLY"; done)
112
113[ $outfile ] && post="$post >$outfile"
114
115{
116	# Compute table of content from postscript output.
117	# Generate output in gtroff intermediate format, so
118	# it can be merged with content.
119	{
120		[ -f "$cover" ] && cat "$cover" || {
121			printf ".af %% i\n.P1\n"
122			printf ".OH ||%s||\n" "$volume"
123			printf ".EH ||%s||\n" "$volume"
124			printf ".OF |%s|%s|%%|\n" "$release" "$date"
125			printf ".EF |%s|%s|%%|\n" "$release" "$date"
126			printf ".TL\n%s\n" "$title"
127			printf ".AU\n%s\n.AB no\n.AE\n" "$author"
128		}
129		for f
130		do
131			case $f in
132			(*.Z|*.gz) zcat $f;;
133			(*.bz2)    bzcat $f;;
134			(*)        cat $f;;
135			esac
136		done | groff -man -rC1 -Tps | gawk '
137		$1 == "%%Page:" {page = $2}
138		/%%EndPageSetup/ {
139			getline l; getline; $0 = l $0
140			# extract first word (disgard everything
141			# outside braces).
142			sub(/^[^\(]*\(/, "")
143			gsub(/\)[^\(]*\(/, "")
144			gsub(/\\214/, "fi")
145			gsub(/\\215/, "fl")
146			sub(/\)[^\(]*/, "")
147			sub(/\\\(.*/, "")
148			if (name != $0) {
149				print (page == 1) ? ".XS 1" : ".XA " page
150				print $0
151			}
152			name = $0
153		}
154		END {print ".XE"; print ".PX"}'
155	} | groff -Z -ms | head --lines=-1
156
157	# Output content, in gtroff intermediate format.
158	for f
159	do
160		case $f in
161		(*.Z|*.gz) zcat $f;;
162		(*.bz2)    bzcat $f;;
163		(*)        cat $f;;
164		esac
165	done | groff -Z -man -rC1 | gawk 'NR >3'
166
167} | eval $post
168