1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc.
3     Written by James Clark (jjc@jclark.com)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 2, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file COPYING.  If not, write to the Free Software
19Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20
21#include "eqn.h"
22#include "pbox.h"
23
24enum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 };
25
26// Small must be none-zero and must exist in each device.
27// Small will be put in the roman font, others are assumed to be
28// on the special font (so no font change will be necessary.)
29
30struct delimiter {
31  const char *name;
32  int flags;
33  const char *small;
34  const char *chain_format;
35  const char *ext;
36  const char *top;
37  const char *mid;
38  const char *bot;
39} delim_table[] = {
40  {
41    "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]",
42    "\\[parenleftex]",
43    "\\[parenlefttp]",
44    0,
45    "\\[parenleftbt]",
46  },
47  {
48    ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]",
49    "\\[parenrightex]",
50    "\\[parenrighttp]",
51    0,
52    "\\[parenrightbt]",
53  },
54  {
55    "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]",
56    "\\[bracketleftex]",
57    "\\[bracketlefttp]",
58    0,
59    "\\[bracketleftbt]",
60  },
61  {
62    "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]",
63    "\\[bracketrightex]",
64    "\\[bracketrighttp]",
65    0,
66    "\\[bracketrightbt]",
67  },
68  {
69    "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]",
70    "\\[braceleftex]",
71    "\\[bracelefttp]",
72    "\\[braceleftmid]",
73    "\\[braceleftbt]",
74  },
75  {
76    "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]",
77    "\\[bracerightex]",
78    "\\[bracerighttp]",
79    "\\[bracerightmid]",
80    "\\[bracerightbt]",
81  },
82  {
83    "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
84    "\\[barex]",
85    0,
86    0,
87    0,
88  },
89  {
90    "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]",
91    "\\[bracketleftex]",
92    0,
93    0,
94    "\\[bracketleftbt]",
95  },
96  {
97    "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]",
98    "\\[bracketrightex]",
99    0,
100    0,
101    "\\[bracketrightbt]",
102  },
103  {
104    "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]",
105    "\\[bracketleftex]",
106    "\\[bracketlefttp]",
107    0,
108    0,
109  },
110  {
111    "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]",
112    "\\[bracketrightex]",
113    "\\[bracketrighttp]",
114    0,
115    0,
116  },
117  {
118    "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
119    "\\[bardblex]",
120    0,
121    0,
122    0,
123  },
124  {
125    "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]",
126    0,
127    0,
128    0,
129    0,
130  },
131  {
132    ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]",
133    0,
134    0,
135    0,
136    0,
137  },
138  {
139    "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]",
140    "\\[arrowvertex]",
141    "\\[arrowverttp]",
142    0,
143    0,
144  },
145  {
146    "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]",
147    "\\[arrowvertex]",
148    0,
149    0,
150    "\\[arrowvertbt]",
151  },
152  {
153    "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]",
154    "\\[arrowvertex]",
155    "\\[arrowverttp]",
156    0,
157    "\\[arrowvertbt]",
158  },
159};
160
161const int DELIM_TABLE_SIZE = int(sizeof(delim_table)/sizeof(delim_table[0]));
162
163class delim_box : public box {
164private:
165  char *left;
166  char *right;
167  box *p;
168public:
169  delim_box(char *, box *, char *);
170  ~delim_box();
171  int compute_metrics(int);
172  void output();
173  void check_tabs(int);
174  void debug_print();
175};
176
177box *make_delim_box(char *l, box *pp, char *r)
178{
179  if (l != 0 && *l == '\0') {
180    a_delete l;
181    l = 0;
182  }
183  if (r != 0 && *r == '\0') {
184    a_delete r;
185    r = 0;
186  }
187  return new delim_box(l, pp, r);
188}
189
190delim_box::delim_box(char *l, box *pp, char *r)
191: left(l), right(r), p(pp)
192{
193}
194
195delim_box::~delim_box()
196{
197  a_delete left;
198  a_delete right;
199  delete p;
200}
201
202static void build_extensible(const char *ext, const char *top, const char *mid,
203			     const char *bot)
204{
205  assert(ext != 0);
206  printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
207	 ext);
208  printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n");
209  printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n");
210  if (top) {
211    printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
212	   ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
213	   top);
214    printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n");
215    printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n");
216  }
217  if (mid) {
218    printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
219	   ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
220	   mid);
221    printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n");
222    printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n");
223  }
224  if (bot) {
225    printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
226	   ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
227	   bot);
228    printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n");
229    printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n");
230  }
231  printf(".nr " TOTAL_HEIGHT_REG " 0");
232  if (top)
233    printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]");
234  if (bot)
235    printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]");
236  if (mid)
237    printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]");
238  printf("\n");
239  // determine how many extensible characters we need
240  printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]");
241  if (mid)
242    printf("/2");
243  printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n["
244	 EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n");
245
246  printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n["
247	 EXT_DEPTH_REG "]*\\n[" TEMP_REG "]");
248  if (mid)
249    printf("*2");
250  printf(")\n");
251  printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
252	 "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n",
253	 axis_height);
254  if (top)
255    printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'"
256	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
257	   "\\v'\\n[" TOP_DEPTH_REG "]u'\n",
258	   top);
259
260  // this macro appends $2 copies of $3 to string $1
261  printf(".de " REPEAT_APPEND_STRING_MACRO "\n"
262	 ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n"
263	 "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n"
264	 ".\\}\n"
265	 "..\n");
266
267  printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] "
268	 "\\v'\\n[" EXT_HEIGHT_REG "]u'"
269	 "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
270	 "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
271	 ext);
272
273  if (mid) {
274    printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'"
275	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
276	   "\\v'\\n[" MID_DEPTH_REG "]u'\n",
277	   mid);
278    printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING
279	   " \\n[" TEMP_REG "] "
280	   "\\v'\\n[" EXT_HEIGHT_REG "]u'"
281	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
282	   "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
283	   ext);
284  }
285  if (bot)
286    printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'"
287	   "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
288	   "\\v'\\n[" BOT_DEPTH_REG "]u'\n",
289	   bot);
290  printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n");
291}
292
293static void define_extensible_string(char *delim, int uid,
294				     left_or_right_t left_or_right)
295{
296  printf(".ds " DELIM_STRING "\n");
297  delimiter *d = delim_table;
298  int delim_len = strlen(delim);
299  int i;
300  for (i = 0; i < DELIM_TABLE_SIZE; i++, d++)
301    if (strncmp(delim, d->name, delim_len) == 0
302	&& (left_or_right & d->flags) != 0)
303      break;
304  if (i >= DELIM_TABLE_SIZE) {
305    error("there is no `%1' delimiter", delim);
306    printf(".nr " DELIM_WIDTH_REG " 0\n");
307    return;
308  }
309
310  printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
311	 ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
312	   "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
313	 ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n"
314	 ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
315	 "\\{",
316	 current_roman_font, d->small, axis_height,
317	 current_roman_font, d->small);
318
319  char buf[256];
320  sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]");
321  printf(".nr " INDEX_REG " 0\n"
322	 ".de " TEMP_MACRO "\n"
323	 ".ie c%s \\{\\\n"
324	 ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n"
325	 ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
326	   "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n"
327	 ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n"
328	 ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
329	 "\\{.nr " INDEX_REG " +1\n"
330	 "." TEMP_MACRO "\n"
331	 ".\\}\\}\n"
332	 ".el .nr " INDEX_REG " 0-1\n"
333	 "..\n"
334	 "." TEMP_MACRO "\n",
335	 buf, buf, axis_height, buf);
336  if (d->ext) {
337    printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext);
338    build_extensible(d->ext, d->top, d->mid, d->bot);
339    printf(".\\}\\}\n");
340  }
341  printf(".\\}\n");
342  printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n");
343  printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid);
344  printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
345	 ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n",
346	 uid, uid, axis_height);
347  printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
348	 ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n",
349	 uid, uid, axis_height);
350}
351
352int delim_box::compute_metrics(int style)
353{
354  int r = p->compute_metrics(style);
355  printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
356  printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
357  printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
358  printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM"
359	 ">?(\\n[" DEPTH_FORMAT "]+%dM)\n",
360	 p->uid, axis_height, p->uid, axis_height);
361  printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500"
362	 ">?(\\n[" DELTA_REG "]*2-%dM)\n",
363	 delimiter_factor, delimiter_shortfall);
364  if (left) {
365    define_extensible_string(left, uid, LEFT_DELIM);
366    printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n",
367	   uid);
368    if (r)
369      printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n");
370  }
371  if (right) {
372    define_extensible_string(right, uid, RIGHT_DELIM);
373    printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n",
374	   uid);
375  }
376  return r;
377}
378
379void delim_box::output()
380{
381  if (left)
382    printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid);
383  p->output();
384  if (right)
385    printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid);
386}
387
388void delim_box::check_tabs(int level)
389{
390  p->check_tabs(level);
391}
392
393void delim_box::debug_print()
394{
395  fprintf(stderr, "left \"%s\" { ", left ? left : "");
396  p->debug_print();
397  fprintf(stderr, " }");
398  if (right)
399    fprintf(stderr, " right \"%s\"", right);
400}
401
402