LCOV - code coverage report
Current view: top level - preproc/eqn - delim.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 103 126 81.7 %
Date: 2026-01-16 17:51:41 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14