1#! /bin/bash
2#
3# original from:
4# @(#) pages.sh 1.0 92/09/26
5# 92/09/05 John H. DuBois III (jhdiii@armory.com)
6# 92/09/26 Added help
7#
8# conversion to bash v2 syntax by Chet Ramey
9
10Usage="$0 [-h] [-n lines/page] page-ranges [file ...]"
11
12usage()
13{
14	echo "$Usage" 1>&2
15}
16
17phelp()
18{
19echo "$0: print selected pages.
20Usage: $Usage
21
22If no file names are given, the standard input is read.
23
24The input is grouped into pages and a selected subset of them is printed.
25Formfeeds are acted on correctly.
26
27If the output device does automatic line wrap, lines that longer than
28the width of the output device will result in incorrect output.
29The first non-option argument is a list of pages to print.
30
31Pages are given as a list of ranges separated by commas.
32A range is either one number, two numbers separted by a dash,
33or one number followed by a dash.  A range consisting of one
34number followed by a dash extends to the end of the document.
35
36Options: 
37-n sets the number of lines per page to n.  The default is 66."
38}
39
40while getopts "n:h" opt; do
41	case "$opt" in
42	n)	LinesPerPage=$OPTARG;;
43	h)	phelp; exit 0;;
44	*)	usage; exit 2;;
45	esac
46done
47
48shift $(($OPTIND - 1))
49
50if [ $# -eq 0 ]; then
51    echo $0: no page ranges given. 1>&2
52    usage
53    exit 1
54fi
55
56PageList=$1
57shift
58
59gawk "
60BEGIN {
61    PageList = \"$PageList\"; LinesPerPage = \"$LinesPerPage\""'
62    if (LinesPerPage == "")
63	LinesPerPage = 66
64    else
65	if (LinesPerPage !~ "[1-9][0-9]*")
66	    ErrExit("Bad value for lines per page: " LinesPerPage)
67    LinesPerPage += 0
68    NumRanges = split(PageList,Ranges,",")
69    for (i = 1; i <= NumRanges; i++) {
70	if ((StartRange = EndRange = Ranges[i]) !~ "^[0-9]+(-([0-9]+)?)?$")
71	    ErrExit("Bad range \"" StartRange "\"")
72	sub("-.*","",StartRange)
73	sub(".*-","",EndRange)
74	if (EndRange == "")
75	    EndRange = 2 ^ 30
76	# Force StartRange and EndRange to be numeric values
77	if ((StartRange += 0) == 0 || (EndRange += 0) == 0)
78	    ErrExit("Invalid page number \"0\" in range " Ranges[i])
79	if (StartRange > EndRange)
80	    ErrExit("Start page comes after end page in range " Ranges[i])
81	TmpRangeStarts[i] = StartRange
82	TmpRangeEnds[i] = EndRange
83    }
84
85    # Sort ranges
86    qsort(TmpRangeStarts,k)
87    RangeEnds[0] = 0
88    for (i = 1; i <= NumRanges; i++) {
89	RangeEnds[i] = TmpRangeEnds[k[i]]
90	if ((RangeStarts[i] = TmpRangeStarts[k[i]]) <= RangeEnds[i - 1])
91	    ErrExit("Overlapping ranges: " Ranges[k[i]] "," Ranges[k[i - 1]])
92    }
93
94    RangeNum = LineNum = PageNum = 1
95    InRange = In(PageNum,RangeStarts[RangeNum],RangeEnds[RangeNum])
96    FS = "\014"
97}
98
99{
100    if (LineNum > LinesPerPage)
101	NewPage()
102    if (InRange)
103	printf "%s",$1
104    # Deal with formfeeds
105    for (i = 2; i <= NF; i++) {
106	if (InRange)
107	    printf "\014"
108	NewPage()
109	if (InRange)
110	    printf "%s",$i
111    }
112    if (InRange)
113	print ""
114    LineNum++
115}
116
117function NewPage() {
118    PageNum++
119    LineNum = 1
120    # At the start of each page, check whether we are in a print range
121    WereInRange = InRange
122    InRange = In(PageNum,RangeStarts[RangeNum],RangeEnds[RangeNum])
123    # If last page was in range and we no longer are, move to next range
124    if (WereInRange && !InRange && ++RangeNum > NumRanges)
125	exit
126}
127
128function In(a,Min,Max) {
129    return (Min <= a && a <= Max)
130}
131
132function ErrExit(S) {
133    print S > "/dev/stderr"
134    Err = 1
135    exit 1
136}
137
138# Arr is an array of values with arbitrary indices.
139# Array k is returned with numeric indices 1..n.
140# The values in k are the indices of array arr, 
141# ordered so that if array arr is stepped through
142# in the order arr[k[1]] .. arr[k[n]], it will be stepped
143# through in order of the values of its elements.
144# The return value is the number of elements in the array (n).
145function qsort(arr,k,  ArrInd,end) {
146    end = 0
147    for (ArrInd in arr)
148	k[++end] = ArrInd;
149    qsortseg(arr,k,1,end);
150    return end
151}
152
153function qsortseg(arr,k,start,end,  left,right,sepval,tmp,tmpe,tmps) {
154    # handle two-element case explicitely for a tiny speedup
155    if ((end - start) == 1) {
156	if (arr[tmps = k[start]] > arr[tmpe = k[end]]) {
157	    k[start] = tmpe
158	    k[end] = tmps
159	}
160	return
161    }
162    left = start;
163    right = end;
164    sepval = arr[k[int((left + right) / 2)]]
165    # Make every element <= sepval be to the left of every element > sepval
166    while (left < right) {
167	while (arr[k[left]] < sepval)
168	    left++
169	while (arr[k[right]] > sepval)
170	    right--
171	if (left < right) {
172	    tmp = k[left]
173	    k[left++] = k[right]
174	    k[right--] = tmp 
175	}
176    }
177    if (left == right)
178	if (arr[k[left]] < sepval)
179	    left++
180	else
181	    right--
182    if (start < right)
183	qsortseg(arr,k,start,right)
184    if (left < end)
185	qsortseg(arr,k,left,end)
186}
187' "$@"
188