1114402Sru// -*- C++ -*-
2114402Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc.
3114402Sru     Written by James Clark (jjc@jclark.com)
4114402Sru
5114402SruThis file is part of groff.
6114402Sru
7114402Srugroff is free software; you can redistribute it and/or modify it under
8114402Sruthe terms of the GNU General Public License as published by the Free
9114402SruSoftware Foundation; either version 2, or (at your option) any later
10114402Sruversion.
11114402Sru
12114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
13114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
14114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15114402Srufor more details.
16114402Sru
17114402SruYou should have received a copy of the GNU General Public License along
18114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
19151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20114402Sru
21114402Sru#include "eqn.h"
22114402Sru#include "pbox.h"
23114402Sru#include "ptable.h"
24114402Sru
25114402Sruclass char_box : public simple_box {
26114402Sru  unsigned char c;
27114402Sru  char next_is_italic;
28114402Sru  char prev_is_italic;
29114402Srupublic:
30114402Sru  char_box(unsigned char);
31114402Sru  void debug_print();
32114402Sru  void output();
33114402Sru  int is_char();
34114402Sru  int left_is_italic();
35114402Sru  int right_is_italic();
36114402Sru  void hint(unsigned);
37114402Sru  void handle_char_type(int, int);
38114402Sru};
39114402Sru
40114402Sruclass special_char_box : public simple_box {
41114402Sru  char *s;
42114402Srupublic:
43114402Sru  special_char_box(const char *);
44114402Sru  ~special_char_box();
45114402Sru  void output();
46114402Sru  void debug_print();
47114402Sru  int is_char();
48114402Sru  void handle_char_type(int, int);
49114402Sru};
50114402Sru
51114402Sruconst char *spacing_type_table[] = {
52114402Sru  "ordinary",
53114402Sru  "operator",
54114402Sru  "binary",
55114402Sru  "relation",
56114402Sru  "opening",
57114402Sru  "closing",
58114402Sru  "punctuation",
59114402Sru  "inner",
60114402Sru  "suppress",
61114402Sru  0,
62114402Sru};
63114402Sru
64114402Sruconst int DIGIT_TYPE = 0;
65114402Sruconst int LETTER_TYPE = 1;
66114402Sru
67114402Sruconst char *font_type_table[] = {
68114402Sru  "digit",
69114402Sru  "letter",
70114402Sru  0,
71114402Sru};
72114402Sru
73114402Srustruct char_info {
74114402Sru  int spacing_type;
75114402Sru  int font_type;
76114402Sru  char_info();
77114402Sru};
78114402Sru
79114402Sruchar_info::char_info()
80114402Sru: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE)
81114402Sru{
82114402Sru}
83114402Sru
84114402Srustatic char_info char_table[256];
85114402Sru
86114402Srudeclare_ptable(char_info)
87114402Sruimplement_ptable(char_info)
88114402Sru
89114402SruPTABLE(char_info) special_char_table;
90114402Sru
91114402Srustatic int get_special_char_spacing_type(const char *ch)
92114402Sru{
93114402Sru  char_info *p = special_char_table.lookup(ch);
94114402Sru  return p ? p->spacing_type : ORDINARY_TYPE;
95114402Sru}
96114402Sru
97114402Srustatic int get_special_char_font_type(const char *ch)
98114402Sru{
99114402Sru  char_info *p = special_char_table.lookup(ch);
100114402Sru  return p ? p->font_type : DIGIT_TYPE;
101114402Sru}
102114402Sru
103114402Srustatic void set_special_char_type(const char *ch, int st, int ft)
104114402Sru{
105114402Sru  char_info *p = special_char_table.lookup(ch);
106114402Sru  if (!p) {
107114402Sru    p = new char_info[1];
108114402Sru    special_char_table.define(ch, p);
109114402Sru  }
110114402Sru  if (st >= 0)
111114402Sru    p->spacing_type = st;
112114402Sru  if (ft >= 0)
113114402Sru    p->font_type = ft;
114114402Sru}
115114402Sru
116114402Sruvoid init_char_table()
117114402Sru{
118114402Sru  set_special_char_type("pl", 2, -1); // binary
119114402Sru  set_special_char_type("mi", 2, -1);
120114402Sru  set_special_char_type("eq", 3, -1); // relation
121114402Sru  set_special_char_type("<=", 3, -1);
122114402Sru  set_special_char_type(">=", 3, -1);
123114402Sru  char_table['}'].spacing_type = 5; // closing
124114402Sru  char_table[')'].spacing_type = 5;
125114402Sru  char_table[']'].spacing_type = 5;
126114402Sru  char_table['{'].spacing_type = 4; // opening
127114402Sru  char_table['('].spacing_type = 4;
128114402Sru  char_table['['].spacing_type = 4;
129114402Sru  char_table[','].spacing_type = 6; // punctuation
130114402Sru  char_table[';'].spacing_type = 6;
131114402Sru  char_table[':'].spacing_type = 6;
132114402Sru  char_table['.'].spacing_type = 6;
133114402Sru  char_table['>'].spacing_type = 3;
134114402Sru  char_table['<'].spacing_type = 3;
135114402Sru  char_table['*'].spacing_type = 2; // binary
136114402Sru  for (int i = 0; i < 256; i++)
137114402Sru    if (csalpha(i))
138114402Sru      char_table[i].font_type = LETTER_TYPE;
139114402Sru}
140114402Sru
141114402Srustatic int lookup_spacing_type(const char *type)
142114402Sru{
143114402Sru  for (int i = 0; spacing_type_table[i] != 0; i++)
144114402Sru    if (strcmp(spacing_type_table[i], type) == 0)
145114402Sru      return i;
146114402Sru  return -1;
147114402Sru}
148114402Sru
149114402Srustatic int lookup_font_type(const char *type)
150114402Sru{
151114402Sru  for (int i = 0; font_type_table[i] != 0; i++)
152114402Sru    if (strcmp(font_type_table[i], type) == 0)
153114402Sru      return i;
154114402Sru  return -1;
155114402Sru}
156114402Sru
157114402Sruvoid box::set_spacing_type(char *type)
158114402Sru{
159114402Sru  int t = lookup_spacing_type(type);
160114402Sru  if (t < 0)
161114402Sru    error("unrecognised type `%1'", type);
162114402Sru  else
163114402Sru    spacing_type = t;
164114402Sru  a_delete type;
165114402Sru}
166114402Sru
167114402Sruchar_box::char_box(unsigned char cc)
168114402Sru: c(cc), next_is_italic(0), prev_is_italic(0)
169114402Sru{
170114402Sru  spacing_type = char_table[c].spacing_type;
171114402Sru}
172114402Sru
173114402Sruvoid char_box::hint(unsigned flags)
174114402Sru{
175114402Sru  if (flags & HINT_PREV_IS_ITALIC)
176114402Sru    prev_is_italic = 1;
177114402Sru  if (flags & HINT_NEXT_IS_ITALIC)
178114402Sru    next_is_italic = 1;
179114402Sru}
180114402Sru
181114402Sruvoid char_box::output()
182114402Sru{
183114402Sru  int font_type = char_table[c].font_type;
184114402Sru  if (font_type != LETTER_TYPE)
185114402Sru    printf("\\f[%s]", current_roman_font);
186114402Sru  if (!prev_is_italic)
187114402Sru    fputs("\\,", stdout);
188114402Sru  if (c == '\\')
189114402Sru    fputs("\\e", stdout);
190114402Sru  else
191114402Sru    putchar(c);
192114402Sru  if (!next_is_italic)
193114402Sru    fputs("\\/", stdout);
194114402Sru  else
195114402Sru    fputs("\\&", stdout);		// suppress ligaturing and kerning
196114402Sru  if (font_type != LETTER_TYPE)
197114402Sru    fputs("\\fP", stdout);
198114402Sru}
199114402Sru
200114402Sruint char_box::left_is_italic()
201114402Sru{
202114402Sru  int font_type = char_table[c].font_type;
203114402Sru  return font_type == LETTER_TYPE;
204114402Sru}
205114402Sru
206114402Sruint char_box::right_is_italic()
207114402Sru{
208114402Sru  int font_type = char_table[c].font_type;
209114402Sru  return font_type == LETTER_TYPE;
210114402Sru}
211114402Sru
212114402Sruint char_box::is_char()
213114402Sru{
214114402Sru  return 1;
215114402Sru}
216114402Sru
217114402Sruvoid char_box::debug_print()
218114402Sru{
219114402Sru  if (c == '\\') {
220114402Sru    putc('\\', stderr);
221114402Sru    putc('\\', stderr);
222114402Sru  }
223114402Sru  else
224114402Sru    putc(c, stderr);
225114402Sru}
226114402Sru
227114402Sruspecial_char_box::special_char_box(const char *t)
228114402Sru{
229114402Sru  s = strsave(t);
230114402Sru  spacing_type = get_special_char_spacing_type(s);
231114402Sru}
232114402Sru
233114402Sruspecial_char_box::~special_char_box()
234114402Sru{
235114402Sru  a_delete s;
236114402Sru}
237114402Sru
238114402Sruvoid special_char_box::output()
239114402Sru{
240114402Sru  int font_type = get_special_char_font_type(s);
241114402Sru  if (font_type != LETTER_TYPE)
242114402Sru    printf("\\f[%s]", current_roman_font);
243114402Sru  printf("\\,\\[%s]\\/", s);
244114402Sru  if (font_type != LETTER_TYPE)
245114402Sru    printf("\\fP");
246114402Sru}
247114402Sru
248114402Sruint special_char_box::is_char()
249114402Sru{
250114402Sru  return 1;
251114402Sru}
252114402Sru
253114402Sruvoid special_char_box::debug_print()
254114402Sru{
255114402Sru  fprintf(stderr, "\\[%s]", s);
256114402Sru}
257114402Sru
258114402Sru
259114402Sruvoid char_box::handle_char_type(int st, int ft)
260114402Sru{
261114402Sru  if (st >= 0)
262114402Sru    char_table[c].spacing_type = st;
263114402Sru  if (ft >= 0)
264114402Sru    char_table[c].font_type = ft;
265114402Sru}
266114402Sru
267114402Sruvoid special_char_box::handle_char_type(int st, int ft)
268114402Sru{
269114402Sru  set_special_char_type(s, st, ft);
270114402Sru}
271114402Sru
272114402Sruvoid set_char_type(const char *type, char *ch)
273114402Sru{
274114402Sru  assert(ch != 0);
275114402Sru  int st = lookup_spacing_type(type);
276114402Sru  int ft = lookup_font_type(type);
277114402Sru  if (st < 0 && ft < 0) {
278114402Sru    error("bad character type `%1'", type);
279114402Sru    a_delete ch;
280114402Sru    return;
281114402Sru  }
282114402Sru  box *b = split_text(ch);
283114402Sru  b->handle_char_type(st, ft);
284114402Sru  delete b;
285114402Sru}
286114402Sru
287114402Sru/* We give primes special treatment so that in ``x' sub 2'', the ``2''
288114402Sruwill be tucked under the prime */
289114402Sru
290114402Sruclass prime_box : public pointer_box {
291114402Sru  box *pb;
292114402Srupublic:
293114402Sru  prime_box(box *);
294114402Sru  ~prime_box();
295114402Sru  int compute_metrics(int style);
296114402Sru  void output();
297114402Sru  void compute_subscript_kern();
298114402Sru  void debug_print();
299114402Sru  void handle_char_type(int, int);
300114402Sru};
301114402Sru
302114402Srubox *make_prime_box(box *pp)
303114402Sru{
304114402Sru  return new prime_box(pp);
305114402Sru}
306114402Sru
307114402Sruprime_box::prime_box(box *pp) : pointer_box(pp)
308114402Sru{
309114402Sru  pb = new special_char_box("fm");
310114402Sru}
311114402Sru
312114402Sruprime_box::~prime_box()
313114402Sru{
314114402Sru  delete pb;
315114402Sru}
316114402Sru
317114402Sruint prime_box::compute_metrics(int style)
318114402Sru{
319114402Sru  int res = p->compute_metrics(style);
320114402Sru  pb->compute_metrics(style);
321114402Sru  printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]"
322114402Sru	 "+\\n[" WIDTH_FORMAT "]\n",
323114402Sru	 uid, p->uid, pb->uid);
324114402Sru  printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
325114402Sru	 ">?\\n[" HEIGHT_FORMAT "]\n",
326114402Sru	 uid, p->uid, pb->uid);
327114402Sru  printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
328114402Sru	 ">?\\n[" DEPTH_FORMAT "]\n",
329114402Sru	 uid, p->uid, pb->uid);
330114402Sru  return res;
331114402Sru}
332114402Sru
333114402Sruvoid prime_box::compute_subscript_kern()
334114402Sru{
335114402Sru  p->compute_subscript_kern();
336114402Sru  printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]"
337114402Sru	 "+\\n[" SUB_KERN_FORMAT "]>?0\n",
338114402Sru	 uid, pb->uid, p->uid);
339114402Sru}
340114402Sru
341114402Sruvoid prime_box::output()
342114402Sru{
343114402Sru  p->output();
344114402Sru  pb->output();
345114402Sru}
346114402Sru
347114402Sruvoid prime_box::handle_char_type(int st, int ft)
348114402Sru{
349114402Sru  p->handle_char_type(st, ft);
350114402Sru  pb->handle_char_type(st, ft);
351114402Sru}
352114402Sru
353114402Sruvoid prime_box::debug_print()
354114402Sru{
355114402Sru  p->debug_print();
356114402Sru  putc('\'', stderr);
357114402Sru}
358114402Sru
359114402Srubox *split_text(char *text)
360114402Sru{
361114402Sru  list_box *lb = 0;
362114402Sru  box *fb = 0;
363114402Sru  char *s = text;
364114402Sru  while (*s != '\0') {
365114402Sru    char c = *s++;
366114402Sru    box *b = 0;
367114402Sru    switch (c) {
368114402Sru    case '+':
369114402Sru      b = new special_char_box("pl");
370114402Sru      break;
371114402Sru    case '-':
372114402Sru      b = new special_char_box("mi");
373114402Sru      break;
374114402Sru    case '=':
375114402Sru      b = new special_char_box("eq");
376114402Sru      break;
377114402Sru    case '\'':
378114402Sru      b = new special_char_box("fm");
379114402Sru      break;
380114402Sru    case '<':
381114402Sru      if (*s == '=') {
382114402Sru	b = new special_char_box("<=");
383114402Sru	s++;
384114402Sru	break;
385114402Sru      }
386114402Sru      goto normal_char;
387114402Sru    case '>':
388114402Sru      if (*s == '=') {
389114402Sru	b = new special_char_box(">=");
390114402Sru	s++;
391114402Sru	break;
392114402Sru      }
393114402Sru      goto normal_char;
394114402Sru    case '\\':
395114402Sru      if (*s == '\0') {
396114402Sru	lex_error("bad escape");
397114402Sru	break;
398114402Sru      }
399114402Sru      c = *s++;
400114402Sru      switch (c) {
401114402Sru      case '(':
402114402Sru	{
403114402Sru	  char buf[3];
404114402Sru	  if (*s != '\0') {
405114402Sru	    buf[0] = *s++;
406114402Sru	    if (*s != '\0') {
407114402Sru	      buf[1] = *s++;
408114402Sru	      buf[2] = '\0';
409114402Sru	      b = new special_char_box(buf);
410114402Sru	    }
411114402Sru	    else {
412114402Sru	      lex_error("bad escape");
413114402Sru	    }
414114402Sru	  }
415114402Sru	  else {
416114402Sru	    lex_error("bad escape");
417114402Sru	  }
418114402Sru	}
419114402Sru	break;
420114402Sru      case '[':
421114402Sru	{
422114402Sru	  char *ch = s;
423114402Sru	  while (*s != ']' && *s != '\0')
424114402Sru	    s++;
425114402Sru	  if (*s == '\0')
426114402Sru	    lex_error("bad escape");
427114402Sru	  else {
428114402Sru	    *s++ = '\0';
429114402Sru	    b = new special_char_box(ch);
430114402Sru	  }
431114402Sru	}
432114402Sru	break;
433114402Sru      case 'f':
434114402Sru      case 'g':
435114402Sru      case 'k':
436114402Sru      case 'n':
437114402Sru      case '*':
438114402Sru	{
439114402Sru	  char *escape_start = s - 2;
440114402Sru	  switch (*s) {
441114402Sru	  case '(':
442114402Sru	    if (*++s != '\0')
443114402Sru	      ++s;
444114402Sru	    break;
445114402Sru	  case '[':
446114402Sru	    for (++s; *s != '\0' && *s != ']'; s++)
447114402Sru	      ;
448114402Sru	    break;
449114402Sru	  }
450114402Sru	  if (*s == '\0')
451114402Sru	    lex_error("bad escape");
452114402Sru	  else {
453114402Sru	    ++s;
454114402Sru	    char *buf = new char[s - escape_start + 1];
455114402Sru	    memcpy(buf, escape_start, s - escape_start);
456114402Sru	    buf[s - escape_start] = '\0';
457114402Sru	    b = new quoted_text_box(buf);
458114402Sru	  }
459114402Sru	}
460114402Sru	break;
461114402Sru      case '-':
462114402Sru      case '_':
463114402Sru	{
464114402Sru	  char buf[2];
465114402Sru	  buf[0] = c;
466114402Sru	  buf[1] = '\0';
467114402Sru	  b = new special_char_box(buf);
468114402Sru	}
469114402Sru	break;
470114402Sru      case '`':
471114402Sru	b = new special_char_box("ga");
472114402Sru	break;
473114402Sru      case '\'':
474114402Sru	b = new special_char_box("aa");
475114402Sru	break;
476114402Sru      case 'e':
477114402Sru      case '\\':
478114402Sru	b = new char_box('\\');
479114402Sru	break;
480114402Sru      case '^':
481114402Sru      case '|':
482114402Sru      case '0':
483114402Sru	{
484114402Sru	  char buf[3];
485114402Sru	  buf[0] = '\\';
486114402Sru	  buf[1] = c;
487114402Sru	  buf[2] = '\0';
488114402Sru	  b = new quoted_text_box(strsave(buf));
489114402Sru	  break;
490114402Sru	}
491114402Sru      default:
492114402Sru	lex_error("unquoted escape");
493114402Sru	b = new quoted_text_box(strsave(s - 2));
494114402Sru	s = strchr(s, '\0');
495114402Sru	break;
496114402Sru      }
497114402Sru      break;
498114402Sru    default:
499114402Sru    normal_char:
500114402Sru      b = new char_box(c);
501114402Sru      break;
502114402Sru    }
503114402Sru    while (*s == '\'') {
504114402Sru      if (b == 0)
505114402Sru	b = new quoted_text_box(0);
506114402Sru      b = new prime_box(b);
507114402Sru      s++;
508114402Sru    }
509114402Sru    if (b != 0) {
510114402Sru      if (lb != 0)
511114402Sru	lb->append(b);
512114402Sru      else if (fb != 0) {
513114402Sru	lb = new list_box(fb);
514114402Sru	lb->append(b);
515114402Sru      }
516114402Sru      else
517114402Sru	fb = b;
518114402Sru    }
519114402Sru  }
520114402Sru  a_delete text;
521114402Sru  if (lb != 0)
522114402Sru    return lb;
523114402Sru  else if (fb != 0)
524114402Sru    return fb;
525114402Sru  else
526114402Sru    return new quoted_text_box(0);
527114402Sru}
528114402Sru
529