1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2003 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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20 
21 #include "eqn.h"
22 #include "pbox.h"
23 #include "ptable.h"
24 
25 class char_box : public simple_box {
26   unsigned char c;
27   char next_is_italic;
28   char prev_is_italic;
29 public:
30   char_box(unsigned char);
31   void debug_print();
32   void output();
33   int is_char();
34   int left_is_italic();
35   int right_is_italic();
36   void hint(unsigned);
37   void handle_char_type(int, int);
38 };
39 
40 class special_char_box : public simple_box {
41   char *s;
42 public:
43   special_char_box(const char *);
44   ~special_char_box();
45   void output();
46   void debug_print();
47   int is_char();
48   void handle_char_type(int, int);
49 };
50 
51 const char *spacing_type_table[] = {
52   "ordinary",
53   "operator",
54   "binary",
55   "relation",
56   "opening",
57   "closing",
58   "punctuation",
59   "inner",
60   "suppress",
61   0,
62 };
63 
64 const int DIGIT_TYPE = 0;
65 const int LETTER_TYPE = 1;
66 
67 const char *font_type_table[] = {
68   "digit",
69   "letter",
70   0,
71 };
72 
73 struct char_info {
74   int spacing_type;
75   int font_type;
76   char_info();
77 };
78 
char_info()79 char_info::char_info()
80 : spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE)
81 {
82 }
83 
84 static char_info char_table[256];
85 
86 declare_ptable(char_info)
87 implement_ptable(char_info)
88 
89 PTABLE(char_info) special_char_table;
90 
get_special_char_spacing_type(const char * ch)91 static int get_special_char_spacing_type(const char *ch)
92 {
93   char_info *p = special_char_table.lookup(ch);
94   return p ? p->spacing_type : ORDINARY_TYPE;
95 }
96 
get_special_char_font_type(const char * ch)97 static int get_special_char_font_type(const char *ch)
98 {
99   char_info *p = special_char_table.lookup(ch);
100   return p ? p->font_type : DIGIT_TYPE;
101 }
102 
set_special_char_type(const char * ch,int st,int ft)103 static void set_special_char_type(const char *ch, int st, int ft)
104 {
105   char_info *p = special_char_table.lookup(ch);
106   if (!p) {
107     p = new char_info[1];
108     special_char_table.define(ch, p);
109   }
110   if (st >= 0)
111     p->spacing_type = st;
112   if (ft >= 0)
113     p->font_type = ft;
114 }
115 
init_char_table()116 void init_char_table()
117 {
118   set_special_char_type("pl", 2, -1); // binary
119   set_special_char_type("mi", 2, -1);
120   set_special_char_type("eq", 3, -1); // relation
121   set_special_char_type("<=", 3, -1);
122   set_special_char_type(">=", 3, -1);
123   char_table['}'].spacing_type = 5; // closing
124   char_table[')'].spacing_type = 5;
125   char_table[']'].spacing_type = 5;
126   char_table['{'].spacing_type = 4; // opening
127   char_table['('].spacing_type = 4;
128   char_table['['].spacing_type = 4;
129   char_table[','].spacing_type = 6; // punctuation
130   char_table[';'].spacing_type = 6;
131   char_table[':'].spacing_type = 6;
132   char_table['.'].spacing_type = 6;
133   char_table['>'].spacing_type = 3;
134   char_table['<'].spacing_type = 3;
135   char_table['*'].spacing_type = 2; // binary
136   for (int i = 0; i < 256; i++)
137     if (csalpha(i))
138       char_table[i].font_type = LETTER_TYPE;
139 }
140 
lookup_spacing_type(const char * type)141 static int lookup_spacing_type(const char *type)
142 {
143   for (int i = 0; spacing_type_table[i] != 0; i++)
144     if (strcmp(spacing_type_table[i], type) == 0)
145       return i;
146   return -1;
147 }
148 
lookup_font_type(const char * type)149 static int lookup_font_type(const char *type)
150 {
151   for (int i = 0; font_type_table[i] != 0; i++)
152     if (strcmp(font_type_table[i], type) == 0)
153       return i;
154   return -1;
155 }
156 
set_spacing_type(char * type)157 void box::set_spacing_type(char *type)
158 {
159   int t = lookup_spacing_type(type);
160   if (t < 0)
161     error("unrecognised type `%1'", type);
162   else
163     spacing_type = t;
164   a_delete type;
165 }
166 
char_box(unsigned char cc)167 char_box::char_box(unsigned char cc)
168 : c(cc), next_is_italic(0), prev_is_italic(0)
169 {
170   spacing_type = char_table[c].spacing_type;
171 }
172 
hint(unsigned flags)173 void char_box::hint(unsigned flags)
174 {
175   if (flags & HINT_PREV_IS_ITALIC)
176     prev_is_italic = 1;
177   if (flags & HINT_NEXT_IS_ITALIC)
178     next_is_italic = 1;
179 }
180 
output()181 void char_box::output()
182 {
183   int font_type = char_table[c].font_type;
184   if (font_type != LETTER_TYPE)
185     printf("\\f[%s]", current_roman_font);
186   if (!prev_is_italic)
187     fputs("\\,", stdout);
188   if (c == '\\')
189     fputs("\\e", stdout);
190   else
191     putchar(c);
192   if (!next_is_italic)
193     fputs("\\/", stdout);
194   else
195     fputs("\\&", stdout);		// suppress ligaturing and kerning
196   if (font_type != LETTER_TYPE)
197     fputs("\\fP", stdout);
198 }
199 
left_is_italic()200 int char_box::left_is_italic()
201 {
202   int font_type = char_table[c].font_type;
203   return font_type == LETTER_TYPE;
204 }
205 
right_is_italic()206 int char_box::right_is_italic()
207 {
208   int font_type = char_table[c].font_type;
209   return font_type == LETTER_TYPE;
210 }
211 
is_char()212 int char_box::is_char()
213 {
214   return 1;
215 }
216 
debug_print()217 void char_box::debug_print()
218 {
219   if (c == '\\') {
220     putc('\\', stderr);
221     putc('\\', stderr);
222   }
223   else
224     putc(c, stderr);
225 }
226 
special_char_box(const char * t)227 special_char_box::special_char_box(const char *t)
228 {
229   s = strsave(t);
230   spacing_type = get_special_char_spacing_type(s);
231 }
232 
~special_char_box()233 special_char_box::~special_char_box()
234 {
235   a_delete s;
236 }
237 
output()238 void special_char_box::output()
239 {
240   int font_type = get_special_char_font_type(s);
241   if (font_type != LETTER_TYPE)
242     printf("\\f[%s]", current_roman_font);
243   printf("\\,\\[%s]\\/", s);
244   if (font_type != LETTER_TYPE)
245     printf("\\fP");
246 }
247 
is_char()248 int special_char_box::is_char()
249 {
250   return 1;
251 }
252 
debug_print()253 void special_char_box::debug_print()
254 {
255   fprintf(stderr, "\\[%s]", s);
256 }
257 
258 
handle_char_type(int st,int ft)259 void char_box::handle_char_type(int st, int ft)
260 {
261   if (st >= 0)
262     char_table[c].spacing_type = st;
263   if (ft >= 0)
264     char_table[c].font_type = ft;
265 }
266 
handle_char_type(int st,int ft)267 void special_char_box::handle_char_type(int st, int ft)
268 {
269   set_special_char_type(s, st, ft);
270 }
271 
set_char_type(const char * type,char * ch)272 void set_char_type(const char *type, char *ch)
273 {
274   assert(ch != 0);
275   int st = lookup_spacing_type(type);
276   int ft = lookup_font_type(type);
277   if (st < 0 && ft < 0) {
278     error("bad character type `%1'", type);
279     a_delete ch;
280     return;
281   }
282   box *b = split_text(ch);
283   b->handle_char_type(st, ft);
284   delete b;
285 }
286 
287 /* We give primes special treatment so that in ``x' sub 2'', the ``2''
288 will be tucked under the prime */
289 
290 class prime_box : public pointer_box {
291   box *pb;
292 public:
293   prime_box(box *);
294   ~prime_box();
295   int compute_metrics(int style);
296   void output();
297   void compute_subscript_kern();
298   void debug_print();
299   void handle_char_type(int, int);
300 };
301 
make_prime_box(box * pp)302 box *make_prime_box(box *pp)
303 {
304   return new prime_box(pp);
305 }
306 
prime_box(box * pp)307 prime_box::prime_box(box *pp) : pointer_box(pp)
308 {
309   pb = new special_char_box("fm");
310 }
311 
~prime_box()312 prime_box::~prime_box()
313 {
314   delete pb;
315 }
316 
compute_metrics(int style)317 int prime_box::compute_metrics(int style)
318 {
319   int res = p->compute_metrics(style);
320   pb->compute_metrics(style);
321   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]"
322 	 "+\\n[" WIDTH_FORMAT "]\n",
323 	 uid, p->uid, pb->uid);
324   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
325 	 ">?\\n[" HEIGHT_FORMAT "]\n",
326 	 uid, p->uid, pb->uid);
327   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
328 	 ">?\\n[" DEPTH_FORMAT "]\n",
329 	 uid, p->uid, pb->uid);
330   return res;
331 }
332 
compute_subscript_kern()333 void prime_box::compute_subscript_kern()
334 {
335   p->compute_subscript_kern();
336   printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]"
337 	 "+\\n[" SUB_KERN_FORMAT "]>?0\n",
338 	 uid, pb->uid, p->uid);
339 }
340 
output()341 void prime_box::output()
342 {
343   p->output();
344   pb->output();
345 }
346 
handle_char_type(int st,int ft)347 void prime_box::handle_char_type(int st, int ft)
348 {
349   p->handle_char_type(st, ft);
350   pb->handle_char_type(st, ft);
351 }
352 
debug_print()353 void prime_box::debug_print()
354 {
355   p->debug_print();
356   putc('\'', stderr);
357 }
358 
split_text(char * text)359 box *split_text(char *text)
360 {
361   list_box *lb = 0;
362   box *fb = 0;
363   char *s = text;
364   while (*s != '\0') {
365     char c = *s++;
366     box *b = 0;
367     switch (c) {
368     case '+':
369       b = new special_char_box("pl");
370       break;
371     case '-':
372       b = new special_char_box("mi");
373       break;
374     case '=':
375       b = new special_char_box("eq");
376       break;
377     case '\'':
378       b = new special_char_box("fm");
379       break;
380     case '<':
381       if (*s == '=') {
382 	b = new special_char_box("<=");
383 	s++;
384 	break;
385       }
386       goto normal_char;
387     case '>':
388       if (*s == '=') {
389 	b = new special_char_box(">=");
390 	s++;
391 	break;
392       }
393       goto normal_char;
394     case '\\':
395       if (*s == '\0') {
396 	lex_error("bad escape");
397 	break;
398       }
399       c = *s++;
400       switch (c) {
401       case '(':
402 	{
403 	  char buf[3];
404 	  if (*s != '\0') {
405 	    buf[0] = *s++;
406 	    if (*s != '\0') {
407 	      buf[1] = *s++;
408 	      buf[2] = '\0';
409 	      b = new special_char_box(buf);
410 	    }
411 	    else {
412 	      lex_error("bad escape");
413 	    }
414 	  }
415 	  else {
416 	    lex_error("bad escape");
417 	  }
418 	}
419 	break;
420       case '[':
421 	{
422 	  char *ch = s;
423 	  while (*s != ']' && *s != '\0')
424 	    s++;
425 	  if (*s == '\0')
426 	    lex_error("bad escape");
427 	  else {
428 	    *s++ = '\0';
429 	    b = new special_char_box(ch);
430 	  }
431 	}
432 	break;
433       case 'f':
434       case 'g':
435       case 'k':
436       case 'n':
437       case '*':
438 	{
439 	  char *escape_start = s - 2;
440 	  switch (*s) {
441 	  case '(':
442 	    if (*++s != '\0')
443 	      ++s;
444 	    break;
445 	  case '[':
446 	    for (++s; *s != '\0' && *s != ']'; s++)
447 	      ;
448 	    break;
449 	  }
450 	  if (*s == '\0')
451 	    lex_error("bad escape");
452 	  else {
453 	    ++s;
454 	    char *buf = new char[s - escape_start + 1];
455 	    memcpy(buf, escape_start, s - escape_start);
456 	    buf[s - escape_start] = '\0';
457 	    b = new quoted_text_box(buf);
458 	  }
459 	}
460 	break;
461       case '-':
462       case '_':
463 	{
464 	  char buf[2];
465 	  buf[0] = c;
466 	  buf[1] = '\0';
467 	  b = new special_char_box(buf);
468 	}
469 	break;
470       case '`':
471 	b = new special_char_box("ga");
472 	break;
473       case '\'':
474 	b = new special_char_box("aa");
475 	break;
476       case 'e':
477       case '\\':
478 	b = new char_box('\\');
479 	break;
480       case '^':
481       case '|':
482       case '0':
483 	{
484 	  char buf[3];
485 	  buf[0] = '\\';
486 	  buf[1] = c;
487 	  buf[2] = '\0';
488 	  b = new quoted_text_box(strsave(buf));
489 	  break;
490 	}
491       default:
492 	lex_error("unquoted escape");
493 	b = new quoted_text_box(strsave(s - 2));
494 	s = strchr(s, '\0');
495 	break;
496       }
497       break;
498     default:
499     normal_char:
500       b = new char_box(c);
501       break;
502     }
503     while (*s == '\'') {
504       if (b == 0)
505 	b = new quoted_text_box(0);
506       b = new prime_box(b);
507       s++;
508     }
509     if (b != 0) {
510       if (lb != 0)
511 	lb->append(b);
512       else if (fb != 0) {
513 	lb = new list_box(fb);
514 	lb->append(b);
515       }
516       else
517 	fb = b;
518     }
519   }
520   a_delete text;
521   if (lb != 0)
522     return lb;
523   else if (fb != 0)
524     return fb;
525   else
526     return new quoted_text_box(0);
527 }
528 
529