1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 #include "driver.h"
22 #include "device.h"
23
24 const char *current_filename;
25 int current_lineno;
26 const char *device = 0;
27 FILE *current_file;
28
29 int get_integer(); // don't read the newline
30 int possibly_get_integer(int *);
31 char *get_string(int is_long = 0);
32 void skip_line();
33
34 struct environment_list {
35 environment env;
36 environment_list *next;
37
38 environment_list(const environment &, environment_list *);
39 };
40
environment_list(const environment & e,environment_list * p)41 environment_list::environment_list(const environment &e, environment_list *p)
42 : env(e), next(p)
43 {
44 }
45
get_char()46 inline int get_char()
47 {
48 return getc(current_file);
49 }
50
do_file(const char * filename)51 void do_file(const char *filename)
52 {
53 int npages = 0;
54 if (filename[0] == '-' && filename[1] == '\0') {
55 current_filename = "<standard input>";
56 current_file = stdin;
57 }
58 else {
59 errno = 0;
60 current_file = fopen(filename, "r");
61 if (current_file == 0) {
62 error("can't open `%1'", filename);
63 return;
64 }
65 current_filename = filename;
66 }
67 environment env;
68 env.vpos = -1;
69 env.hpos = -1;
70 env.fontno = -1;
71 env.height = 0;
72 env.slant = 0;
73 environment_list *env_list = 0;
74 current_lineno = 1;
75 int command;
76 char *s;
77 command = get_char();
78 if (command == EOF)
79 return;
80 if (command != 'x')
81 fatal("the first command must be `x T'");
82 s = get_string();
83 if (s[0] != 'T')
84 fatal("the first command must be `x T'");
85 char *dev = get_string();
86 if (pr == 0) {
87 device = strsave(dev);
88 if (!font::load_desc())
89 fatal("sorry, I can't continue");
90 }
91 else {
92 if (device == 0 || strcmp(device, dev) != 0)
93 fatal("all files must use the same device");
94 }
95 skip_line();
96 env.size = 10*font::sizescale;
97 command = get_char();
98 if (command != 'x')
99 fatal("the second command must be `x res'");
100 s = get_string();
101 if (s[0] != 'r')
102 fatal("the second command must be `x res'");
103 int n = get_integer();
104 if (n != font::res)
105 fatal("resolution does not match");
106 n = get_integer();
107 if (n != font::hor)
108 fatal("horizontal resolution does not match");
109 n = get_integer();
110 if (n != font::vert)
111 fatal("vertical resolution does not match");
112 skip_line();
113 command = get_char();
114 if (command != 'x')
115 fatal("the third command must be `x init'");
116 s = get_string();
117 if (s[0] != 'i')
118 fatal("the third command must be `x init'");
119 skip_line();
120 if (pr == 0)
121 pr = make_printer();
122 while ((command = get_char()) != EOF) {
123 switch (command) {
124 case 's':
125 env.size = get_integer();
126 if (env.height == env.size)
127 env.height = 0;
128 break;
129 case 'f':
130 env.fontno = get_integer();
131 break;
132 case 'C':
133 {
134 if (npages == 0)
135 fatal("`C' command illegal before first `p' command");
136 char *s = get_string();
137 pr->set_special_char(s, &env);
138 }
139 break;
140 case 'N':
141 {
142 if (npages == 0)
143 fatal("`N' command illegal before first `p' command");
144 pr->set_numbered_char(get_integer(), &env);
145 }
146 break;
147 case 'H':
148 env.hpos = get_integer();
149 break;
150 case 'h':
151 env.hpos += get_integer();
152 break;
153 case 'V':
154 env.vpos = get_integer();
155 break;
156 case 'v':
157 env.vpos += get_integer();
158 break;
159 case '0':
160 case '1':
161 case '2':
162 case '3':
163 case '4':
164 case '5':
165 case '6':
166 case '7':
167 case '8':
168 case '9':
169 {
170 int c = get_char();
171 if (!isascii(c) || !isdigit(c))
172 fatal("digit expected");
173 env.hpos += (command - '0')*10 + (c - '0');
174 }
175 // fall through
176 case 'c':
177 {
178 if (npages == 0)
179 fatal("`c' command illegal before first `p' command");
180 int c = get_char();
181 if (c == EOF)
182 fatal("missing argument to `c' command");
183 pr->set_ascii_char(c, &env);
184 }
185 break;
186 case 'n':
187 if (npages == 0)
188 fatal("`n' command illegal before first `p' command");
189 pr->end_of_line();
190 (void)get_integer();
191 (void)get_integer();
192 break;
193 case 'w':
194 case ' ':
195 break;
196 case '\n':
197 current_lineno++;
198 break;
199 case 'p':
200 if (npages)
201 pr->end_page(env.vpos);
202 npages++;
203 pr->begin_page(get_integer());
204 env.vpos = 0;
205 break;
206 case '{':
207 env_list = new environment_list(env, env_list);
208 break;
209 case '}':
210 if (!env_list) {
211 fatal("can't pop");
212 }
213 else {
214 env = env_list->env;
215 environment_list *tem = env_list;
216 env_list = env_list->next;
217 delete tem;
218 }
219 break;
220 case 'u':
221 {
222 if (npages == 0)
223 fatal("`u' command illegal before first `p' command");
224 int kern = get_integer();
225 int c = get_char();
226 while (c == ' ')
227 c = get_char();
228 while (c != EOF) {
229 if (c == '\n') {
230 current_lineno++;
231 break;
232 }
233 int w;
234 pr->set_ascii_char(c, &env, &w);
235 env.hpos += w + kern;
236 c = get_char();
237 if (c == ' ')
238 break;
239 }
240 }
241 break;
242 case 't':
243 {
244 if (npages == 0)
245 fatal("`t' command illegal before first `p' command");
246 int c;
247 while ((c = get_char()) != EOF && c != ' ') {
248 if (c == '\n') {
249 current_lineno++;
250 break;
251 }
252 int w;
253 pr->set_ascii_char(c, &env, &w);
254 env.hpos += w;
255 }
256 }
257 break;
258 case '#':
259 skip_line();
260 break;
261 case 'D':
262 {
263 if (npages == 0)
264 fatal("`D' command illegal before first `p' command");
265 int c;
266 while ((c = get_char()) == ' ')
267 ;
268 int n;
269 int *p = 0;
270 int szp = 0;
271 for (int np = 0; possibly_get_integer(&n); np++) {
272 if (np >= szp) {
273 if (szp == 0) {
274 szp = 16;
275 p = new int[szp];
276 }
277 else {
278 int *oldp = p;
279 p = new int[szp*2];
280 memcpy(p, oldp, szp*sizeof(int));
281 szp *= 2;
282 a_delete oldp;
283 }
284 }
285 p[np] = n;
286 }
287 pr->draw(c, p, np, &env);
288 if (c == 'e') {
289 if (np > 0)
290 env.hpos += p[0];
291 }
292 else {
293 for (int i = 0; i < np/2; i++) {
294 env.hpos += p[i*2];
295 env.vpos += p[i*2 + 1];
296 }
297 // there might be an odd number of characters
298 if (i*2 < np)
299 env.hpos += p[i*2];
300 }
301 a_delete p;
302 skip_line();
303 }
304 break;
305 case 'x':
306 {
307 char *s = get_string();
308 int suppress_skip = 0;
309 switch (s[0]) {
310 case 'i':
311 error("duplicate `x init' command");
312 break;
313 case 'T':
314 error("duplicate `x T' command");
315 break;
316 case 'r':
317 error("duplicate `x res' command");
318 break;
319 case 'p':
320 break;
321 case 's':
322 break;
323 case 't':
324 break;
325 case 'f':
326 {
327 int n = get_integer();
328 char *name = get_string();
329 pr->load_font(n, name);
330 }
331 break;
332 case 'H':
333 env.height = get_integer();
334 if (env.height == env.size)
335 env.height = 0;
336 break;
337 case 'S':
338 env.slant = get_integer();
339 break;
340 case 'X':
341 if (npages == 0)
342 fatal("`x X' command illegal before first `p' command");
343 pr->special(get_string(1), &env);
344 suppress_skip = 1;
345 break;
346 default:
347 error("unrecognised x command `%1'", s);
348 }
349 if (!suppress_skip)
350 skip_line();
351 }
352 break;
353 default:
354 error("unrecognised command code %1", int(command));
355 skip_line();
356 break;
357 }
358 }
359 if (npages)
360 pr->end_page(env.vpos);
361 }
362
get_integer()363 int get_integer()
364 {
365 int c = get_char();
366 while (c == ' ')
367 c = get_char();
368 int neg = 0;
369 if (c == '-') {
370 neg = 1;
371 c = get_char();
372 }
373 if (!isascii(c) || !isdigit(c))
374 fatal("integer expected");
375 int total = 0;
376 do {
377 total = total*10;
378 if (neg)
379 total -= c - '0';
380 else
381 total += c - '0';
382 c = get_char();
383 } while (isascii(c) && isdigit(c));
384 if (c != EOF)
385 ungetc(c, current_file);
386 return total;
387 }
388
possibly_get_integer(int * res)389 int possibly_get_integer(int *res)
390 {
391 int c = get_char();
392 while (c == ' ')
393 c = get_char();
394 int neg = 0;
395 if (c == '-') {
396 neg = 1;
397 c = get_char();
398 }
399 if (!isascii(c) || !isdigit(c)) {
400 if (c != EOF)
401 ungetc(c, current_file);
402 return 0;
403 }
404 int total = 0;
405 do {
406 total = total*10;
407 if (neg)
408 total -= c - '0';
409 else
410 total += c - '0';
411 c = get_char();
412 } while (isascii(c) && isdigit(c));
413 if (c != EOF)
414 ungetc(c, current_file);
415 *res = total;
416 return 1;
417 }
418
419
get_string(int is_long)420 char *get_string(int is_long)
421 {
422 static char *buf;
423 static int buf_size;
424 int c = get_char();
425 while (c == ' ')
426 c = get_char();
427 for (int i = 0;; i++) {
428 if (i >= buf_size) {
429 if (buf_size == 0) {
430 buf_size = 16;
431 buf = new char[buf_size];
432 }
433 else {
434 char *old_buf = buf;
435 int old_size = buf_size;
436 buf_size *= 2;
437 buf = new char[buf_size];
438 memcpy(buf, old_buf, old_size);
439 a_delete old_buf;
440 }
441 }
442 if ((!is_long && (c == ' ' || c == '\n')) || c == EOF) {
443 buf[i] = '\0';
444 break;
445 }
446 if (is_long && c == '\n') {
447 current_lineno++;
448 c = get_char();
449 if (c == '+')
450 c = '\n';
451 else {
452 buf[i] = '\0';
453 break;
454 }
455 }
456 buf[i] = c;
457 c = get_char();
458 }
459 if (c != EOF)
460 ungetc(c, current_file);
461 return buf;
462 }
463
skip_line()464 void skip_line()
465 {
466 int c;
467 while ((c = get_char()) != EOF)
468 if (c == '\n') {
469 current_lineno++;
470 break;
471 }
472 }
473
474