LCOV - code coverage report
Current view: top level - libs/libdriver - printer.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 96 121 79.3 %
Date: 2026-01-16 17:51:41 Functions: 14 18 77.8 %
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
       7             :    under the terms of the GNU General Public License as published by
       8             :    the Free 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
      12             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :    General Public License 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             : 
      20             : #ifdef HAVE_CONFIG_H
      21             : #include <config.h>
      22             : #endif
      23             : 
      24             : #include <assert.h>
      25             : #include <errno.h> // EINVAL, errno
      26             : #include <stdio.h> // clearerr(), ferror(), fflush(), stdout
      27             : #include <string.h> // strcmp()
      28             : 
      29             : #include "driver.h"
      30             : 
      31             : /* If we are sending output to an onscreen pager (as is the normal case
      32             :    when reading man pages), then we may get an error state on the output
      33             :    stream, if the user does not read all the way to the end.
      34             : 
      35             :    We normally expect to catch this, and clean up the error context,
      36             :    when the pager exits, because we should get, and handle, a SIGPIPE.
      37             : 
      38             :    However ...
      39             : */
      40             : 
      41             : #if (defined(_MSC_VER) || defined(_WIN32)) \
      42             :     && !defined(__CYGWIN__) && !defined(_UWIN)
      43             : 
      44             :   /* Native MS-Windows doesn't know about SIGPIPE, so we cannot detect
      45             :      the early exit from the pager, and therefore, cannot clean up the
      46             :      error context; thus we use the following static function to
      47             :      identify this particular error context, and so suppress unwanted
      48             :      diagnostics.
      49             :   */
      50             : 
      51             :   static int
      52             :   check_for_output_error (FILE* stream)
      53             :   {
      54             :     /* First, clean up any prior error context on the output stream */
      55             :     if (ferror (stream))
      56             :       clearerr (stream);
      57             :     /* Clear errno, in case clearerr() and fflush() don't */
      58             :     errno = 0;
      59             :     /* Flush the output stream, so we can capture any error context,
      60             :        other than the specific case we wish to suppress.
      61             : 
      62             :        Microsoft doesn't document it, but the error code for the
      63             :        specific context we are trying to suppress seems to be EINVAL --
      64             :        a strange choice, since it is not normally associated with
      65             :        fflush(); of course, it *should* be EPIPE, but this *definitely*
      66             :        is not used, and *is* so documented.
      67             :     */
      68             :     return ((fflush(stream) < 0) && (errno != EINVAL));
      69             :   }
      70             : 
      71             : #else
      72             : 
      73             :   /* For other systems, we simply assume that *any* output error context
      74             :      is to be reported.
      75             :   */
      76             : # define check_for_output_error(stream) ferror(stream) || fflush(stream) < 0
      77             : 
      78             : #endif
      79             : 
      80             : 
      81        1464 : font_pointer_list::font_pointer_list(font *f, font_pointer_list *fp)
      82        1464 : : p(f), next(fp)
      83             : {
      84        1464 : }
      85             : 
      86        1004 : printer::printer()
      87        1004 : : font_list(0 /* nullptr */), font_table(0 /* nullptr */), nfonts(0)
      88             : {
      89        1004 : }
      90             : 
      91        2008 : printer::~printer()
      92             : {
      93        1004 :   delete[] font_table;
      94        2468 :   while (font_list) {
      95        1464 :     font_pointer_list *tem = font_list;
      96        1464 :     font_list = font_list->next;
      97        1464 :     delete tem->p;
      98        1464 :     delete tem;
      99             :   }
     100        1004 :   if (check_for_output_error(stdout))
     101           0 :     fatal("output error");
     102        1004 : }
     103             : 
     104        2374 : void printer::load_font(int n, const char *nm)
     105             : {
     106        2374 :   assert(n >= 0);
     107        2374 :   if (n >= nfonts) {
     108         989 :     if (0 == nfonts) {
     109         975 :       nfonts = 10;
     110         975 :       if (nfonts <= n)
     111          15 :         nfonts = n + 1;
     112         975 :       font_table = new font *[nfonts];
     113       11117 :       for (int i = 0; i < nfonts; i++)
     114       10142 :         font_table[i] = 0 /* nullptr */;
     115             :     }
     116             :     else {
     117          14 :       font **old_font_table = font_table;
     118          14 :       int old_nfonts = nfonts;
     119          14 :       nfonts *= 2;
     120          14 :       if (n >= nfonts)
     121           3 :         nfonts = n + 1;
     122          14 :       font_table = new font *[nfonts];
     123             :       int i;
     124         437 :       for (i = 0; i < old_nfonts; i++)
     125         423 :         font_table[i] = old_font_table[i];
     126         480 :       for (i = old_nfonts; i < nfonts; i++)
     127         466 :         font_table[i] = 0 /* nullptr */;
     128          14 :       delete[] old_font_table;
     129             :     }
     130             :   }
     131        2374 :   font *f = find_font(nm);
     132        2374 :   font_table[n] = f;
     133        2374 : }
     134             : 
     135        2374 : font *printer::find_font(const char *nm)
     136             : {
     137        9130 :   for (font_pointer_list *p = font_list; p; p = p->next)
     138        7666 :     if (strcmp(p->p->get_filename(), nm) == 0)
     139         910 :       return p->p;
     140        1464 :   font *f = make_font(nm);
     141        1464 :   if (0 /* nullptr */ == f)
     142           0 :     fatal("cannot find font '%1'", nm);
     143        1464 :   font_list = new font_pointer_list(f, font_list);
     144        1464 :   return f;
     145             : }
     146             : 
     147           0 : font *printer::make_font(const char *nm)
     148             : {
     149           0 :   return font::load_font(nm);
     150             : }
     151             : 
     152       29491 : void printer::end_of_line()
     153             : {
     154       29491 : }
     155             : 
     156           0 : void printer::special(char *, const environment *, char)
     157             : {
     158           0 : }
     159             : 
     160        5877 : void printer::devtag(char *, const environment *, char)
     161             : {
     162        5877 : }
     163             : 
     164             : // TODO: 1st and 3rd args should be `const`.
     165           0 : void printer::draw(int, int *, int, const environment *)
     166             : {
     167           0 : }
     168             : 
     169       47439 : void printer::change_color(const environment * const)
     170             : {
     171       47439 : }
     172             : 
     173       65278 : void printer::change_fill_color(const environment * const)
     174             : {
     175       65278 : }
     176             : 
     177     1420823 : void printer::set_ascii_char(unsigned char c, const environment *env,
     178             :                              int *widthp)
     179             : {
     180             :   char  buf[2];
     181             :   int   w;
     182             :   font *f;
     183             : 
     184     1420823 :   buf[0] = c;
     185     1420823 :   buf[1] = '\0';
     186             : 
     187     1420823 :   glyph *g = set_char_and_width(buf, env, &w, &f);
     188             : 
     189     1420823 :   if (g != UNDEFINED_GLYPH) {
     190     1420822 :     set_char(g, f, env, w, 0 /* nullptr */);
     191     1420822 :     if (widthp != 0 /* nullptr */)
     192     1420606 :       *widthp = w;
     193             :   }
     194     1420823 : }
     195             : 
     196       16637 : void printer::set_special_char(const char *nm, const environment *env,
     197             :                                int *widthp)
     198             : {
     199             :   font *f;
     200             :   int w;
     201       16637 :   glyph *g = set_char_and_width(nm, env, &w, &f);
     202       16637 :   if (g != UNDEFINED_GLYPH) {
     203       16637 :     set_char(g, f, env, w, nm);
     204       16637 :     if (widthp != 0 /* nullptr */)
     205           0 :       *widthp = w;
     206             :   }
     207       16637 : }
     208             : 
     209     1437460 : glyph *printer::set_char_and_width(const char *nm,
     210             :                                    const environment *env, int *widthp,
     211             :                                    font **f)
     212             : {
     213     1437460 :   glyph *g = name_to_glyph(nm);
     214     1437460 :   int fn = env->fontno;
     215     1437460 :   if ((fn < 0) || (fn >= nfonts)) {
     216           1 :     error("invalid font position '%1'", fn);
     217           1 :     return UNDEFINED_GLYPH;
     218             :   }
     219     1437459 :   *f = font_table[fn];
     220     1437459 :   if (0 /* nullptr */ == *f) {
     221           0 :     error("no font mounted at position %1", fn);
     222           0 :     return UNDEFINED_GLYPH;
     223             :   }
     224     1437459 :   if (!(*f)->contains(g)) {
     225           0 :     if ((nm[0] != '\0') && ('\0' == nm[1]))
     226           0 :       error("font description file '%1' lacks glyph for ordinary"
     227           0 :             " character '%2'", (*f)->get_filename(), nm[0]);
     228             :     else
     229           0 :       error("font description file '%1' lacks glyph for special"
     230           0 :             " character '%2'", (*f)->get_filename(), nm);
     231           0 :     return UNDEFINED_GLYPH;
     232             :   }
     233     1437459 :   int w = (*f)->get_width(g, env->size);
     234     1437459 :   if (widthp != 0 /* nullptr */)
     235     1437459 :     *widthp = w;
     236     1437459 :   return g;
     237             : }
     238             : 
     239       21138 : void printer::set_numbered_char(int num, const environment *env, int
     240             :                                 *widthp)
     241             : {
     242       21138 :   glyph *g = number_to_glyph(num);
     243       21138 :   int fn = env->fontno;
     244       21138 :   if ((fn < 0) || (fn >= nfonts)) {
     245           0 :     error("invalid font position '%1'", fn);
     246           0 :     return;
     247             :   }
     248       21138 :   font *f = font_table[fn];
     249       21138 :   if (0 /* nullptr */ == f) {
     250           0 :     error("no font mounted at position %1", fn);
     251           0 :     return;
     252             :   }
     253       21138 :   if (!f->contains(g)) {
     254           0 :     error("font '%1' has no glyph at index %2", f->get_filename(), num);
     255           0 :     return;
     256             :   }
     257       21138 :   int w = f->get_width(g, env->size);
     258       21138 :   if (widthp != 0 /* nullptr */)
     259           0 :     *widthp = w;
     260       21138 :   set_char(g, f, env, w, 0 /* nullptr */);
     261             : }
     262             : 
     263        5210 : font *printer::get_font_from_index(int fontno)
     264             : {
     265        5210 :   if ((fontno >= 0) && (fontno < nfonts))
     266        5210 :     return font_table[fontno];
     267             :   else
     268           0 :     return 0 /* nullptr */;
     269             : }
     270             : 
     271             : // Local Variables:
     272             : // fill-column: 72
     273             : // mode: C++
     274             : // End:
     275             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14