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
24114402Sruenum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 };
25114402Sru
26114402Sru// Small must be none-zero and must exist in each device.
27114402Sru// Small will be put in the roman font, others are assumed to be
28114402Sru// on the special font (so no font change will be necessary.)
29114402Sru
30114402Srustruct delimiter {
31114402Sru  const char *name;
32114402Sru  int flags;
33114402Sru  const char *small;
34114402Sru  const char *chain_format;
35114402Sru  const char *ext;
36114402Sru  const char *top;
37114402Sru  const char *mid;
38114402Sru  const char *bot;
39114402Sru} delim_table[] = {
40114402Sru  {
41114402Sru    "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]",
42114402Sru    "\\[parenleftex]",
43114402Sru    "\\[parenlefttp]",
44114402Sru    0,
45114402Sru    "\\[parenleftbt]",
46114402Sru  },
47114402Sru  {
48114402Sru    ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]",
49114402Sru    "\\[parenrightex]",
50114402Sru    "\\[parenrighttp]",
51114402Sru    0,
52114402Sru    "\\[parenrightbt]",
53114402Sru  },
54114402Sru  {
55114402Sru    "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]",
56114402Sru    "\\[bracketleftex]",
57114402Sru    "\\[bracketlefttp]",
58114402Sru    0,
59114402Sru    "\\[bracketleftbt]",
60114402Sru  },
61114402Sru  {
62114402Sru    "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]",
63114402Sru    "\\[bracketrightex]",
64114402Sru    "\\[bracketrighttp]",
65114402Sru    0,
66114402Sru    "\\[bracketrightbt]",
67114402Sru  },
68114402Sru  {
69114402Sru    "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]",
70114402Sru    "\\[braceleftex]",
71114402Sru    "\\[bracelefttp]",
72114402Sru    "\\[braceleftmid]",
73114402Sru    "\\[braceleftbt]",
74114402Sru  },
75114402Sru  {
76114402Sru    "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]",
77114402Sru    "\\[bracerightex]",
78114402Sru    "\\[bracerighttp]",
79114402Sru    "\\[bracerightmid]",
80114402Sru    "\\[bracerightbt]",
81114402Sru  },
82114402Sru  {
83114402Sru    "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
84114402Sru    "\\[barex]",
85114402Sru    0,
86114402Sru    0,
87114402Sru    0,
88114402Sru  },
89114402Sru  {
90114402Sru    "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]",
91114402Sru    "\\[bracketleftex]",
92114402Sru    0,
93114402Sru    0,
94114402Sru    "\\[bracketleftbt]",
95114402Sru  },
96114402Sru  {
97114402Sru    "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]",
98114402Sru    "\\[bracketrightex]",
99114402Sru    0,
100114402Sru    0,
101114402Sru    "\\[bracketrightbt]",
102114402Sru  },
103114402Sru  {
104114402Sru    "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]",
105114402Sru    "\\[bracketleftex]",
106114402Sru    "\\[bracketlefttp]",
107114402Sru    0,
108114402Sru    0,
109114402Sru  },
110114402Sru  {
111114402Sru    "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]",
112114402Sru    "\\[bracketrightex]",
113114402Sru    "\\[bracketrighttp]",
114114402Sru    0,
115114402Sru    0,
116114402Sru  },
117114402Sru  {
118114402Sru    "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
119114402Sru    "\\[bardblex]",
120114402Sru    0,
121114402Sru    0,
122114402Sru    0,
123114402Sru  },
124114402Sru  {
125114402Sru    "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]",
126114402Sru    0,
127114402Sru    0,
128114402Sru    0,
129114402Sru    0,
130114402Sru  },
131114402Sru  {
132114402Sru    ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]",
133114402Sru    0,
134114402Sru    0,
135114402Sru    0,
136114402Sru    0,
137114402Sru  },
138114402Sru  {
139114402Sru    "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]",
140114402Sru    "\\[arrowvertex]",
141114402Sru    "\\[arrowverttp]",
142114402Sru    0,
143114402Sru    0,
144114402Sru  },
145114402Sru  {
146114402Sru    "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]",
147114402Sru    "\\[arrowvertex]",
148114402Sru    0,
149114402Sru    0,
150114402Sru    "\\[arrowvertbt]",
151114402Sru  },
152114402Sru  {
153114402Sru    "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]",
154114402Sru    "\\[arrowvertex]",
155114402Sru    "\\[arrowverttp]",
156114402Sru    0,
157114402Sru    "\\[arrowvertbt]",
158114402Sru  },
159114402Sru};
160114402Sru
161114402Sruconst int DELIM_TABLE_SIZE = int(sizeof(delim_table)/sizeof(delim_table[0]));
162114402Sru
163114402Sruclass delim_box : public box {
164114402Sruprivate:
165114402Sru  char *left;
166114402Sru  char *right;
167114402Sru  box *p;
168114402Srupublic:
169114402Sru  delim_box(char *, box *, char *);
170114402Sru  ~delim_box();
171114402Sru  int compute_metrics(int);
172114402Sru  void output();
173114402Sru  void check_tabs(int);
174114402Sru  void debug_print();
175114402Sru};
176114402Sru
177114402Srubox *make_delim_box(char *l, box *pp, char *r)
178114402Sru{
179114402Sru  if (l != 0 && *l == '\0') {
180114402Sru    a_delete l;
181114402Sru    l = 0;
182114402Sru  }
183114402Sru  if (r != 0 && *r == '\0') {
184114402Sru    a_delete r;
185114402Sru    r = 0;
186114402Sru  }
187114402Sru  return new delim_box(l, pp, r);
188114402Sru}
189114402Sru
190114402Srudelim_box::delim_box(char *l, box *pp, char *r)
191114402Sru: left(l), right(r), p(pp)
192114402Sru{
193114402Sru}
194114402Sru
195114402Srudelim_box::~delim_box()
196114402Sru{
197114402Sru  a_delete left;
198114402Sru  a_delete right;
199114402Sru  delete p;
200114402Sru}
201114402Sru
202114402Srustatic void build_extensible(const char *ext, const char *top, const char *mid,
203114402Sru			     const char *bot)
204114402Sru{
205114402Sru  assert(ext != 0);
206114402Sru  printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
207114402Sru	 ext);
208114402Sru  printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n");
209114402Sru  printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n");
210114402Sru  if (top) {
211114402Sru    printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
212114402Sru	   ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
213114402Sru	   top);
214114402Sru    printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n");
215114402Sru    printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n");
216114402Sru  }
217114402Sru  if (mid) {
218114402Sru    printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
219114402Sru	   ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
220114402Sru	   mid);
221114402Sru    printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n");
222114402Sru    printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n");
223114402Sru  }
224114402Sru  if (bot) {
225114402Sru    printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
226114402Sru	   ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
227114402Sru	   bot);
228114402Sru    printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n");
229114402Sru    printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n");
230114402Sru  }
231114402Sru  printf(".nr " TOTAL_HEIGHT_REG " 0");
232114402Sru  if (top)
233114402Sru    printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]");
234114402Sru  if (bot)
235114402Sru    printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]");
236114402Sru  if (mid)
237114402Sru    printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]");
238114402Sru  printf("\n");
239114402Sru  // determine how many extensible characters we need
240114402Sru  printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]");
241114402Sru  if (mid)
242114402Sru    printf("/2");
243114402Sru  printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n["
244114402Sru	 EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n");
245114402Sru
246114402Sru  printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n["
247114402Sru	 EXT_DEPTH_REG "]*\\n[" TEMP_REG "]");
248114402Sru  if (mid)
249114402Sru    printf("*2");
250114402Sru  printf(")\n");
251114402Sru  printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
252114402Sru	 "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n",
253114402Sru	 axis_height);
254114402Sru  if (top)
255114402Sru    printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'"
256114402Sru	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
257114402Sru	   "\\v'\\n[" TOP_DEPTH_REG "]u'\n",
258114402Sru	   top);
259114402Sru
260114402Sru  // this macro appends $2 copies of $3 to string $1
261114402Sru  printf(".de " REPEAT_APPEND_STRING_MACRO "\n"
262114402Sru	 ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n"
263114402Sru	 "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n"
264114402Sru	 ".\\}\n"
265114402Sru	 "..\n");
266114402Sru
267114402Sru  printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] "
268114402Sru	 "\\v'\\n[" EXT_HEIGHT_REG "]u'"
269114402Sru	 "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
270114402Sru	 "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
271114402Sru	 ext);
272114402Sru
273114402Sru  if (mid) {
274114402Sru    printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'"
275114402Sru	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
276114402Sru	   "\\v'\\n[" MID_DEPTH_REG "]u'\n",
277114402Sru	   mid);
278114402Sru    printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING
279114402Sru	   " \\n[" TEMP_REG "] "
280114402Sru	   "\\v'\\n[" EXT_HEIGHT_REG "]u'"
281114402Sru	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
282114402Sru	   "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
283114402Sru	   ext);
284114402Sru  }
285114402Sru  if (bot)
286114402Sru    printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'"
287114402Sru	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
288114402Sru	   "\\v'\\n[" BOT_DEPTH_REG "]u'\n",
289114402Sru	   bot);
290114402Sru  printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n");
291114402Sru}
292114402Sru
293114402Srustatic void define_extensible_string(char *delim, int uid,
294114402Sru				     left_or_right_t left_or_right)
295114402Sru{
296114402Sru  printf(".ds " DELIM_STRING "\n");
297114402Sru  delimiter *d = delim_table;
298114402Sru  int delim_len = strlen(delim);
299114402Sru  int i;
300114402Sru  for (i = 0; i < DELIM_TABLE_SIZE; i++, d++)
301114402Sru    if (strncmp(delim, d->name, delim_len) == 0
302114402Sru	&& (left_or_right & d->flags) != 0)
303114402Sru      break;
304114402Sru  if (i >= DELIM_TABLE_SIZE) {
305114402Sru    error("there is no `%1' delimiter", delim);
306114402Sru    printf(".nr " DELIM_WIDTH_REG " 0\n");
307114402Sru    return;
308114402Sru  }
309114402Sru
310114402Sru  printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
311114402Sru	 ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
312114402Sru	   "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
313114402Sru	 ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n"
314114402Sru	 ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
315114402Sru	 "\\{",
316114402Sru	 current_roman_font, d->small, axis_height,
317114402Sru	 current_roman_font, d->small);
318114402Sru
319114402Sru  char buf[256];
320114402Sru  sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]");
321114402Sru  printf(".nr " INDEX_REG " 0\n"
322114402Sru	 ".de " TEMP_MACRO "\n"
323114402Sru	 ".ie c%s \\{\\\n"
324114402Sru	 ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n"
325114402Sru	 ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
326114402Sru	   "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n"
327114402Sru	 ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n"
328114402Sru	 ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
329114402Sru	 "\\{.nr " INDEX_REG " +1\n"
330114402Sru	 "." TEMP_MACRO "\n"
331114402Sru	 ".\\}\\}\n"
332114402Sru	 ".el .nr " INDEX_REG " 0-1\n"
333114402Sru	 "..\n"
334114402Sru	 "." TEMP_MACRO "\n",
335114402Sru	 buf, buf, axis_height, buf);
336114402Sru  if (d->ext) {
337114402Sru    printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext);
338114402Sru    build_extensible(d->ext, d->top, d->mid, d->bot);
339114402Sru    printf(".\\}\\}\n");
340114402Sru  }
341114402Sru  printf(".\\}\n");
342114402Sru  printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n");
343114402Sru  printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid);
344114402Sru  printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
345114402Sru	 ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n",
346114402Sru	 uid, uid, axis_height);
347114402Sru  printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
348114402Sru	 ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n",
349114402Sru	 uid, uid, axis_height);
350114402Sru}
351114402Sru
352114402Sruint delim_box::compute_metrics(int style)
353114402Sru{
354114402Sru  int r = p->compute_metrics(style);
355114402Sru  printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
356114402Sru  printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
357114402Sru  printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
358114402Sru  printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM"
359114402Sru	 ">?(\\n[" DEPTH_FORMAT "]+%dM)\n",
360114402Sru	 p->uid, axis_height, p->uid, axis_height);
361114402Sru  printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500"
362114402Sru	 ">?(\\n[" DELTA_REG "]*2-%dM)\n",
363114402Sru	 delimiter_factor, delimiter_shortfall);
364114402Sru  if (left) {
365114402Sru    define_extensible_string(left, uid, LEFT_DELIM);
366114402Sru    printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n",
367114402Sru	   uid);
368114402Sru    if (r)
369114402Sru      printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n");
370114402Sru  }
371114402Sru  if (right) {
372114402Sru    define_extensible_string(right, uid, RIGHT_DELIM);
373114402Sru    printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n",
374114402Sru	   uid);
375114402Sru  }
376114402Sru  return r;
377114402Sru}
378114402Sru
379114402Sruvoid delim_box::output()
380114402Sru{
381114402Sru  if (left)
382114402Sru    printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid);
383114402Sru  p->output();
384114402Sru  if (right)
385114402Sru    printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid);
386114402Sru}
387114402Sru
388114402Sruvoid delim_box::check_tabs(int level)
389114402Sru{
390114402Sru  p->check_tabs(level);
391114402Sru}
392114402Sru
393114402Sruvoid delim_box::debug_print()
394114402Sru{
395114402Sru  fprintf(stderr, "left \"%s\" { ", left ? left : "");
396114402Sru  p->debug_print();
397114402Sru  fprintf(stderr, " }");
398114402Sru  if (right)
399114402Sru    fprintf(stderr, " right \"%s\"", right);
400114402Sru}
401114402Sru
402