summaryrefslogtreecommitdiff
path: root/dviware/dvi2bitmap/PageRange.cc
blob: 12d8f9b98523b18748a5369a55fea69f8d6afbbb (plain)
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
/* This file is part of dvi2bitmap; see README for copyrights and licence */

#include <config.h>

#include "PageRange.h"
#include <iostream>

#ifdef HAVE_CSTD_INCLUDE
#include <cstdlib>
#include <cctype>
#else
#include <stdlib.h>
#include <ctype.h>
#endif

verbosities PageRange::verbosity_ = normal;

/**
 * Constructs a new PageRange object.  This represents a selection of
 * selected pages.  When created, the <code>PageRange</code> object
 * represents all pages, but this set can be constrained in a variety
 * of ways using the {@link #addSpec} method.  The resulting object
 * can be queried, to ask whether a particular page would be included
 * in the set, using the {@link #isSelected} method.
 */
PageRange::PageRange()
    : useCounts_(true), useCountNo_(0),
      first_(0), last_(0), rangeType_(unset)
{
}

/**
 * Add a constraint to the set of selected pages represented by this
 * PageRange object.  The arguments are a selector, which is one of
 * the characters `l', `p', or `P', and a string representing one or
 * more numbers.  The meanings of the three selectors are as follows.
 *
 * <dl>
 * <dt>'l' pagenum</dt>
 *        <dd>The  last  page  printed will be the first one num-
 *        bered num Default is the last page in the document.
 *        If  the  num is prefixed by an equals sign, then it
 *        (and any argument to the -p option) is treated as a
 *        sequence  number,  rather  than  a value to compare
 *        with <code>\\count0</code> values.  Thus, using -l  =9  will  end
 *        with the ninth page of the document, no matter what
 *        the pages are actually numbered.</dd>
 *
 * <dt>'p' pagenum</dt>
 *        <dd>The  first  page printed will be the first one num-
 *        bered num.  Default is the first page in the  docu-
 *        ment.   If  the  num is prefixed by an equals sign,
 *        then it (and any argument  to  the  -l  option)  is
 *        treated  as  a sequence number, rather than a value
 *        to compare with <code>\\count0</code> values.  Thus, using -p  =3
 *        will  start with the third page of the document, no
 *        matter what the pages are actually numbered.</dd>
 *
 * <dt>'P' pagelist</dt>
 *        <dd>A comma-separated list of pages  and  ranges  (a-b)
 *        may  be given, which will be interpreted as <code>\\count0</code>
 *        values.  Pages not specified will not  be  printed.
 *        Multiple  -pp options may be specified or all pages
 *        and page ranges  can  be  specified  with  one  -pp
 *        option.</dd>
 * </dl>
 *
 * <p>Any of these specifications may be prefixed by either
 * <code>=</code> or <code>:n:</code>
 * In the former case, DVI page numbers are used rather than TeX
 * <code>\\count</code> registers; in the latter case, the program examines the
 * <code>\\countn</code> register rather than the default <code>\\count0</code>
 *
 * <p>Thus, the syntax of pagelist is extended to satisfy:
 * <pre>
 *    pagenum:  prefix* number
 *    pagelist: prefix* page-or-range [',' page-or-range]*
 *    prefix: '=' | ':' number ':'
 *    page-or-range: number | number-number
 * </pre>
 *
 * @param type one of the letters `l', `p', `P'.
 * @param spec a page specification conforming to the grammar above
 */
bool PageRange::addSpec (const char type, const char *spec) 
{
    bool parseOK = true;

    // prefix*
    for (bool readPrefix = true; readPrefix; )
	switch (*spec)
	{
	  case '=':
	    useCounts_ = false;
	    spec++;
	    break;
	  case ':':
	    {
		spec++;
		char *endp;
		int countnum = static_cast<int>(STD::strtol (spec, &endp, 10));
		spec = endp;
		if (*spec != ':')
		{
		    parseOK = false;
		    readPrefix = false;
		}
		else
		{
		    if (countnum < 0 || countnum > 9) parseOK = false;
		    useCountNo_ = countnum;
		    spec++;
		}
		break;
	    }
	  default:
	    readPrefix = false;
	}
    // now have spec pointing at first page-or-range or pagenum

    switch (type)
    {
      case 'p':
	if (rangeType_ == ranges)
	{
	    if (verbosity_ >= normal)
		std::cerr << "Inconsistent range specs\n";
	    parseOK = false;
	}
	else
	{
	    first_ = STD::atoi (spec);
	    rangeType_ = oneRange;
	}
	break;

      case 'P':
	{
	    if (rangeType_ == oneRange)
		parseOK = false;
	    else
		rangeType_ = ranges;
	
	    char *endp;
	    int p1=0, p2;
	    endp = const_cast<char*>(spec);
	    char lastsep = ',';
	    while (*endp != '\0' && parseOK)
	    {
		p2 = static_cast<int>(STD::strtol (spec, &endp, 10));
		if (endp == spec)	// no digits found
		{
		    parseOK = false;
		    continue;
		}

		switch (lastsep)
		{
		  case ',':	// single page or start of range
		    setPages_[p2] = true;
		    p1 = p2;
		    break;
		  case '-':	// end of range
		    if (p1 > p2)
			parseOK = false;
		    else
			for (int i=p1; i<=p2; i++)
			    setPages_[i] = true;
		    break;
		  default:
		    parseOK = false;
		    break;
		}
		lastsep = *endp;
		spec = endp+1;
		// now have endp pointing at a separator or \0, 
		// spec at next char
	    }
	}
	break;

      case 'l':
	if (rangeType_ == ranges)
	{
	    if (verbosity_ >= normal)
		std::cerr << "Inconsistent range specs\n";
	    parseOK = false;
	}
	else
	{
	    last_ = STD::atoi (spec);
	    rangeType_ = oneRange;
	}
	break;

      default:
	parseOK = false;
    }

    return parseOK;
}

/**
 * Returns true if the specified page is to be included.  We test
 * either the page-sequence number or the TeX <code>\\count0-9</code>
 * registers, depending on how the page range was specified.
 *
 * @param pagenum a page-sequence number, counting pages from the
 * beginning of the DVI file
 * @param count is the array of TeX <code>\\count0-9</code> register
 * @return true if the current settings of this PageRange object
 * would indicate that the specified page is to be printed
 */
bool PageRange::isSelected (const int pagenum, const int* count)
{
    bool rval;
    if (rangeType_ == unset)
	return true;

    int testval = (useCounts_ ? count[useCountNo_] : pagenum);
    if (rangeType_ == oneRange)
	rval = testval >= first_ && (last_ == 0 || testval <= last_);
    else
	rval = setPages_[testval];

    return rval;
}