[self-follow-up]

At 2026-07-24T18:16:20-0500, G. Branden Robinson wrote:
> At 2026-07-25T01:14:46+0200, Bruno Haible wrote:
> > I tried building the groff-1.25.0.rc1 with clang's ASAN.
> > The next finding:
> > 
> >   GROFF    doc/webpage.ps
> > =================================================================
> > ==850822==ERROR: AddressSanitizer: heap-buffer-overflow on address 
> > 0x7667d11e0291 at pc 0x639af6237a11 bp 0x7ffd0e103b90 sp 0x7ffd0e103b88
> > READ of size 1 at 0x7667d11e0291 thread T0
> >     #0 0x639af6237a10 in ps_printer::do_import(char*, environment const*) 
> > /build/groff-1.25.0.rc1/build-64-clang/../src/devices/grops/ps.cpp:1870:11
> 
> Yup.  I'm on it.
> 
> https://savannah.gnu.org/bugs/index.php?68559

Actually that ticket isn't squarely on point, but it's adjacent to the
problem.  Once I know I have a fix, I'll be filing another ticket.

I'm building groff 1.24.1 with Clang/ASAN on my slow Termux tablet right
now because I suspect this bug to be an old one.

I thrashed around in "ps.cpp" quite a bit since then, but mostly
cosmetically, modernizing the code style, or making diagnostic messages
more intelligible.

diff --git a/src/devices/grops/ps.cpp b/src/devices/grops/ps.cpp
index 7f8145902..755e34450 100644
--- a/src/devices/grops/ps.cpp
+++ b/src/devices/grops/ps.cpp
@@ -1,5 +1,8 @@
-/* Copyright 1989-2023 Free Software Foundation, Inc.
-     Written by James Clark ([email protected])
+/* Copyright 1989-2003 Free Software Foundation, Inc.
+                  2023 TANAKA Takuji
+                  2026 G. Branden Robinson
+
+Written by James Clark ([email protected])

 This file is part of groff, the GNU roff typesetting system.

@@ -34,25 +37,35 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>. */
 #include <stdint.h> // uint16_t
 #include <stdio.h> // EOF, FILE, fclose(), fgets(), fileno(), fseek(),
                   // getc(), SEEK_SET, setbuf(), stderr, stdout
-#include <stdlib.h> // exit(), EXIT_SUCCESS, putenv(), strtol()
+#include <stdlib.h> // exit(), EXIT_SUCCESS, setenv(), strtol()
 #include <string.h> // strchr(), strcmp(), strcpy(), strerror(),
                    // strlen(), strncmp(), strstr(), strtok()
 #include <time.h> // asctime()

+// GNU extensions to C standard library
 #include <getopt.h> // getopt_long()

+#include <new> // std::bad_alloc
+
+// operating system services
 // needed for SET_BINARY()
 #include "posix.h"
 #include "nonposix.h"

+// libgroff
+#include "symbol.h" // prerequisite of color.h
+#include "color.h"
+#include "cset.h" // csalpha(), csprint(), csspace()
+#include "curtime.h" // current_time()
+#include "geometry.h" // adjust_arc_center()
 #include "lib.h" // PI
-
-#include "cset.h"
-#include "curtime.h"
-#include "driver.h"
-#include "paper.h"
 #include "stringclass.h"

+// libdriver
+#include "driver.h" // interpret_troff_output_file()
+#include "printer.h" // environment, printer
+
+// grops
 #include "ps.h"

 extern "C" const char *Version_string;
@@ -85,12 +98,12 @@ const char *const dict_name = "grops";
 const char *const defs_dict_name = "DEFS";
 const int DEFS_DICT_SPARE = 50;

-double degrees(double r)
+static double degrees(double r)
 {
-  return r*180.0/PI;
+  return (r * 180.0) / PI;
 }

-double radians(double d)
+static double radians(double d)
 {
   return (d * PI) / 180.0;
 }
@@ -99,13 +112,14 @@ double radians(double d)
 // PostScript file using \nnn, so we really want the character to be
 // less than 0200.

-inline int is_ascii(char c)
+static inline bool is_ascii(char c)
 {
-  return (unsigned char)c < 0200;
+  return (unsigned char)(c) < 0200;
 }

 ps_output::ps_output(FILE *f, int n)
-: fp(f), col(0), max_line_length(n), need_space(0), fixed_point(0)
+: fp(f), col(0), max_line_length(n), is_space_needed(false),
+  fixed_point(0)
 {
 }

@@ -129,14 +143,14 @@ ps_output &ps_output::end_line()
   if (col != 0) {
     putc('\n', fp);
     col = 0;
-    need_space = 0;
+    is_space_needed = false;
   }
   return *this;
 }

 ps_output &ps_output::special(const char *s)
 {
-  if (s == 0 || *s == '\0')
+  if ((0 /* nullptr */ == s) || ('\0' == *s))
     return *this;
   if (col != 0) {
     putc('\n', fp);
@@ -145,7 +159,7 @@ ps_output &ps_output::special(const char *s)
   fputs(s, fp);
   if (strchr(s, '\0')[-1] != '\n')
     putc('\n', fp);
-  need_space = 0;
+  is_space_needed = false;
   return *this;
 }

@@ -158,7 +172,7 @@ ps_output &ps_output::simple_comment(const char *s)
   fputs(s, fp);
   putc('\n', fp);
   col = 0;
-  need_space = 0;
+  is_space_needed = false;
   return *this;
 }

@@ -179,7 +193,7 @@ ps_output &ps_output::end_comment()
     putc('\n', fp);
     col = 0;
   }
-  need_space = 0;
+  is_space_needed = false;
   return *this;
 }

@@ -212,7 +226,7 @@ ps_output &ps_output::put_delimiter(char c)
   }
   putc(c, fp);
   col++;
-  need_space = 0;
+  is_space_needed = false;
   return *this;
 }

@@ -308,27 +322,28 @@ ps_output &ps_output::put_string(const uint16_t *s, 
size_t n,
     putc(')', fp);
     col++;
   }
-  need_space = 0;
+  is_space_needed = false;
   return *this;
 }

 ps_output &ps_output::put_number(int n)
 {
-  char buf[1 + INT_DIGITS + 1];
+  char buf[1 /* sign */ + INT_DIGITS + 1 /* '\0' */];
   sprintf(buf, "%d", n);
   size_t len = strlen(buf);
-  if ((col > 0) && ((col + len + need_space) > max_line_length)) {
+  if ((col > 0)
+      && ((col + len + int(is_space_needed)) > max_line_length)) {
     putc('\n', fp);
     col = 0;
-    need_space = 0;
+    is_space_needed = false;
   }
-  if (need_space) {
+  if (is_space_needed) {
     putc(' ', fp);
     col++;
   }
   fputs(buf, fp);
   col += len;
-  need_space = 1;
+  is_space_needed = true;
   return *this;
 }

@@ -336,18 +351,19 @@ ps_output &ps_output::put_fix_number(int i)
 {
   const char *p = if_to_a(i, fixed_point);
   size_t len = strlen(p);
-  if ((col > 0) && ((col + len + need_space) > max_line_length)) {
+  if ((col > 0)
+      && ((col + len + int(is_space_needed)) > max_line_length)) {
     putc('\n', fp);
     col = 0;
-    need_space = 0;
+    is_space_needed = false;
   }
-  if (need_space) {
+  if (is_space_needed) {
     putc(' ', fp);
     col++;
   }
   fputs(p, fp);
   col += len;
-  need_space = 1;
+  is_space_needed = true;
   return *this;
 }

@@ -361,36 +377,38 @@ ps_output &ps_output::put_float(double d)
   if (buf[last] == '.')
     last--;
   buf[++last] = '\0';
-  if ((col > 0) && ((col + last + need_space) > max_line_length)) {
+  if ((col > 0)
+      && ((col + last + int(is_space_needed)) > max_line_length)) {
     putc('\n', fp);
     col = 0;
-    need_space = 0;
+    is_space_needed = false;
   }
-  if (need_space) {
+  if (is_space_needed) {
     putc(' ', fp);
     col++;
   }
   fputs(buf, fp);
   col += last;
-  need_space = 1;
+  is_space_needed = true;
   return *this;
 }

 ps_output &ps_output::put_symbol(const char *s)
 {
   size_t len = strlen(s);
-  if ((col > 0) && ((col + len + need_space) > max_line_length)) {
+  if ((col > 0)
+      && ((col + len + int(is_space_needed)) > max_line_length)) {
     putc('\n', fp);
     col = 0;
-    need_space = 0;
+    is_space_needed = false;
   }
-  if (need_space) {
+  if (is_space_needed) {
     putc(' ', fp);
     col++;
   }
   fputs(s, fp);
   col += len;
-  need_space = 1;
+  is_space_needed = true;
   return *this;
 }

@@ -399,18 +417,19 @@ ps_output &ps_output::put_color(unsigned int c)
   char buf[128];
   sprintf(buf, "%.3g", double(c) / double(color::MAX_COLOR_VAL));
   size_t len = strlen(buf);
-  if ((col > 0) && ((col + len + need_space) > max_line_length)) {
+  if ((col > 0)
+      && ((col + len + int(is_space_needed)) > max_line_length)) {
     putc('\n', fp);
     col = 0;
-    need_space = 0;
+    is_space_needed = false;
   }
-  if (need_space) {
+  if (is_space_needed) {
     putc(' ', fp);
     col++;
   }
   fputs(buf, fp);
   col += len;
-  need_space = 1;
+  is_space_needed = true;
   return *this;
 }

@@ -424,7 +443,7 @@ ps_output &ps_output::put_literal_symbol(const char *s)
   putc('/', fp);
   fputs(s, fp);
   col += len + 1;
-  need_space = 1;
+  is_space_needed = true;
   return *this;
 }

@@ -438,22 +457,30 @@ public:
   void handle_unknown_font_command(const char * /* command */,
                                   const char * /* arg */,
                                   const char * /* fn */,
-                                  int lineno);
+                                  int /* lineno */);
   static ps_font *load_ps_font(const char * /* s */);
 };

 ps_font *ps_font::load_ps_font(const char *s)
 {
-  ps_font *f = new ps_font(s);
+  ps_font *f = 0 /* nullptr */;
+  try {
+    f = new ps_font(s);
+  }
+  catch (const std::bad_alloc &e) {
+    fatal("cannot allocate %1 bytes for storage of font description"
+         " for PostScript font '%2'", sizeof(ps_font), s);
+  }
   if (!f->load()) {
     delete f;
-    return 0;
+    return 0 /* nullptr */;
   }
   return f;
 }

 ps_font::ps_font(const char *nm)
-: font(nm), encoding_index(-1), encoding(0), reencoded_name(0)
+: font(nm), encoding_index(-1), encoding(0 /* nullptr */),
+  reencoded_name(0 /* nullptr */)
 {
 }

@@ -463,25 +490,29 @@ ps_font::~ps_font()
   delete[] reencoded_name;
 }

-void ps_font::handle_unknown_font_command(const char *command, const char *arg,
+void ps_font::handle_unknown_font_command(const char *command,
+                                         const char *arg,
                                          const char *fn, int lineno)
 {
   if (strcmp(command, "encoding") == 0) {
-    if (arg == 0)
+    if (0 == arg)
       error_with_file_and_line(fn, lineno,
-                              "'encoding' command requires an argument");
+                              "device description directive 'encoding'"
+                              " requires an argument");
     else
       encoding = strsave(arg);
   }
 }

-static void handle_unknown_desc_command(const char *command, const char *arg,
+static void handle_unknown_desc_command(const char *command,
+                                       const char *arg,
                                        const char *fn, int lineno)
 {
   if (strcmp(command, "broken") == 0) {
-    if (arg == 0)
+    if (0 /* nullptr */ == arg)
       error_with_file_and_line(fn, lineno,
-                              "'broken' command requires an argument");
+                              "device description directive 'broken'"
+                              " requires an argument");
     else if (!bflag)
       broken_flags = atoi(arg);
   }
@@ -536,11 +567,11 @@ style::style(font *p, subencoding *s, int sz, int h, int 
sl)

 int style::operator==(const style &s) const
 {
-  return (f == s.f
-         && sub == s.sub
-         && point_size == s.point_size
-         && height == s.height
-         && slant == s.slant);
+  return ((f == s.f)
+         && (sub == s.sub)
+         && (point_size == s.point_size)
+         && (height == s.height)
+         && (slant == s.slant));
 }

 int style::operator!=(const style &s) const
@@ -654,28 +685,28 @@ ps_printer::ps_printer(double pl)
   if (linewidth < 0)
     linewidth = DEFAULT_LINEWIDTH;
   if (font::hor != 1)
-    fatal("device horizontal motion quantum must be 1, got %1",
-       font::hor);
+    fatal("device description horizontal motion quantum directive"
+         " must be 1; got %1", font::hor);
   if (font::vert != 1)
-    fatal("device vertical motion quantum must be 1, got %1",
-       font::vert);
-  if (font::res % (font::sizescale*72) != 0)
-    fatal("device resolution must be a multiple of 72*'sizescale', got"
-       " %1 ('sizescale'=%2)", font::res, font::sizescale);
+    fatal("device description vertical motion quantum directive"
+         " must be 1; got %1", font::vert);
+  if (font::res % (font::sizescale * 72) != 0)
+    fatal("device resolution must be a multiple of 72 * 'sizescale',"
+         " got %1 ('sizescale'=%2)", font::res, font::sizescale);
   int r = font::res;
   int point = 0;
-  while (r % 10 == 0) {
+  while ((r % 10) == 0) {
     r /= 10;
     point++;
   }
   res = r;
   out.set_fixed_point(point);
   space_glyph = name_to_glyph("space");
-  if (pl == 0)
+  if (0.0 == pl)
     paper_length = font::paperlength;
   else
     paper_length = int(pl * font::res + 0.5);
-  if (paper_length == 0)
+  if (0 == paper_length)
     paper_length = 11 * font::res;
   equalise_spaces = font::res >= 72000;
 }
@@ -689,7 +720,8 @@ int ps_printer::set_encoding_index(ps_font *f)
       char *encoding = (static_cast<ps_font *>(p->p))->encoding;
       int encoding_index
        = (static_cast<ps_font *>(p->p))->encoding_index;
-      if ((encoding != 0 /* nullptr */) && encoding_index >= 0
+      if ((encoding != 0 /* nullptr */)
+         && (encoding_index >= 0)
          && strcmp(f->encoding, encoding) == 0) {
        return f->encoding_index = encoding_index;
       }
@@ -719,15 +751,24 @@ subencoding *ps_printer::set_subencoding(font *f, glyph 
*g,
   code[0] = idx % 256;
   code[1] = 0;
   unsigned int num = idx >> 8;
-  if (num == 0)
+  if (0U == num)
     return 0 /* nullptr */;
   subencoding *p = 0 /* nullptr */;
   for (p = subencodings; p; p = p->next)
     if ((p->p == f) && (p->num == num))
       break;
-  if (0 /* nullptr */ == p)
-    p = subencodings = new subencoding(f, num, next_subencoding_index++,
-                                      subencodings);
+  if (0 /* nullptr */ == p) {
+    try {
+      subencodings = new subencoding(f, num, next_subencoding_index++,
+                                    subencodings);
+    }
+    catch (const std::bad_alloc &e) {
+      fatal("cannot allocate %1 bytes for storage of subencoding %2 for"
+           " font '%3'", sizeof(subencoding), next_subencoding_index,
+           psname);
+    }
+    p = subencodings;
+  }
   p->glyphs[*code] = f->get_special_device_encoding(g);
   return p;
 }
@@ -735,8 +776,16 @@ subencoding *ps_printer::set_subencoding(font *f, glyph *g,
 char *ps_printer::get_subfont(subencoding *sub, const char *stem)
 {
   assert(sub != 0 /* nullptr */);
+  size_t amt = strlen(stem) + 2 /* "@@" */ + INT_DIGITS + 1 /* '\0' */;
   if (!sub->subfont) {
-    char *tem = new char[strlen(stem) + 2 + INT_DIGITS + 1];
+    char *tem = 0 /* nullptr */;
+    try {
+      tem = new char[amt];
+    }
+    catch (const std::bad_alloc &e) {
+      fatal("cannot allocate %1 bytes for storage of subfont '%2@@%3",
+           amt, stem, sub->idx);
+    }
     sprintf(tem, "%s@@%d", stem, sub->idx);
     sub->subfont = tem;
   }
@@ -753,7 +802,8 @@ void ps_printer::set_char(glyph *g, font *f, const 
environment *env,
   style sty(f, sub, env->size, env->height, env->slant);
   if (sty.slant != 0) {
     if (sty.slant > 80 || sty.slant < -80) {
-      error("silly slant '%1' degrees", sty.slant);
+      error("glyph slant of %1 degrees is out of range; using 0",
+           sty.slant);
       sty.slant = 0;
     }
   }
@@ -769,7 +819,7 @@ void ps_printer::set_char(glyph *g, font *f, const 
environment *env,
        sbuf_end_hpos += w + sbuf_kern;
        return;
       }
-      if ((sbuf_len == 1) && (sbuf_kern == 0)) {
+      if ((1 == sbuf_len) && (0 == sbuf_kern)) {
        sbuf_kern = env->hpos - sbuf_end_hpos;
        sbuf_end_hpos = env->hpos + sbuf_kern + w;
        sbuf[sbuf_len++] = code[0];
@@ -779,8 +829,10 @@ void ps_printer::set_char(glyph *g, font *f, const 
environment *env,
       }
       /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
         starting a new string. */
-      if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
-         && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
+      if ((sbuf_len < (SBUF_SIZE - 1))
+         && (env->hpos >= sbuf_end_hpos)
+         && ((0 == sbuf_kern)
+             || ((sbuf_end_hpos - sbuf_kern) != env->hpos))) {
        if (sbuf_space_code < 0) {
          if (f->contains(space_glyph) && !sub) {
            sbuf_space_code = f->get_code(space_glyph);
@@ -796,7 +848,8 @@ void ps_printer::set_char(glyph *g, font *f, const 
environment *env,
        }
        else {
          int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
-         if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
+         if (0 == diff
+             || (equalise_spaces && ((1 == diff) || (-1 == diff)))) {
            sbuf_end_hpos = env->hpos + w + sbuf_kern;
            sbuf[sbuf_len++] = sbuf_space_code;
            sbuf[sbuf_len++] = code[0];
@@ -869,11 +922,22 @@ void ps_printer::define_encoding(const char *encoding,
     if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
       char *q = strtok(0, WS);
       int n = 0;               // pacify compiler
-      if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
+      if ((0 /* nullptr */ == q)
+         || (sscanf(q, "%d", &n) != 1)
+         || (n < 0)
+         || (n >= 256))
        fatal_with_file_and_line(path, lineno, "invalid encoding file:"
            " expected integer in range 0-255 as second word on line,"
            " got '%1'", q);
-      vec[n] = new char[strlen(p) + 1];
+      size_t amount = strlen(p) + 1 /* '\0' */;
+      vec[n] = 0 /* nullptr */;
+      try {
+       vec[n] = new char[amount];
+      }
+      catch (const std::bad_alloc &e) {
+       fatal("cannot allocate %1 bytes for storage of encoding vector"
+             " '%2'", amount, p);
+      }
       strcpy(vec[n], p);
     }
     lineno++;
@@ -882,7 +946,7 @@ void ps_printer::define_encoding(const char *encoding,
   out.put_literal_symbol(make_encoding_name(encoding_index))
      .put_delimiter('[');
   for (i = 0; i < 256; i++) {
-    if (vec[i] == 0)
+    if (0 /* nullptr */ == vec[i])
       out.put_literal_symbol(".notdef");
     else {
       out.put_literal_symbol(vec[i]);
@@ -904,9 +968,16 @@ void ps_printer::reencode_font(ps_font *f)

 void ps_printer::encode_fonts()
 {
-  if (next_encoding_index == 0)
+  if (0 == next_encoding_index)
     return;
-  char *done_encoding = new char[next_encoding_index];
+  char *done_encoding = 0 /* nullptr */;
+  try {
+    done_encoding = new char[next_encoding_index];
+  }
+  catch (const std::bad_alloc &e) {
+    fatal("cannot allocate %1 bytes for storage of encoding index",
+         next_encoding_index);
+  }
   for (int i = 0; i < next_encoding_index; i++)
     done_encoding[i] = 0;
   for (font_pointer_list *f = font_list; f; f = f->next) {
@@ -957,12 +1028,21 @@ void ps_printer::set_style(const style &sty)
     fatal("cannot set style; font description file '%1' lacks an"
          " 'internalname' directive", sty.f->get_filename());
   char *encoding = ((ps_font *)sty.f)->encoding;
-  if (sty.sub == 0) {
+  if (0 /* nullptr */ == sty.sub) {
     if (encoding != 0 /* nullptr */) {
       char *s = ((ps_font *)sty.f)->reencoded_name;
       if (0 /* nullptr */ == s) {
        int ei = set_encoding_index((ps_font *)sty.f);
-       char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
+       size_t amount = strlen(psname) + 1 /* '@' */ + INT_DIGITS
+                       + 1 /* '\0' */;
+       char *tem = 0 /* nullptr */;
+       try {
+         tem = new char[amount];
+       }
+       catch (const std::bad_alloc &e) {
+         fatal("cannot allocate %1 bytes for storage of PostScript"
+               " internal font name '%2@%3'", amount, psname, ei);
+       }
        sprintf(tem, "%s@%d", psname, ei);
        psname = tem;
        ((ps_font *)sty.f)->reencoded_name = tem;
@@ -973,11 +1053,12 @@ void ps_printer::set_style(const style &sty)
   }
   else
     psname = get_subfont(sty.sub, psname);
-  out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
+  out.put_fix_number((font::res / (72 * font::sizescale))
+                    * sty.point_size);
   if (sty.height != 0 || sty.slant != 0) {
-    int h = sty.height == 0 ? sty.point_size : sty.height;
-    h *= font::res/(72*font::sizescale);
-    int c = int(h*tan(radians(sty.slant)) + .5);
+    int h = (0 == sty.height) ? sty.point_size : sty.height;
+    h *= font::res / (72 * font::sizescale);
+    int c = int(h * tan(radians(sty.slant)) + .5);
     out.put_fix_number(c)
        .put_fix_number(h)
        .put_literal_symbol(psname)
@@ -1051,8 +1132,8 @@ void ps_printer::flush_sbuf()
     RELATIVE_HV,
     ABSOLUTE
     } motion = NONE;
-  int space_flag = 0;
-  if (sbuf_len == 0)
+  bool space_flag = false;
+  if (0 == sbuf_len)
     return;
   if (output_style != sbuf_style) {
     set_style(sbuf_style);
@@ -1078,11 +1159,11 @@ void ps_printer::flush_sbuf()
        set_space_code(sbuf_space_code);
        output_space_code = sbuf_space_code;
       }
-      space_flag = 1;
+      space_flag = true;
       extra_space = sbuf_space_width - w - sbuf_kern;
-      if (sbuf_space_diff_count > sbuf_space_count/2)
+      if (sbuf_space_diff_count > (sbuf_space_count / 2))
        extra_space++;
-      else if (sbuf_space_diff_count < -(sbuf_space_count/2))
+      else if (sbuf_space_diff_count < -(sbuf_space_count / 2))
        extra_space--;
     }
   }
@@ -1101,7 +1182,9 @@ void ps_printer::flush_sbuf()
                          'M', 'N', 'O', 'P',
                          'Q', 'R', 'S', 'T'};
   char sym[2];
-  sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)];
+  sym[0] = command_array[(motion * 4)
+                        + int(space_flag)
+                        + (2 * (sbuf_kern != 0))];
   sym[1] = '\0';
   switch (motion) {
   case NONE:
@@ -1134,7 +1217,10 @@ void ps_printer::set_line_thickness_and_color(const 
environment *env)
   if (line_thickness < 0) {
     if (output_draw_point_size != env->size) {
       // we ought to check for overflow here
-      int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
+      int lw = ((font::res / (72 * font::sizescale))
+                * linewidth
+                * env->size)
+              / 1000;
       out.put_fix_number(lw)
         .put_symbol("LW");
       output_draw_point_size = env->size;
@@ -1174,12 +1260,12 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
   case 'c':
     // troff adds an extra argument to C
     if (np != 1 && !(code == 'C' && np == 2)) {
-      error("1 argument required for circle");
+      error("circle drawing command requires exactly one argument");
       break;
     }
-    out.put_fix_number(env->hpos + p[0]/2)
+    out.put_fix_number(env->hpos + p[0] / 2)
        .put_fix_number(env->vpos)
-       .put_fix_number(p[0]/2)
+       .put_fix_number(p[0] / 2)
        .put_symbol("DC");
     if (fill_flag)
       fill_path(env);
@@ -1190,7 +1276,8 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
     break;
   case 'l':
     if (np != 2) {
-      error("2 arguments required for line");
+      error("line drawing command requires"
+           " exactly two arguments");
       break;
     }
     set_line_thickness_and_color(env);
@@ -1205,12 +1292,13 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
     // fall through
   case 'e':
     if (np != 2) {
-      error("2 arguments required for ellipse");
+      error("ellipse drawing command requires"
+           " exactly two arguments");
       break;
     }
     out.put_fix_number(p[0])
        .put_fix_number(p[1])
-       .put_fix_number(env->hpos + p[0]/2)
+       .put_fix_number(env->hpos + (p[0] / 2))
        .put_fix_number(env->vpos)
        .put_symbol("DE");
     if (fill_flag)
@@ -1226,11 +1314,12 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
   case 'p':
     {
       if (np & 1) {
-       error("even number of arguments required for polygon");
+       error("polygon drawing command requires"
+             " an even number of arguments");
        break;
       }
       if (np == 0) {
-       error("no arguments for polygon");
+       error("polygon drawing command requires arguments");
        break;
       }
       out.put_fix_number(env->hpos)
@@ -1252,18 +1341,19 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
   case '~':
     {
       if (np & 1) {
-       error("even number of arguments required for spline");
+       error("spline drawing command requires"
+             " an even number of arguments");
        break;
       }
       if (np == 0) {
-       error("no arguments for spline");
+       error("spline drawing command requires arguments");
        break;
       }
       out.put_fix_number(env->hpos)
         .put_fix_number(env->vpos)
         .put_symbol("MT");
-      out.put_fix_number(p[0]/2)
-        .put_fix_number(p[1]/2)
+      out.put_fix_number(p[0] / 2)
+        .put_fix_number(p[1] / 2)
         .put_symbol("RL");
       /* tnum/tden should be between 0 and 1; the closer it is to 1
         the tighter the curve will be to the guiding lines; 2/3
@@ -1271,16 +1361,16 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
       const int tnum = 2;
       const int tden = 3;
       for (int i = 0; i < np - 2; i += 2) {
-       out.put_fix_number((p[i]*tnum)/(2*tden))
-          .put_fix_number((p[i + 1]*tnum)/(2*tden))
-          .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
-          .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
-          .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
-          .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
+       out.put_fix_number((p[i] * tnum) / (2 * tden))
+          .put_fix_number((p[i + 1] * tnum) / (2 * tden))
+          .put_fix_number(p[i] / 2 + (p[i + 2] * (tden - tnum)) / (2 * tden))
+          .put_fix_number(p[i + 1] / 2 + (p[i + 3] * (tden - tnum)) / (2 * 
tden))
+          .put_fix_number((p[i] - p[i] / 2) + p[i + 2] / 2)
+          .put_fix_number((p[i + 1] - p[i + 1] / 2) + p[i + 3] / 2)
           .put_symbol("RC");
       }
-      out.put_fix_number(p[np - 2] - p[np - 2]/2)
-        .put_fix_number(p[np - 1] - p[np - 1]/2)
+      out.put_fix_number(p[np - 2] - p[np - 2] / 2)
+        .put_fix_number(p[np - 1] - p[np - 1] / 2)
         .put_symbol("RL");
       set_line_thickness_and_color(env);
       out.put_symbol("ST");
@@ -1289,7 +1379,8 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
   case 'a':
     {
       if (np != 4) {
-       error("4 arguments required for arc");
+       error("arc drawing command requires"
+             " exactly two arguments");
        break;
       }
       set_line_thickness_and_color(env);
@@ -1315,7 +1406,8 @@ void ps_printer::draw(int code, int *p, int np, const 
environment *env)
     else {
       // troff gratuitously adds an extra 0
       if (np != 1 && np != 2) {
-       error("0 or 1 argument required for thickness");
+       error("line thickness drawing command requires"
+             " at most one argument");
        break;
       }
       line_thickness = p[0];
@@ -1346,14 +1438,14 @@ int ps_printer::media_width()
    *  PostScript definition specifies that media matching should be done
    *  within a tolerance of 5 units.
    */
-  return int(user_paper_width ? user_paper_width*72.0 + 0.5
-                             : font::paperwidth*72.0/font::res + 0.5);
+  return int(user_paper_width ? user_paper_width * 72.0 + 0.5
+                             : font::paperwidth * 72.0 / font::res + 0.5);
 }

 int ps_printer::media_height()
 {
-  return int(user_paper_length ? user_paper_length*72.0 + 0.5
-                              : paper_length*72.0/font::res + 0.5);
+  return int(user_paper_length ? user_paper_length * 72.0 + 0.5
+                              : paper_length * 72.0 / font::res + 0.5);
 }

 void ps_printer::media_set()
@@ -1434,7 +1526,7 @@ void ps_printer::end_page(int)
   set_color(&default_color);
   out.put_symbol("EP");
   if (invis_count != 0) {
-    error("missing 'endinvis' command");
+    error("missing device extension command 'endinvis'");
     invis_count = 0;
   }
 }
@@ -1451,7 +1543,7 @@ ps_printer::~ps_printer()
      .put_symbol("end")
      .simple_comment("EOF");
   if (fseek(tempfp, 0L, SEEK_SET) < 0)
-    fatal("unable to seek within temporary file: %1", strerror(errno));
+    fatal("cannot seek within temporary file: %1", strerror(errno));
   fputs("%!PS-Adobe-", stdout);
   fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
   putchar('\n');
@@ -1631,7 +1723,7 @@ void ps_printer::special(char *arg, const environment 
*env, char type)
   for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
     ;
   if (*command == '\0') {
-    error("empty X command ignored");
+    error("ignoring null device extension command");
     return;
   }
   for (size_t i = 0; i < countof(proc_table); i++)
@@ -1642,7 +1734,7 @@ void ps_printer::special(char *arg, const environment 
*env, char type)
       (this->*(proc_table[i].proc))(p, env);
       return;
     }
-  error("X command '%1' not recognised", command);
+  error("device extension command '%1' not recognised", command);
 }

 // A conforming PostScript document must not have lines longer
@@ -1668,11 +1760,11 @@ void ps_printer::do_exec(char *arg, const environment 
*env)
   while (csspace(*arg))
     arg++;
   if (*arg == '\0') {
-    error("missing argument to X exec command");
+    error("device extension command 'exec' requires arguments");
     return;
   }
   if (!check_line_lengths(arg))
-    warning("lines in X exec command should"
+    warning("lines in device extension command 'exec' should"
            " not be more than 255 characters long");
   out.put_fix_number(env->hpos)
      .put_fix_number(env->vpos)
@@ -1693,7 +1785,7 @@ void ps_printer::do_file(char *arg, const environment 
*env)
   while (csspace(*arg))
     arg++;
   if (*arg == '\0') {
-    error("missing argument to X file command");
+    error("device extension command 'file' requires an argument");
     return;
   }
   const char *resource_filename = arg;
@@ -1719,7 +1811,7 @@ void ps_printer::do_def(char *arg, const environment *)
   while (csspace(*arg))
     arg++;
   if (!check_line_lengths(arg))
-    warning("lines in X def command should"
+    warning("lines in device extension command 'def' should"
            " not be more than 255 characters long");
   defs += arg;
   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
@@ -1734,18 +1826,20 @@ void ps_printer::do_mdef(char *arg, const environment *)
   char *p;
   int n = (int)strtol(arg, &p, 10);
   if (p == arg) {
-    error("first argument to X mdef must be an integer");
+    error("device extension command 'mdef' requires"
+         " an integer as its first argument");
     return;
   }
   if (n < 0) {
-    error("out of range argument '%1' to X mdef command", int(n));
+    error("argument '%1' to device extension command 'mdef'"
+         " is out of range", int(n));
     return;
   }
   arg = p;
   while (csspace(*arg))
     arg++;
   if (!check_line_lengths(arg))
-    warning("lines in X mdef command should"
+    warning("lines in device extension command 'mdef' should"
            " not be more than 255 characters long");
   defs += arg;
   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
@@ -1755,10 +1849,12 @@ void ps_printer::do_mdef(char *arg, const environment *)

 void ps_printer::do_import(char *arg, const environment *env)
 {
-  while (*arg == ' ' || *arg == '\n')
+  while ((' ' == *arg) || ('\n' == *arg))
     arg++;
   char *p;
-  for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
+  // Skip validation of first argument, a file name.
+  // XXX: This means we can't import file names with spaces in them.
+  for (p = arg; (*p != '\0') && (*p != ' ') && (*p != '\n'); p++)
     ;
   if (*p != '\0')
     *p++ = '\0';
@@ -1772,21 +1868,30 @@ void ps_printer::do_import(char *arg, const environment 
*env)
     parms[nparms++] = int(n);
     p = end;
   }
-  if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
-    error("scaling units not allowed in arguments for X import command");
+  size_t idx = 0;
+  if (strlen(p) > 1)
+    idx = 1;
+  if (!csdigit(p[idx])
+      && (p[idx] != ' ')
+      && (p[idx] != '\n')
+      && (p[idx] != '\0')) {
+    error("invalid numeric argument(s) '%1'"
+         " to device extension command 'import'", p);
     return;
   }
-  while (*p == ' ' || *p == '\n')
+  while ((' ' == *p) || ('\n' == *p))
     p++;
   if (nparms < 5) {
     if (*p == '\0')
-      error("too few arguments for X import command");
+      error("too few arguments to device extension command 'import'");
     else
-      error("invalid argument '%1' for X import command", p);
+      error("invalid argument '%1'"
+           " to device extension command 'import'", p);
     return;
   }
   if (*p != '\0') {
-    error("superfluous argument '%1' for X import command", p);
+    error("superfluous argument '%1'"
+         " to device extension command 'import'", p);
     return;
   }
   int llx = parms[0];
@@ -1796,21 +1901,25 @@ void ps_printer::do_import(char *arg, const environment 
*env)
   int desired_width = parms[4];
   int desired_height = parms[5];
   if (desired_width <= 0) {
-    error("bad width argument '%1' for X import command: must be > 0",
+    error("desired width (6th) argument '%1'"
+         " to device extension command 'import' must be > 0",
          desired_width);
     return;
   }
-  if (nparms == 6 && desired_height <= 0) {
-    error("bad height argument '%1' for X import command: must be > 0",
+  if ((nparms == 6) && (desired_height <= 0)) {
+    error("desired height (7th) argument '%1'"
+         " to device extension command 'import' must be > 0",
          desired_height);
     return;
   }
   if (llx == urx) {
-    error("llx and urx arguments for X import command must not be equal");
+    error("llx and urx arguments"
+         " to device extension command 'import' must not be equal");
     return;
   }
   if (lly == ury) {
-    error("lly and ury arguments for X import command must not be equal");
+    error("lly and ury arguments"
+         " to device extension command 'import' must not be equal");
     return;
   }
   if (nparms == 5) {
@@ -1820,7 +1929,7 @@ void ps_printer::do_import(char *arg, const environment 
*env)
       old_wid = -old_wid;
     if (old_ht < 0)
       old_ht = -old_ht;
-    desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
+    desired_height = int(desired_width * (double(old_ht) / double(old_wid)) + 
.5);
   }
   if (env->vpos - desired_height < 0)
     warning("top of imported graphic is above the top of the page");
@@ -1847,7 +1956,7 @@ void ps_printer::do_invis(char *, const environment *)
 void ps_printer::do_endinvis(char *, const environment *)
 {
   if (invis_count == 0)
-    error("unbalanced 'endinvis' command");
+    error("unbalanced device extension command 'endinvis'");
   else
     --invis_count;
 }
@@ -1882,7 +1991,7 @@ int main(int argc, char **argv)
       bflag = 1;
       break;
     case 'c':
-      if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
+      if ((sscanf(optarg, "%d", &ncopies) != 1) || (ncopies <= 0)) {
        error("expected positive integer argument to '-c' option, got"
              " '%1'; ignoring", optarg);
        ncopies = 1;
@@ -1909,11 +2018,7 @@ int main(int argc, char **argv)
        error("ignoring invalid custom paper format '%1'", optarg);
       break;
     case 'P':
-      env = "GROPS_PROLOGUE";
-      env += '=';
-      env += optarg;
-      env += '\0';
-      if (putenv(strsave(env.contents())) != 0)
+      if (setenv("GROPS_PROLOGUE", optarg, 1 /* overwrite */) != 0)
        fatal("cannot update process environment: %1", strerror(errno));
       break;
     case 'v':
@@ -1922,7 +2027,7 @@ int main(int argc, char **argv)
       break;
     case 'w':
       if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
-       error("invalid line width '%1' ignored", optarg);
+       error("ignoring invalid rule thickness '%1'", optarg);
        linewidth = -1;
       }
       break;
@@ -1951,10 +2056,10 @@ int main(int argc, char **argv)
   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
   SET_BINARY(fileno(stdout));
   if (optind >= argc)
-    do_file("-");
+    interpret_troff_output_file("-");
   else {
     for (int i = optind; i < argc; i++)
-      do_file(argv[i]);
+      interpret_troff_output_file(argv[i]);
   }
   return 0;
 }

...but this old James Clark code might in fact be the same problem.

-  if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
-    error("scaling units not allowed in arguments for X import command");

I'll be be finding out in 20 minutes or so when this build finishes.

Attaching a test script in progress so you can see where I'm headed.

Regards,
Branden

Attachment: device-extension-command-import-works.sh
Description: Bourne shell script

Attachment: signature.asc
Description: PGP signature

  • ... Bruno Haible via discussion of the GNU roff typesetting system and related software
    • ... G. Branden Robinson
      • ... G. Branden Robinson
        • ... G. Branden Robinson
        • ... G. Branden Robinson

Reply via email to