[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
device-extension-command-import-works.sh
Description: Bourne shell script
signature.asc
Description: PGP signature
