LCOV - code coverage report
Current view: top level - preproc/pic - troff.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 277 327 84.7 %
Date: 2026-01-16 17:51:41 Functions: 31 32 96.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1989-2025 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 <assert.h>
      24             : #include <stdlib.h> // free()
      25             : #include <stdio.h> // fputs(), printf(), putchar(), puts(), stdout
      26             : 
      27             : #include "pic.h"
      28             : #include "common.h"
      29             : 
      30             : const double RELATIVE_THICKNESS = -1.0;
      31             : const double BAD_THICKNESS = -2.0;
      32             : 
      33             : class simple_output : public common_output {
      34             :   virtual void simple_line(const position &, const position &) = 0;
      35             :   virtual void simple_spline(const position &, const position *, int n)
      36             :     = 0;
      37             :   virtual void simple_arc(const position &, const position &,
      38             :                           const position &) = 0;
      39             :   virtual void simple_circle(int, const position &, double rad) = 0;
      40             :   virtual void simple_ellipse(int, const position &, const distance &)
      41             :     = 0;
      42             :   virtual void simple_polygon(int, const position *, int) = 0;
      43             :   virtual void line_thickness(double) = 0;
      44             :   virtual void set_fill(double) = 0;
      45             :   virtual void set_color(char *, char *) = 0;
      46             :   virtual void reset_color() = 0;
      47             :   virtual char *get_last_filled() = 0;
      48             :   void dot(const position &, const line_type &) = 0;
      49             : public:
      50             :   void start_picture(double sc, const position &ll, const position &ur)
      51             :     = 0;
      52             :   void finish_picture() = 0;
      53             :   void text(const position &, text_piece *, int, double) = 0;
      54             :   void line(const position &, const position *, int n,
      55             :             const line_type &);
      56             :   void polygon(const position *, int n,
      57             :                const line_type &, double);
      58             :   void spline(const position &, const position *, int n,
      59             :               const line_type &);
      60             :   void arc(const position &, const position &, const position &,
      61             :            const line_type &);
      62             :   void circle(const position &, double rad, const line_type &, double);
      63             :   void ellipse(const position &, const distance &, const line_type &,
      64             :                double);
      65             :   int supports_filled_polygons();
      66             : };
      67             : 
      68         374 : int simple_output::supports_filled_polygons()
      69             : {
      70         374 :   return driver_extension_flag != 0;
      71             : }
      72             : 
      73         140 : void simple_output::arc(const position &start, const position &cent,
      74             :                         const position &end, const line_type &lt)
      75             : {
      76         140 :   switch (lt.type) {
      77         140 :   case line_type::solid:
      78         140 :     line_thickness(lt.thickness);
      79         140 :     simple_arc(start, cent, end);
      80         140 :     break;
      81           0 :   case line_type::invisible:
      82           0 :     break;
      83           0 :   case line_type::dashed:
      84           0 :     dashed_arc(start, cent, end, lt);
      85           0 :     break;
      86           0 :   case line_type::dotted:
      87           0 :     dotted_arc(start, cent, end, lt);
      88           0 :     break;
      89             :   }
      90         140 : }
      91             : 
      92         442 : void simple_output::line(const position &start, const position *v,
      93             :                          int n, const line_type &lt)
      94             : {
      95         442 :   position pos = start;
      96         442 :   line_thickness(lt.thickness);
      97        1115 :   for (int i = 0; i < n; i++) {
      98         673 :     switch (lt.type) {
      99         377 :     case line_type::solid:
     100         377 :       simple_line(pos, v[i]);
     101         377 :       break;
     102         200 :     case line_type::dotted:
     103             :       {
     104         200 :         distance vec(v[i] - pos);
     105         200 :         double dist = hypot(vec);
     106         200 :         int ndots = int(dist/lt.dash_width + .5);
     107         200 :         if (ndots == 0)
     108           0 :           dot(pos, lt);
     109             :         else {
     110         200 :           vec /= double(ndots);
     111        2312 :           for (int j = 0; j <= ndots; j++)
     112        2112 :             dot(pos + vec*j, lt);
     113             :         }
     114             :       }
     115         200 :       break;
     116          96 :     case line_type::dashed:
     117             :       {
     118          96 :         distance vec(v[i] - pos);
     119          96 :         double dist = hypot(vec);
     120          96 :         if (dist <= lt.dash_width*2.0)
     121           0 :           simple_line(pos, v[i]);
     122             :         else {
     123          96 :           int ndashes = int((dist - lt.dash_width)
     124          96 :                             / (lt.dash_width * 2.0) + .5);
     125          96 :           distance dash_vec = vec*(lt.dash_width/dist);
     126          96 :           double dash_gap = (dist - lt.dash_width)/ndashes;
     127          96 :           distance dash_gap_vec = vec*(dash_gap/dist);
     128         734 :           for (int j = 0; j <= ndashes; j++) {
     129         638 :             position s(pos + dash_gap_vec*j);
     130         638 :             simple_line(s, s + dash_vec);
     131             :           }
     132             :         }
     133             :       }
     134          96 :       break;
     135           0 :     case line_type::invisible:
     136           0 :       break;
     137           0 :     default:
     138           0 :       assert(0 == "unhandled case of line type");
     139             :     }
     140         673 :     pos = v[i];
     141             :   }
     142         442 : }
     143             : 
     144          10 : void simple_output::spline(const position &start, const position *v,
     145             :                            int n, const line_type &lt)
     146             : {
     147          10 :   line_thickness(lt.thickness);
     148          10 :   simple_spline(start, v, n);
     149          10 : }
     150             : 
     151         454 : void simple_output::polygon(const position *v, int n,
     152             :                             const line_type &lt, double fill)
     153             : {
     154         908 :   if (driver_extension_flag
     155         454 :       && ((fill >= 0.0) || (get_last_filled() != 0))) {
     156         284 :     if (get_last_filled() == 0)
     157         244 :       set_fill(fill);
     158         284 :     simple_polygon(1, v, n);
     159             :   }
     160         454 :   if (lt.type == line_type::solid && driver_extension_flag) {
     161         375 :     line_thickness(lt.thickness);
     162         375 :     simple_polygon(0, v, n);
     163             :   }
     164          79 :   else if (lt.type != line_type::invisible) {
     165          71 :     line_thickness(lt.thickness);
     166          71 :     line(v[n - 1], v, n, lt);
     167             :   }
     168         454 : }
     169             : 
     170         180 : void simple_output::circle(const position &cent, double rad,
     171             :                            const line_type &lt, double fill)
     172             : {
     173         360 :   if (driver_extension_flag
     174         180 :       && ((fill >= 0.0) || (get_last_filled() != 0))) {
     175         144 :     if (get_last_filled() == 0)
     176         142 :       set_fill(fill);
     177         144 :     simple_circle(1, cent, rad);
     178             :   }
     179         180 :   line_thickness(lt.thickness);
     180         180 :   switch (lt.type) {
     181          16 :   case line_type::invisible:
     182          16 :     break;
     183           0 :   case line_type::dashed:
     184           0 :     dashed_circle(cent, rad, lt);
     185           0 :     break;
     186           0 :   case line_type::dotted:
     187           0 :     dotted_circle(cent, rad, lt);
     188           0 :     break;
     189         164 :   case line_type::solid:
     190         164 :     simple_circle(0, cent, rad);
     191         164 :     break;
     192           0 :   default:
     193           0 :     assert(0 == "unhandled case of line type");
     194             :   }
     195         180 : }
     196             : 
     197          29 : void simple_output::ellipse(const position &cent, const distance &dim,
     198             :                             const line_type &lt, double fill)
     199             : {
     200          58 :   if (driver_extension_flag
     201          29 :       && ((fill >= 0.0) || (get_last_filled() != 0))) {
     202           0 :     if (get_last_filled() == 0)
     203           0 :       set_fill(fill);
     204           0 :     simple_ellipse(1, cent, dim);
     205             :   }
     206          29 :   if (lt.type != line_type::invisible)
     207          29 :     line_thickness(lt.thickness);
     208          29 :   switch (lt.type) {
     209           0 :   case line_type::invisible:
     210           0 :     break;
     211           0 :   case line_type::dotted:
     212           0 :     dotted_ellipse(cent, dim, lt);
     213           0 :     break;
     214           0 :   case line_type::dashed:
     215           0 :     dashed_ellipse(cent, dim, lt);
     216           0 :     break;
     217          29 :   case line_type::solid:
     218          29 :     simple_ellipse(0, cent, dim);
     219          29 :     break;
     220           0 :   default:
     221           0 :     assert(0 == "unhandled case of line type");
     222             :   }
     223          29 : }
     224             : 
     225             : class troff_output : public simple_output {
     226             :   const char *last_filename;
     227             :   position upper_left;
     228             :   double height;
     229             :   double scale;
     230             :   double last_line_thickness;
     231             :   double last_fill;
     232             :   char *last_filled;            // color
     233             :   char *last_outlined;          // color
     234             : public:
     235             :   troff_output();
     236             :   ~troff_output();
     237             :   void start_picture(double, const position &ll, const position &ur);
     238             :   void finish_picture();
     239             :   void text(const position &, text_piece *, int, double);
     240             :   void dot(const position &, const line_type &);
     241             :   void command(const char *, const char *, int);
     242             :   void set_location(const char *, int);
     243             :   void simple_line(const position &, const position &);
     244             :   void simple_spline(const position &, const position *, int n);
     245             :   void simple_arc(const position &, const position &, const position &);
     246             :   void simple_circle(int, const position &, double rad);
     247             :   void simple_ellipse(int, const position &, const distance &);
     248             :   void simple_polygon(int, const position *, int);
     249             :   void line_thickness(double p);
     250             :   void set_fill(double);
     251             :   void set_color(char *, char *);
     252             :   void reset_color();
     253             :   char *get_last_filled();
     254             :   char *get_outline_color();
     255             :   position transform(const position &);
     256             : };
     257             : 
     258          48 : output *make_troff_output()
     259             : {
     260          48 :   return new troff_output;
     261             : }
     262             : 
     263          48 : troff_output::troff_output()
     264             : : last_filename(0), last_line_thickness(BAD_THICKNESS),
     265          48 :   last_fill(-1.0), last_filled(0), last_outlined(0)
     266             : {
     267          48 : }
     268             : 
     269          96 : troff_output::~troff_output()
     270             : {
     271          48 :   free((char *)last_filename);
     272          96 : }
     273             : 
     274       10609 : inline position troff_output::transform(const position &pos)
     275             : {
     276       21218 :   return position((pos.x - upper_left.x)/scale,
     277       10609 :                   (upper_left.y - pos.y)/scale);
     278             : }
     279             : 
     280             : #define FILL_REG "00"
     281             : 
     282             : // If this register > 0, then pic will generate \X'ps: ...' commands
     283             : // if the aligned attribute is used.
     284             : #define GROPS_REG "0p"
     285             : 
     286             : // If this register is defined, geqn won't produce '\x's.
     287             : #define EQN_NO_EXTRA_SPACE_REG "0x"
     288             : 
     289             : #define SAVED_STROKE_COLOR_STR "gpic*saved-stroke-color"
     290             : #define SAVED_FILL_COLOR_STR "gpic*saved-fill-color"
     291             : 
     292         113 : void troff_output::start_picture(double sc,
     293             :                                  const position &ll, const position &ur)
     294             : {
     295         113 :   upper_left.x = ll.x;
     296         113 :   upper_left.y = ur.y;
     297         113 :   scale = compute_scale(sc, ll, ur);
     298         113 :   height = (ur.y - ll.y)/scale;
     299         113 :   double width = (ur.x - ll.x)/scale;
     300         113 :   printf(".PS %.3fi %.3fi", height, width);
     301         113 :   if (args)
     302           3 :     printf(" %s\n", args);
     303             :   else
     304         110 :     putchar('\n');
     305         113 :   printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
     306         113 :   printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
     307         113 :   printf(".nr " FILL_REG " \\n(.u\n.nf\n");
     308         113 :   printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
     309             :   // This guarantees that if the picture is used in a diversion it will
     310             :   // have the right width.
     311         113 :   printf("\\h'%.3fi'\n.sp -1\n", width);
     312         113 :   (void) puts(".ds " SAVED_STROKE_COLOR_STR " default");
     313         113 :   (void) puts(".ds " SAVED_FILL_COLOR_STR " default");
     314         113 :   (void) puts(".if !'\\n[.m]'' .ds " SAVED_STROKE_COLOR_STR " \\n[.m]");
     315         113 :   (void) puts(".if !'\\n[.M]'' .ds " SAVED_FILL_COLOR_STR " \\n[.M]");
     316         113 : }
     317             : 
     318         113 : void troff_output::finish_picture()
     319             : {
     320         113 :   line_thickness(BAD_THICKNESS);
     321         113 :   last_fill = -1.0;             // force it to be reset for each picture
     322         113 :   reset_color();
     323         113 :   if (!(want_flyback || want_alternate_flyback))
     324         113 :     printf(".sp %.3fi+1\n", height);
     325         113 :   printf(".if \\n(" FILL_REG " .fi\n");
     326         113 :   printf(".br\n");
     327         113 :   printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
     328         113 :   (void) puts(".gcolor \\*[" SAVED_STROKE_COLOR_STR "]");
     329         113 :   (void) puts(".fcolor \\*[" SAVED_FILL_COLOR_STR "]");
     330             :   // this is a little gross
     331         113 :   set_location(current_filename, current_lineno);
     332         113 :   if (want_flyback)
     333           0 :     fputs(".PF\n", stdout);
     334         113 :   else if (want_alternate_flyback)
     335           0 :     fputs(".PY\n", stdout);
     336             :   else
     337         113 :     fputs(".PE\n", stdout);
     338         113 : }
     339             : 
     340          24 : void troff_output::command(const char *s,
     341             :                            const char *filename, int lineno)
     342             : {
     343          24 :   if (filename != 0)
     344          24 :     set_location(filename, lineno);
     345          24 :   fputs(s, stdout);
     346          24 :   putchar('\n');
     347          24 : }
     348             : 
     349         308 : void troff_output::simple_circle(int filled, const position &cent,
     350             :                                  double rad)
     351             : {
     352         308 :   position c = transform(cent);
     353         308 :   printf("\\h'%.3fi'"
     354             :          "\\v'%.3fi'"
     355             :          "\\D'%c %.3fi'"
     356             :          "\n.sp -1\n",
     357         308 :          c.x - rad/scale,
     358             :          c.y,
     359             :          (filled ? 'C' : 'c'),
     360         308 :          rad*2.0/scale);
     361         308 : }
     362             : 
     363          29 : void troff_output::simple_ellipse(int filled, const position &cent,
     364             :                                   const distance &dim)
     365             : {
     366          29 :   position c = transform(cent);
     367          29 :   printf("\\h'%.3fi'"
     368             :          "\\v'%.3fi'"
     369             :          "\\D'%c %.3fi %.3fi'"
     370             :          "\n.sp -1\n",
     371          29 :          c.x - dim.x/(2.0*scale),
     372             :          c.y,
     373             :          (filled ? 'E' : 'e'),
     374          29 :          dim.x/scale, dim.y/scale);
     375          29 : }
     376             : 
     377         140 : void troff_output::simple_arc(const position &start,
     378             :                               const distance &cent, const distance &end)
     379             : {
     380         140 :   position s = transform(start);
     381         140 :   position c = transform(cent);
     382         140 :   distance cv = c - s;
     383         140 :   distance ev = transform(end) - c;
     384         140 :   printf("\\h'%.3fi'"
     385             :          "\\v'%.3fi'"
     386             :          "\\D'a %.3fi %.3fi %.3fi %.3fi'"
     387             :          "\n.sp -1\n",
     388             :          s.x, s.y, cv.x, cv.y, ev.x, ev.y);
     389         140 : }
     390             : 
     391        3355 : void troff_output::simple_line(const position &start,
     392             :                                const position &end)
     393             : {
     394        3355 :   position s = transform(start);
     395        3355 :   distance ev = transform(end) - s;
     396        3355 :   printf("\\h'%.3fi'"
     397             :          "\\v'%.3fi'"
     398             :          "\\D'l %.3fi %.3fi'"
     399             :          "\n.sp -1\n",
     400             :          s.x, s.y, ev.x, ev.y);
     401        3355 : }
     402             : 
     403          10 : void troff_output::simple_spline(const position &start,
     404             :                                  const position *v, int n)
     405             : {
     406          10 :   position pos = transform(start);
     407          10 :   printf("\\h'%.3fi'"
     408             :          "\\v'%.3fi'",
     409             :          pos.x, pos.y);
     410          10 :   fputs("\\D'~ ", stdout);
     411          44 :   for (int i = 0; i < n; i++) {
     412          34 :     position temp = transform(v[i]);
     413          34 :     distance d = temp - pos;
     414          34 :     pos = temp;
     415          34 :     if (i != 0)
     416          24 :       putchar(' ');
     417          34 :     printf("%.3fi %.3fi", d.x, d.y);
     418             :   }
     419          10 :   printf("'\n.sp -1\n");
     420          10 : }
     421             : 
     422             : // a solid polygon
     423             : 
     424         659 : void troff_output::simple_polygon(int filled, const position *v, int n)
     425             : {
     426         659 :   position pos = transform(v[0]);
     427         659 :   printf("\\h'%.3fi'"
     428             :          "\\v'%.3fi'",
     429             :          pos.x, pos.y);
     430         659 :   printf("\\D'%c ", (filled ? 'P' : 'p'));
     431        2256 :   for (int i = 1; i < n; i++) {
     432        1597 :     position temp = transform(v[i]);
     433        1597 :     distance d = temp - pos;
     434        1597 :     pos = temp;
     435        1597 :     if (i != 1)
     436         938 :       putchar(' ');
     437        1597 :     printf("%.3fi %.3fi", d.x, d.y);
     438             :   }
     439         659 :   printf("'\n.sp -1\n");
     440         659 : }
     441             : 
     442             : const double TEXT_AXIS = 0.22;  // in ems
     443             : 
     444         842 : static const char *choose_delimiter(const char *text)
     445             : {
     446         842 :   if (strchr(text, '\'') == 0)
     447         840 :     return "'";
     448             :   else
     449           2 :     return "\\(ts";
     450             : }
     451             : 
     452         777 : void troff_output::text(const position &center, text_piece *v, int n,
     453             :                         double ang)
     454             : {
     455             :  // text might use lines (e.g., in equations)
     456         777 :   line_thickness(BAD_THICKNESS);
     457         777 :   int rotate_flag = 0;
     458         777 :   if (driver_extension_flag && ang != 0.0) {
     459           0 :     rotate_flag = 1;
     460           0 :     position c = transform(center);
     461           0 :     printf(".if \\n(" GROPS_REG " \\{\\\n"
     462             :            "\\h'%.3fi'"
     463             :            "\\v'%.3fi'"
     464             :            "\\X'ps: exec gsave currentpoint 2 copy translate %.4f"
     465             :            " rotate neg exch neg exch translate'"
     466             :            "\n.sp -1\n"
     467             :            ".\\}\n",
     468           0 :            c.x, c.y, -ang*180.0/M_PI);
     469             :   }
     470        1625 :   for (int i = 0; i < n; i++)
     471         848 :     if (v[i].text != 0 && *v[i].text != '\0') {
     472         842 :       position c = transform(center);
     473         842 :       if (v[i].filename != 0)
     474         842 :         set_location(v[i].filename, v[i].lineno);
     475         842 :       printf("\\h'%.3fi", c.x);
     476         842 :       const char *delim = choose_delimiter(v[i].text);
     477         842 :       if (v[i].adj.h == RIGHT_ADJUST)
     478          40 :         printf("-\\w%s%s%su", delim, v[i].text, delim);
     479         802 :       else if (v[i].adj.h != LEFT_ADJUST)
     480         770 :         printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
     481         842 :       putchar('\'');
     482         842 :       printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
     483             :              c.y,
     484             :              n - 1,
     485             :              i,
     486             :              TEXT_AXIS);
     487         842 :       if (v[i].adj.v == ABOVE_ADJUST)
     488          26 :         printf("-.5v");
     489         816 :       else if (v[i].adj.v == BELOW_ADJUST)
     490          26 :         printf("+.5v");
     491         842 :       putchar('\'');
     492         842 :       fputs(v[i].text, stdout);
     493         842 :       fputs("\n.sp -1\n", stdout);
     494             :     }
     495         777 :   if (rotate_flag)
     496           0 :     printf(".if \\n(" GROPS_REG " \\{\\\n"
     497             :            "\\X'ps: exec grestore'\n.sp -1\n"
     498             :            ".\\}\n");
     499         777 : }
     500             : 
     501        4477 : void troff_output::line_thickness(double p)
     502             : {
     503        4477 :   if (p < 0.0)
     504        4274 :     p = RELATIVE_THICKNESS;
     505        4477 :   if (driver_extension_flag && p != last_line_thickness) {
     506         390 :     printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
     507         390 :     last_line_thickness = p;
     508             :   }
     509        4477 : }
     510             : 
     511         386 : void troff_output::set_fill(double f)
     512             : {
     513         386 :   if (driver_extension_flag && f != last_fill) {
     514             :     // \D'Fg ...' emits a node only in compatibility mode,
     515             :     // thus we add a dummy node
     516         127 :     printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f);
     517         127 :     last_fill = f;
     518             :   }
     519         386 :   if (last_filled) {
     520           0 :     free(last_filled);
     521           0 :     last_filled = 0;
     522           0 :     printf(".fcolor\n");
     523             :   }
     524         386 : }
     525             : 
     526        1733 : void troff_output::set_color(char *color_fill, char *color_outlined)
     527             : {
     528        1733 :   if (driver_extension_flag) {
     529        1733 :     if (last_filled || last_outlined) {
     530          26 :       reset_color();
     531             :     }
     532             :     // .gcolor and .fcolor emit a node in compatibility mode only,
     533             :     // but that won't work anyway
     534        1733 :     if (color_fill) {
     535          42 :       printf(".fcolor %s\n", color_fill);
     536          42 :       last_filled = strsave(color_fill);
     537             :     }
     538        1733 :     if (color_outlined) {
     539          78 :       printf(".gcolor %s\n", color_outlined);
     540          78 :       last_outlined = strsave(color_outlined);
     541             :     }
     542             :   }
     543        1733 : }
     544             : 
     545        1685 : void troff_output::reset_color()
     546             : {
     547        1685 :   if (driver_extension_flag) {
     548        1685 :     if (last_filled) {
     549          42 :       printf(".fcolor\n");
     550          42 :       free(last_filled);
     551          42 :       last_filled = 0;
     552             :     }
     553        1685 :     if (last_outlined) {
     554          78 :       printf(".gcolor\n");
     555          78 :       free(last_outlined);
     556          78 :       last_outlined = 0;
     557             :     }
     558             :   }
     559        1685 : }
     560             : 
     561         679 : char *troff_output::get_last_filled()
     562             : {
     563         679 :   return last_filled;
     564             : }
     565             : 
     566           0 : char *troff_output::get_outline_color()
     567             : {
     568           0 :   return last_outlined;
     569             : }
     570             : 
     571             : const double DOT_AXIS = .044;
     572             : 
     573        2340 : void troff_output::dot(const position &cent, const line_type &lt)
     574             : {
     575        2340 :   if (driver_extension_flag) {
     576        2340 :     line_thickness(lt.thickness);
     577        2340 :     simple_line(cent, cent);
     578             :   }
     579             :   else {
     580           0 :     position c = transform(cent);
     581           0 :     printf("\\h'%.3fi-(\\w'.'u/2u)'"
     582             :            "\\v'%.3fi+%.2fm'"
     583             :            ".\n.sp -1\n",
     584             :            c.x,
     585             :            c.y,
     586             :            DOT_AXIS);
     587             :   }
     588        2340 : }
     589             : 
     590             : // We might consider putting this in libgroff.  We treat null pointers
     591             : // like NaNs: they are incommensurable even with themselves.
     592        1255 : bool strsame(const char *s, const char *t)
     593             : {
     594        1255 :   if ((s == 0 /* nullptr */) || (t == 0 /* nullptr */))
     595          48 :     return false;
     596        1207 :   return (strcmp(s, t) == 0);
     597             : }
     598             : 
     599        1255 : void troff_output::set_location(const char *s, int n)
     600             : {
     601        1255 :   assert(s != 0 /* nullptr */);
     602        1255 :   bool update_file_name = false;
     603        1255 :   if (s != 0 /* nullptr */) {
     604        1255 :     if (!strsame(s, last_filename)) {
     605          55 :       char *lfn = strdup(s);
     606          55 :       if (0 /* nullptr */ == lfn)
     607           0 :         fatal("memory allocation failure while copying file name");
     608          55 :       if (last_filename != 0 /* nullptr */)
     609           7 :         free(const_cast<char *>(last_filename));
     610          55 :       last_filename = lfn;
     611          55 :       update_file_name = true;
     612             :     }
     613             :   }
     614        1255 :   if (update_file_name)
     615          55 :     printf(".lf %d %s%s\n", current_lineno,
     616          55 :         ('"' == current_filename[0]) ? "" : "\"", current_filename);
     617             :   else
     618        1200 :     printf(".lf %d\n", n);
     619        1255 : }
     620             : 
     621             : // Local Variables:
     622             : // fill-column: 72
     623             : // mode: C++
     624             : // End:
     625             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14