xref: /386bsd/usr/src/usr.bin/groff/libdriver/input.cc (revision a2142627)
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