Version in base suite: 3.2.7a-5+deb10u1 Version in overlay suite: 3.2.7a-5+deb10u2 Base version: fig2dev_3.2.7a-5+deb10u2 Target version: fig2dev_3.2.7a-5+deb10u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/f/fig2dev/fig2dev_3.2.7a-5+deb10u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/f/fig2dev/fig2dev_3.2.7a-5+deb10u3.dsc changelog | 10 patches/42_CVE-2019-19746.patch | 44 + patches/43_fgets2getline.patch | 1717 ++++++++++++++++++++++++++++++++++++++++ patches/series | 2 4 files changed, 1773 insertions(+) diff -Nru fig2dev-3.2.7a/debian/changelog fig2dev-3.2.7a/debian/changelog --- fig2dev-3.2.7a/debian/changelog 2019-12-04 21:12:49.000000000 +0000 +++ fig2dev-3.2.7a/debian/changelog 2020-01-07 18:53:09.000000000 +0000 @@ -1,3 +1,13 @@ +fig2dev (1:3.2.7a-5+deb10u3) buster; urgency=medium + + * 42_CVE-2019-19746: Reject huge arrow types causing integer overflow. + This fixes CVE-2019-19746 (Closes: #946628). + * 43_fgets2getline: Replace most calls to fgets() by getline() in + read.c. This fixes CVE-2019-19797 and several other segfaults + (Closes: #946866). + + -- Roland Rosenfeld Tue, 07 Jan 2020 19:53:09 +0100 + fig2dev (1:3.2.7a-5+deb10u2) buster; urgency=medium * 41_CVE-2019-19555: Allow Fig v2 text strings ending with multiple ^A. diff -Nru fig2dev-3.2.7a/debian/patches/42_CVE-2019-19746.patch fig2dev-3.2.7a/debian/patches/42_CVE-2019-19746.patch --- fig2dev-3.2.7a/debian/patches/42_CVE-2019-19746.patch 1970-01-01 00:00:00.000000000 +0000 +++ fig2dev-3.2.7a/debian/patches/42_CVE-2019-19746.patch 2020-01-07 18:53:09.000000000 +0000 @@ -0,0 +1,44 @@ +From: Thomas Loimer +Date: Tue Dec 10 13:17:36 2019 +0100 +Bug: https://sourceforge.net/p/mcj/tickets/57 +Bug-Debian: https://bugs.debian.org/946628 +Origin: https://sourceforge.net/p/mcj/fig2dev/ci/3065abc7b4f740ed6532322843531317de782a26/ +Subject: Reject huge arrow types causing integer overflow. + This fixes CVE-2019-19746 + +--- a/fig2dev/arrow.c ++++ b/fig2dev/arrow.c +@@ -1,9 +1,10 @@ + /* + * Fig2dev: Translate Fig code to various Devices +- * Copyright (c) 1985 by Supoj Sutantavibul + * Copyright (c) 1991 by Micah Beck +- * Parts Copyright (c) 1989-2002 by Brian V. Smith +- * Parts Copyright (c) 2015-2018 by Thomas Loimer ++ * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul ++ * Parts Copyright (c) 1989-2015 by Brian V. Smith ++ * Parts Copyright (c) 2015-2019 by Thomas Loimer ++ * + * + * Any party obtaining a copy of these files is granted, free of charge, a + * full and unrestricted irrevocable, world-wide, paid up, royalty-free, +@@ -78,7 +79,9 @@ make_arrow(int type, int style, double t + { + F_arrow *a; + +- if (style < 0 || style > 1 || type < 0 || (type + 1) * 2 > NUMARROWS) ++ if (style < 0 || style > 1 || type < 0 || ++ /* beware of int overflow */ ++ type > NUMARROWS || (type + 1) * 2 > NUMARROWS) + return NULL; + if (NULL == (Arrow_malloc(a))) { + put_msg(Err_mem); +@@ -90,7 +93,7 @@ make_arrow(int type, int style, double t + + a->type = type; + a->style = style; +- a->thickness = thickness*THICK_SCALE; ++ a->thickness = thickness * THICK_SCALE; + a->wid = wid; + a->ht = ht; + return a; diff -Nru fig2dev-3.2.7a/debian/patches/43_fgets2getline.patch fig2dev-3.2.7a/debian/patches/43_fgets2getline.patch --- fig2dev-3.2.7a/debian/patches/43_fgets2getline.patch 1970-01-01 00:00:00.000000000 +0000 +++ fig2dev-3.2.7a/debian/patches/43_fgets2getline.patch 2020-01-07 18:53:09.000000000 +0000 @@ -0,0 +1,1717 @@ +From: Thomas Loimer +Date: Sun Jan 5 19:22:12 2020 +0100 +Bug: https://sourceforge.net/p/mcj/tickets/58 +Bug: https://sourceforge.net/p/mcj/tickets/59 +Bug: https://sourceforge.net/p/mcj/tickets/61 +Bug: https://sourceforge.net/p/mcj/tickets/62 +Bug: https://sourceforge.net/p/mcj/tickets/67 +Bug: https://sourceforge.net/p/mcj/tickets/78 +Bug: https://sourceforge.net/p/mcj/tickets/79 +Bug-Debian: https://bugs.debian.org/946866 +Origin: https://sourceforge.net/p/mcj/fig2dev/ci/41b9bb838a3d544539f6e68aa4f87d70ef7d45ce/ +Subject: Replace most calls to fgets() by getline() in read.c + Also, fig files version 1.4 must begin with `#FIG 1.4`. Previously, a `#` in the + first line was sufficient to detect at least a version 1.4 fig file. + Move some variables with file scope into functions. + + This commit fixes tickets #58, #59, #61, #62, #67, #78 and #79. + +In fig2dev/lib/, replacements are provided for some library functions used in + fig2dev, e.g., strncasecmp(), strrchr(), etc. The getline() function was + introduced more recently than any of the functions provided in fig2dev/lib. + Nevertheless, for getline() a replacement function is not provided. It seems, + that all the replacement functions do not work, but nobody noticed. Therefore, + only provide a replacement function for getline() if that turns out to + be useful. + The replacement functions do not work, because a header file providing the + necessary function declarations is missing. + + This fixes CVE-2019-19797 + +--- a/fig2dev/fig2dev.c ++++ b/fig2dev/fig2dev.c +@@ -81,7 +81,7 @@ bool bgspec = false; /* flag to say -g + bool support_i18n = false; + #endif + char gif_transparent[20]="\0"; /* GIF transp color hex name (e.g. #ff00dd) */ +-char papersize[20]; /* paper size */ ++char papersize[]; /* paper size */ + char boundingbox[64]; /* boundingbox */ + char lang[40]; /* selected output language */ + RGB background; /* background (if specified by -g) */ +--- a/fig2dev/fig2dev.h ++++ b/fig2dev/fig2dev.h +@@ -95,7 +95,7 @@ extern bool bgspec; /* flag to say -g w + extern bool support_i18n; + #endif + extern char gif_transparent[];/* GIF transp color hex name (e.g. #ff00dd) */ +-extern char papersize[]; /* paper size */ ++extern char papersize[16]; /* paper size */ + extern char boundingbox[]; /* boundingbox */ + extern char lang[]; /* selected output language */ + extern char *Fig_color_names[]; /* hex names for Fig colors */ +--- a/fig2dev/read.c ++++ b/fig2dev/read.c +@@ -3,7 +3,7 @@ + * Copyright (c) 1991 by Micah Beck + * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul + * Parts Copyright (c) 1989-2015 by Brian V. Smith +- * Parts Copyright (c) 2015-2019 by Thomas Loimer ++ * Parts Copyright (c) 2015-2020 by Thomas Loimer + * + * Any party obtaining a copy of these files is granted, free of charge, a + * full and unrestricted irrevocable, world-wide, paid up, royalty-free, +@@ -45,28 +45,34 @@ extern F_arrow *make_arrow(int type, int + User_color user_colors[MAX_USR_COLS]; /* fig2dev.h */ + int user_col_indx[MAX_USR_COLS]; /* fig2dev.h */ + int num_usr_cols; /* fig2dev.h */ +-int num_object; /* read1_3.c */ + /* flags, psfonts.h, genps.c */ + int v2_flag; /* Protocol V2.0 or higher */ + int v21_flag; /* Protocol V2.1 or higher */ + int v30_flag; /* Protocol V3.0 or higher */ + int v32_flag; /* Protocol V3.2 or higher */ + +-static void read_colordef(void); +-static F_ellipse *read_ellipseobject(void); +-static F_line *read_lineobject(FILE *fp); +-static F_text *read_textobject(FILE *fp); +-static F_spline *read_splineobject(FILE *fp); +-static F_arc *read_arcobject(FILE *fp); +-static F_compound *read_compoundobject(FILE *fp); ++static void read_colordef(char *line, int line_no); ++static F_ellipse *read_ellipseobject(char *line, int line_no); ++static F_line *read_lineobject(FILE *fp, char **restrict line, ++ size_t *line_len, int *line_no); ++static F_text *read_textobject(FILE *fp, char **restrict line, ++ size_t *line_len, int *line_no); ++static F_spline *read_splineobject(FILE *fp, char **restrict line, ++ size_t *line_len, int *line_no); ++static F_arc *read_arcobject(FILE *fp, char **restrict line, ++ size_t *line_len, int *line_no); ++static F_compound *read_compoundobject(FILE *fp, char **restrict line, ++ size_t *line_len, int *line_no); + static F_comment *attach_comments(void); +-static void count_lines_correctly(FILE *fp); +-static void init_pats_used(void); +-static int read_objects(FILE *fp, F_compound *obj); +-static int get_line(FILE *fp); +-static void skip_line(FILE *fp); +-static int backslash_count(char cp[], int start); +-static int save_comment(void); ++static void count_lines_correctly(FILE *fp, int *line_no); ++static void init_pats_used(void); ++static int read_objects(FILE *fp, F_compound *obj); ++static ssize_t get_line(FILE *fp, char **restrict line, ++ size_t *line_len, int *line_no); ++static void skip_line(FILE *fp); ++static ptrdiff_t backslash_count(const char *restrict cp, ++ ptrdiff_t start); ++ + static char Err_incomp[] = "Incomplete %s object at line %d."; + static char Err_invalid[] = "Invalid %s object at line %d."; + static char Err_arrow[] = "Invalid %s arrow at line %d."; +@@ -77,9 +83,6 @@ static char Err_arrow[] = "Invalid %s ar + /* max number of comments that can be stored with each object */ + #define MAXCOMMENTS 100 + +-static int gif_colnum = 0; +-static char buf[BUFSIZ]; +-static int line_no = 0; + static char *comments[MAXCOMMENTS]; /* comments saved for current object */ + static int numcom; /* current comment index */ + static bool com_alloc = false; /* whether or not the comment array +@@ -148,7 +151,6 @@ readfp_fig(FILE *fp, F_compound *obj) + char c; + int i, status; + +- num_object = 0; + num_usr_cols = 0; + init_pats_used(); + +@@ -157,15 +159,14 @@ readfp_fig(FILE *fp, F_compound *obj) + /* initialize the comment array */ + if (!com_alloc) + for (i = 0; i < MAXCOMMENTS; ++i) +- comments[i] = (char *) NULL; ++ comments[i] = (char *)NULL; + com_alloc = true; +- memset((char*)obj, '\0', COMOBJ_SIZE); ++ memset((void *)obj, '\0', COMOBJ_SIZE); + + /* read first character to see if it is "#" (#FIG 1.4 and newer) */ + c = fgetc(fp); + if (feof(fp)) + return -2; +- memset((char*)obj, '\0', COMOBJ_SIZE); + /* put the character back */ + ungetc(c, fp); + if (c == '#') +@@ -185,25 +186,30 @@ read_objects(FILE *fp, F_compound *obj) + F_spline *s, *ls = NULL; + F_arc *a, *la = NULL; + F_compound *c, *lc = NULL; +- int object, coord_sys, len; +- +- memset((char*)obj, '\0', COMOBJ_SIZE); +- +- (void) fgets(buf, BUFSIZ, fp); /* get the version line */ +- if (strncmp(buf, "#FIG ", 5)) { +- put_msg("Incorrect format string in first line of input file."); ++ bool objects = false; ++ int object, coord_sys; ++ int line_no; ++ int gif_colnum = 0; ++ char *line; ++ char buf[16]; ++ size_t line_len = 256; ++ ++ /* Get the 15 chars of the first line. ++ Use fgets(), because get_line() would store the line as a comment */ ++ if (fgets(buf, sizeof buf, fp) == NULL) { ++ put_msg("Could not read input file."); + return -1; + } ++ /* seek to the end of the first line */ ++ if (strchr(buf, '\n') == NULL) { ++ int c; ++ do ++ c = fgetc(fp); ++ while (c != '\n' && c != EOF); ++ } + +- /* remove newline and any carriage return (from a PC, perhaps) */ +- len = strlen(buf); +- if (buf[len-1] == '\n') { +- if (buf[len-2] == '\r') +- buf[len-2] = '\0'; +- else +- buf[len-1] = '\0'; +- } else { /* fgets() only stops at newline and end-of-file */ +- put_msg("File is truncated at first line."); ++ if (strncmp(buf, "#FIG ", 5)) { ++ put_msg("Incorrect format string in first line of input file."); + return -1; + } + +@@ -211,49 +217,65 @@ read_objects(FILE *fp, F_compound *obj) + v2_flag = (!strncmp(buf, "#FIG 2", 6) || !strncmp(buf, "#FIG 3", 6)); + /* v21_flag is for version 2.1 or higher */ + v21_flag = (!strncmp(buf, "#FIG 2.1", 8) || !strncmp(buf, "#FIG 3", 6)); +- /* version 2.2 was only beta - 3.0 is the official release (they are identical) */ ++ /* version 2.2 was only beta - 3.0 is the official release ++ (they are identical) */ + v30_flag = (!strncmp(buf, "#FIG 3", 6) || !strncmp(buf, "#FIG 2.2", 8)); +- /* version 3.2 contains paper size, magnif, multiple page and transparent color +- in Fig file */ ++ /* version 3.2 contains paper size, magnif, multiple page ++ and transparent color in Fig file */ + v32_flag = (!strncmp(buf, "#FIG 3.2", 8)); + if (strncmp(&buf[5], PACKAGE_VERSION, 3) > 0) { +- put_msg("Fig file format (%s) newer than this version of fig2dev (%s), exiting", +- &buf[5], PACKAGE_VERSION); +- exit(1); ++ put_msg("Fig file format (%s) newer than this version of fig2dev (%s), exiting", ++ &buf[5], PACKAGE_VERSION); ++ exit(EXIT_FAILURE); ++ } ++ ++ if ((v2_flag | v21_flag | v30_flag | v32_flag) == 0 && ++ strncmp(buf, "#FIG 1.4", 8)) { ++ put_msg("Cannot determine fig file format from string '%s'.", ++ &buf[5]); ++ exit(EXIT_FAILURE); ++ } ++ ++ if ((line = malloc(line_len)) == NULL) { ++ put_msg(Err_mem); ++ return -1; + } + ++ line_no = 1; + if (v30_flag) { + /* read the orientation spec (landscape/portrait) */ +- line_no=1; +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at landscape/portrait specification."); ++ free(line); + return -1; + } + /* but set only if the user didn't specify the orientation + on the command line */ + if (!orientspec) +- landscape = !strncasecmp(buf,"land",4); ++ landscape = !strncasecmp(line, "land", 4); + + /* now read the metric/inches spec OR centering spec */ +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at metric/inches or centering specification."); ++ free(line); + return -1; + } + /* read justification spec */ +- if ((strncasecmp(buf,"center",6) == 0) || +- (strncasecmp(buf,"flush",5) == 0)) { ++ if ((strncasecmp(line, "center", 6) == 0) || ++ (strncasecmp(line, "flush", 5) == 0)) { + /* but set only if user didn't specify it */ + if (!centerspec) +- center = strncasecmp(buf,"flush",5); ++ center = strncasecmp(line, "flush", 5); + /* now read metric/inches spec */ +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at metric/inches specification."); ++ free(line); + return -1; + } + } + /* read metric/inches spec */ + /* if metric, scale magnification to correct for xfig display error */ +- if (strncasecmp(buf,"metric", 6) == 0) { ++ if (strncasecmp(line, "metric", 6) == 0) { + metric = 1; + } else { + metric = 0; +@@ -261,56 +283,67 @@ read_objects(FILE *fp, F_compound *obj) + + /* new stuff in 3.2 */ + if (v32_flag) { +- char *p; + /* read the paper size */ +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at paper size specification."); ++ free(line); + return -1; + } + if (!paperspec) { +- strcpy(papersize,buf); +- /* and truncate at first blank, if any */ +- if ((p=strchr(papersize,' '))) ++ char *p; ++ /* truncate at first blank, if any */ ++ if ((p = strchr(line, ' '))) + *p = '\0'; ++ if (strlen(line) + 1 > sizeof papersize) { ++ put_msg("Invalid paper size specification at line %d: %s", ++ line_no, line); ++ free(line); ++ return -1; ++ } ++ strcpy(papersize, line); + } + + /* read the magnification */ +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at magnification specification."); ++ free(line); + return -1; + } +- /* if the users hasn't specified a magnification on the command line, +- use the one in the file */ ++ /* if the users hasn't specified a magnification on ++ the command line, use the one in the file */ + if (!magspec) { +- mag = atof(buf)/100.0; ++ mag = atof(line)/100.0; + if (mag <= 0.) + mag = 1.; + fontmag = mag; + } + + /* read the multiple page flag */ +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at multiple page specification."); ++ free(line); + return -1; + } + if (!multispec) +- multi_page = (strncasecmp(buf,"multiple",8) == 0); ++ multi_page = (strncasecmp(line, "multiple", 8) == 0); + + /* Read the GIF transparent color. */ +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at transparent color specification."); ++ free(line); + return -1; + } + if (!transspec) { +- gif_colnum = atoi(buf); ++ gif_colnum = atoi(line); + if (gif_colnum < -3) { + put_msg("Invalid color number for transparent color."); ++ free(line); + return -1; + } + /* if standard color, get the name from the array */ + /* for user colors, wait till we've read in the file to get the value */ + if (gif_colnum < NUM_STD_COLS && gif_colnum >= 0) +- strcpy(gif_transparent,Fig_color_names[gif_colnum]); ++ strcpy(gif_transparent, Fig_color_names[gif_colnum]); + } + } + } else { +@@ -329,17 +362,20 @@ read_objects(FILE *fp, F_compound *obj) + } + + /* now read for resolution and coord_sys (coord_sys is not used) */ +- if (get_line(fp) < 0) { ++ if (get_line(fp, &line, &line_len, &line_no) < 0) { + put_msg("File is truncated at resolution specification."); ++ free(line); + return -1; + } +- if (sscanf(buf,"%lf%d\n", &ppi, &coord_sys) != 2) { ++ if (sscanf(line, "%lf%d", &ppi, &coord_sys) != 2) { + put_msg("Incomplete resolution information at line %d.", line_no); ++ free(line); + return -1; + } + if (ppi <= 0.) { + put_msg("Invalid resolution information (%g) at line %d.", + ppi, line_no); ++ free(line); + return -1; + } + +@@ -349,24 +385,28 @@ read_objects(FILE *fp, F_compound *obj) + /* attach any comments found thus far to the whole figure */ + obj->comments = attach_comments(); + +- while (get_line(fp) > 0) { +- if (sscanf(buf, "%d", &object) != 1) { ++ while (get_line(fp, &line, &line_len, &line_no) > 0) { ++ if (sscanf(line, "%d", &object) != 1) { + put_msg("Incorrect format at line %d.", line_no); ++ free(line); + return -1; + } + switch (object) { + case OBJ_COLOR_DEF: +- read_colordef(); +- if (num_object) { ++ if (objects) { + put_msg("Color definitions must come before other objects (line %d).", + line_no); +- return (-1); ++ free(line); ++ return -1; + } +- ++num_usr_cols; ++ read_colordef(line, line_no); + break; + case OBJ_POLYLINE : +- if ((l = read_lineobject(fp)) == NULL) ++ if ((l = read_lineobject(fp, &line, &line_len, &line_no)) == ++ NULL) { ++ free(line); + return -1; ++ } + #ifdef V4_0 + if ((l->pic != NULL) && (l->pic->figure != NULL)) { + if (lc) +@@ -388,79 +428,97 @@ read_objects(FILE *fp, F_compound *obj) + ll = (ll->next = l); + else + ll = obj->lines = l; +- num_object++; ++ objects = true; + break; + #endif /* V4_0 */ + case OBJ_SPLINE : +- if ((s = read_splineobject(fp)) == NULL) { ++ if ((s = read_splineobject(fp, &line, &line_len, &line_no)) ++ == NULL) { ++ free(line); + return -1; +- } ++ } + if (v32_flag){ /* s is a line */ + if (ll) + ll = (ll->next = (F_line *) s); + else + ll = obj->lines = (F_line *) s; +- num_object++; ++ objects = true; + break; + } + if (ls) + ls = (ls->next = s); + else + ls = obj->splines = s; +- num_object++; ++ objects = true; + break; + case OBJ_ELLIPSE : +- if ((e = read_ellipseobject()) == NULL) ++ if ((e = read_ellipseobject(line, line_no)) == NULL) { ++ free(line); + return -1; ++ } + if (le) + le = (le->next = e); + else + le = obj->ellipses = e; +- num_object++; ++ objects = true; + break; + case OBJ_ARC : +- if ((a = read_arcobject(fp)) == NULL) ++ if ((a = read_arcobject(fp, &line, &line_len, &line_no)) == ++ NULL) { ++ free(line); + return -1; ++ } + if (la) + la = (la->next = a); + else + la = obj->arcs = a; +- num_object++; ++ objects = true; + break; + case OBJ_TEXT : +- if ((t = read_textobject(fp)) == NULL) ++ if ((t = read_textobject(fp, &line, &line_len, &line_no)) == ++ NULL) { ++ free(line); + return -1; ++ } + if (lt) + lt = (lt->next = t); + else + lt = obj->texts = t; +- num_object++; ++ objects = true; + break; + case OBJ_COMPOUND : +- if ((c = read_compoundobject(fp)) == NULL) ++ if ((c = read_compoundobject(fp, &line, &line_len,&line_no)) ++ == NULL) { ++ free(line); + return -1; ++ } + if (lc) + lc = (lc->next = c); + else + lc = obj->compounds = c; +- num_object++; ++ objects = true; + break; + default : + put_msg("Incorrect object code at line %d.", line_no); ++ free(line); + return -1; + } /* switch */ +- } /* while (get_line(fp)) */ ++ } /* while (get_line(...)) */ ++ free(line); + + /* if user color was requested for GIF transparent color, get the + rgb values from the user color array now that we've read them in */ + if (gif_colnum >= NUM_STD_COLS) { + int i; +- for (i=0; i MAX_USR_COLS) ++ num_usr_cols = MAX_USR_COLS; ++ for (i=0; i < num_usr_cols; ++i) + if (user_col_indx[i] == gif_colnum) + break; + if (i < num_usr_cols) +- sprintf(gif_transparent,"#%2x%2x%2x", +- user_colors[i].r,user_colors[i].g,user_colors[i].b); ++ sprintf(gif_transparent, "#%2x%2x%2x", ++ user_colors[i].r, user_colors[i].g, user_colors[i].b); + } + + if (feof(fp)) +@@ -474,55 +532,72 @@ read_objects(FILE *fp, F_compound *obj) + } /* read_objects */ + + static void +-read_colordef(void) ++read_colordef(char *line, int line_no) + { +- int c; +- unsigned int r,g,b; ++ int c; ++ unsigned int r,g,b; + +- if ((sscanf(buf, "%*d %d #%2x%2x%2x", &c, &r, &g, &b) != 4) || +- (c < NUM_STD_COLS)) { +- buf[strlen(buf)-1]='\0'; /* remove the newline */ +- put_msg("Invalid color definition: %s, setting to black (#00000).",buf); +- r=g=b=0; +- } +- user_col_indx[num_usr_cols] = c; +- user_colors[num_usr_cols].r = r; +- user_colors[num_usr_cols].g = g; +- user_colors[num_usr_cols].b = b; ++ if (num_usr_cols >= MAX_USR_COLS) { ++ if (num_usr_cols == MAX_USR_COLS) { ++ put_msg("Maximum number of color definitions (%d) exceeded at line %d.", ++ MAX_USR_COLS, line_no); ++ ++num_usr_cols; ++ } ++ /* ignore additional colors */ ++ return; ++ } ++ if (sscanf(line, "%*d %d #%2x%2x%2x", &c, &r, &g, &b) != 4) { ++ if (c >= NUM_STD_COLS && c < NUM_STD_COLS + MAX_USR_COLS) { ++ put_msg("Invalid color definition at line %d: %s, setting to black (#00000).", ++ line_no, line); ++ r = g = b = 0; ++ } else { ++ put_msg("User color number at line %d out of range (%d), should be between %d and %d.", ++ line_no, c, NUM_STD_COLS, ++ NUM_STD_COLS + MAX_USR_COLS - 1); ++ return; ++ } ++ } ++ user_col_indx[num_usr_cols] = c; ++ user_colors[num_usr_cols].r = r; ++ user_colors[num_usr_cols].g = g; ++ user_colors[num_usr_cols].b = b; ++ ++num_usr_cols; + } + + static void +-fix_and_note_color(int *color) ++fix_and_note_color(int *color, int line_no) + { +- int i; +- if (*color < DEFAULT) { +- put_msg("Invalid color number %d at line %d, using default color.", +- *color, line_no); +- *color = DEFAULT; +- return; +- } +- if (*color < NUM_STD_COLS) { +- if (*color >= BLACK_COLOR) { +- std_color_used[*color] = true; ++ int i; ++ ++ if (*color < DEFAULT) { ++ put_msg("Invalid color number %d at line %d, using default color.", ++ *color, line_no); ++ *color = DEFAULT; ++ return; + } +- return; +- } +- for (i=0; i= BLACK_COLOR) { ++ std_color_used[*color] = true; ++ } + return; + } +- put_msg("Cannot locate user color %d, using default color at line %d.", +- *color, line_no); +- *color = DEFAULT; +- return; ++ for (i = 0; i < MIN(num_usr_cols, MAX_USR_COLS); ++i) ++ if (*color == user_col_indx[i]) { ++ *color = i + NUM_STD_COLS; ++ return; ++ } ++ put_msg("Cannot locate user color %d, using default color at line %d.", ++ *color, line_no); ++ *color = DEFAULT; ++ return; + } + + static void +-note_fill(int fill, int *color) ++note_fill(int fill, int *color, int line_no) + { + if (fill != UNFILLED) { +- fix_and_note_color(color); ++ fix_and_note_color(color, line_no); + if (fill >= NUMSHADES + NUMTINTS) { + pattern_used[fill - NUMSHADES - NUMTINTS] = true; + pats_used = true; +@@ -531,7 +606,7 @@ note_fill(int fill, int *color) + } + + static F_arc * +-read_arcobject(FILE *fp) ++read_arcobject(FILE *fp, char **restrict line, size_t *line_len, int *line_no) + { + F_arc *a; + int n, fa, ba; +@@ -548,7 +623,7 @@ read_arcobject(FILE *fp) + a->back_arrow = NULL; + a->next = NULL; + if (v30_flag) { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d%lf%lf%d%d%d%d%d%d\n", ++ n = sscanf(*line,"%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d%lf%lf%d%d%d%d%d%d", + &a->type, &a->style, &a->thickness, + &a->pen_color, &a->fill_color, &a->depth, &a->pen, &a->fill_style, + &a->style_val, &a->cap_style, +@@ -558,7 +633,7 @@ read_arcobject(FILE *fp) + &a->point[1].x, &a->point[1].y, + &a->point[2].x, &a->point[2].y); + } else { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d%d%lf%lf%d%d%d%d%d%d\n", ++ n = sscanf(*line, "%*d%d%d%d%d%d%d%d%lf%d%d%d%lf%lf%d%d%d%d%d%d", + &a->type, &a->style, &a->thickness, + &a->pen_color, &a->depth, &a->pen, &a->fill_style, + &a->style_val, &a->direction, &fa, &ba, +@@ -570,45 +645,45 @@ read_arcobject(FILE *fp) + a->cap_style = 0; /* butt line cap */ + } + if ((v30_flag && n != 21) || (!v30_flag && n != 19)) { +- put_msg(Err_incomp, "arc", line_no); ++ put_msg(Err_incomp, "arc", *line_no); + free(a); + return NULL; + } + a->thickness *= round(THICK_SCALE); + a->fill_style = FILL_CONVERT(a->fill_style); + if (INVALID_ARC(a)) { +- put_msg(Err_invalid, "arc", line_no); ++ put_msg(Err_invalid, "arc", *line_no); + free(a); + return NULL; + } +- fix_and_note_color(&a->pen_color); +- note_fill(a->fill_style, &a->fill_color); ++ fix_and_note_color(&a->pen_color, *line_no); ++ note_fill(a->fill_style, &a->fill_color, *line_no); + if (fa) { +- if (get_line(fp) < 0 || +- sscanf(buf, "%d%d%lf%lf%lf", ++ if (get_line(fp, line, line_len, line_no) < 0 || ++ sscanf(*line, "%d%d%lf%lf%lf", + &type, &style, &thickness, &wid, &ht) != 5) { +- put_msg(Err_incomp, "arc", line_no); ++ put_msg(Err_incomp, "arc", *line_no); + free(a); + return NULL; + } + if ((a->for_arrow = make_arrow(type, style, thickness, wid, ht)) + == NULL) { +- put_msg(Err_arrow, "forward", line_no); ++ put_msg(Err_arrow, "forward", *line_no); + free(a); + return NULL; + } + } + if (ba) { +- if (get_line(fp) < 0 || +- sscanf(buf, "%d%d%lf%lf%lf", ++ if (get_line(fp, line, line_len, line_no) < 0 || ++ sscanf(*line, "%d%d%lf%lf%lf", + &type, &style, &thickness, &wid, &ht) != 5) { +- put_msg(Err_incomp, "arc", line_no); ++ put_msg(Err_incomp, "arc", *line_no); + free(a); + return NULL; + } + if ((a->back_arrow = make_arrow(type, style, thickness, wid, ht)) + == NULL) { +- put_msg(Err_arrow, "backward", line_no); ++ put_msg(Err_arrow, "backward", *line_no); + free(a); + return NULL; + } +@@ -618,7 +693,8 @@ read_arcobject(FILE *fp) + } + + static F_compound * +-read_compoundobject(FILE *fp) ++read_compoundobject(FILE *fp, char **restrict line, size_t *line_len, ++ int *line_no) + { + F_arc *a, *la = NULL; + F_ellipse *e, *le = NULL; +@@ -638,22 +714,23 @@ read_compoundobject(FILE *fp) + com->next = NULL; + com->comments = attach_comments(); /* attach any comments */ + +- n = sscanf(buf, "%*d%d%d%d%d\n", &com->nwcorner.x, &com->nwcorner.y, ++ n = sscanf(*line, "%*d%d%d%d%d", &com->nwcorner.x, &com->nwcorner.y, + &com->secorner.x, &com->secorner.y); + if (n != 4) { +- put_msg(Err_incomp, "compound", line_no); ++ put_msg(Err_incomp, "compound", *line_no); + free(com); + return NULL; + } +- while (get_line(fp) > 0) { +- if (sscanf(buf, "%d", &object) != 1) { +- put_msg(Err_incomp, "compound", line_no); ++ while (get_line(fp, line, line_len, line_no) > 0) { ++ if (sscanf(*line, "%d", &object) != 1) { ++ put_msg(Err_incomp, "compound", *line_no); + free_compound(&com); + return NULL; +- } ++ } + switch (object) { + case OBJ_POLYLINE : +- if ((l = read_lineobject(fp)) == NULL) { ++ if ((l = read_lineobject(fp, line, line_len, line_no)) == ++ NULL) { + return NULL; + } + #ifdef V4_0 +@@ -674,7 +751,8 @@ read_compoundobject(FILE *fp) + #endif /* V4_0 */ + break; + case OBJ_SPLINE : +- if ((s = read_splineobject(fp)) == NULL) { ++ if ((s = read_splineobject(fp, line, line_len, line_no)) == ++ NULL) { + return NULL; + } + if (v32_flag){ /* s is a line */ +@@ -690,7 +768,7 @@ read_compoundobject(FILE *fp) + ls = com->splines = s; + break; + case OBJ_ELLIPSE : +- if ((e = read_ellipseobject()) == NULL) { ++ if ((e = read_ellipseobject(*line, *line_no)) == NULL) { + return NULL; + } + if (le) +@@ -699,7 +777,8 @@ read_compoundobject(FILE *fp) + le = com->ellipses = e; + break; + case OBJ_ARC : +- if ((a = read_arcobject(fp)) == NULL) { ++ if ((a = read_arcobject(fp, line, line_len, line_no)) == ++ NULL) { + return NULL; + } + if (la) +@@ -708,7 +787,8 @@ read_compoundobject(FILE *fp) + la = com->arcs = a; + break; + case OBJ_TEXT : +- if ((t = read_textobject(fp)) == NULL) { ++ if ((t = read_textobject(fp, line, line_len, line_no)) == ++ NULL) { + return NULL; + } + if (lt) +@@ -717,7 +797,8 @@ read_compoundobject(FILE *fp) + lt = com->texts = t; + break; + case OBJ_COMPOUND : +- if ((c = read_compoundobject(fp)) == NULL) { ++ if ((c = read_compoundobject(fp, line, line_len, line_no)) ++ == NULL) { + return NULL; + } + if (lc) +@@ -728,7 +809,7 @@ read_compoundobject(FILE *fp) + case OBJ_END_COMPOUND : + return com; + default : +- put_msg("Wrong object code at line %d", line_no); ++ put_msg("Wrong object code at line %d", *line_no); + return NULL; + } /* switch */ + } +@@ -739,7 +820,7 @@ read_compoundobject(FILE *fp) + } + + static F_ellipse * +-read_ellipseobject(void) ++read_ellipseobject(char *line, int line_no) + { + F_ellipse *e; + int n; +@@ -749,7 +830,7 @@ read_ellipseobject(void) + e->pen = 0; + e->next = NULL; + if (v30_flag) { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d\n", ++ n = sscanf(line, "%*d%d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d", + &e->type, &e->style, &e->thickness, + &e->pen_color, &e->fill_color, &e->depth, &e->pen, &e->fill_style, + &e->style_val, &e->direction, &e->angle, +@@ -758,7 +839,7 @@ read_ellipseobject(void) + &e->start.x, &e->start.y, + &e->end.x, &e->end.y); + } else { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d\n", ++ n = sscanf(line, "%*d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d", + &e->type, &e->style, &e->thickness, + &e->pen_color, &e->depth, &e->pen, &e->fill_style, + &e->style_val, &e->direction, &e->angle, +@@ -773,7 +854,7 @@ read_ellipseobject(void) + free(e); + return NULL; + } +- fix_and_note_color(&e->pen_color); ++ fix_and_note_color(&e->pen_color, line_no); + e->thickness *= round(THICK_SCALE); + e->fill_style = FILL_CONVERT(e->fill_style); + if (INVALID_ELLIPSE(e)) { +@@ -781,7 +862,7 @@ read_ellipseobject(void) + free(e); + return NULL; + } +- note_fill(e->fill_style, &e->fill_color); ++ note_fill(e->fill_style, &e->fill_color, line_no); + e->comments = attach_comments(); /* attach any comments */ + return e; + } +@@ -798,8 +879,9 @@ read_ellipseobject(void) + */ + static int + sanitize_lineobject( +- F_line *l, /* the line */ +- F_point *p /* the last point of the line */ ++ F_line *l, /* the line */ ++ F_point *p, /* the last point of the line */ ++ int line_no + ) + { + F_point *q; +@@ -886,7 +968,7 @@ sanitize_lineobject( + } + + static F_line * +-read_lineobject(FILE *fp) ++read_lineobject(FILE *fp, char **restrict line, size_t *line_len, int *line_no) + { + F_line *l; + F_point *o = NULL, *p, *q; +@@ -907,40 +989,38 @@ read_lineobject(FILE *fp) + l->pic = NULL; + l->comments = NULL; + +- sscanf(buf,"%*d%d",&l->type); /* get the line type */ ++ sscanf(*line, "%*d%d", &l->type); /* get the line type */ + + radius_flag = v30_flag || v21_flag || (v2_flag && l->type == T_ARC_BOX); + if (radius_flag) { + if (v30_flag) { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d%d%d", ++ n = sscanf(*line, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d%d%d", + &l->type,&l->style,&l->thickness,&l->pen_color,&l->fill_color, + &l->depth,&l->pen,&l->fill_style,&l->style_val, + &l->join_style,&l->cap_style, + &l->radius,&fa,&ba,&npts); + } else { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d%d", +- &l->type,&l->style,&l->thickness,&l->pen_color, +- &l->depth,&l->pen,&l->fill_style,&l->style_val,&l->radius,&fa, &ba); ++ n = sscanf(*line, "%*d%d%d%d%d%d%d%d%lf%d%d%d", ++ &l->type,&l->style,&l->thickness,&l->pen_color,&l->depth, ++ &l->pen,&l->fill_style,&l->style_val,&l->radius,&fa, &ba); + l->fill_color = l->pen_color; + } + } + /* old format uses pen for radius of arc-box corners */ + else { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d", ++ n = sscanf(*line, "%*d%d%d%d%d%d%d%d%lf%d%d", + &l->type,&l->style,&l->thickness,&l->pen_color, + &l->depth,&l->pen,&l->fill_style,&l->style_val,&fa,&ba); + l->fill_color = l->pen_color; +- if (l->type == T_ARC_BOX) +- { +- l->radius = (int) l->pen; ++ if (l->type == T_ARC_BOX) { ++ l->radius = l->pen; + l->pen = 0; +- } +- else ++ } else + l->radius = 0; + } + if ((!radius_flag && n!=10) || + (radius_flag && ((!v30_flag && n!=11)||(v30_flag && n!=15)))) { +- put_msg(Err_incomp, "line", line_no); ++ put_msg(Err_incomp, "line", *line_no); + free(l); + return NULL; + } +@@ -948,45 +1028,47 @@ read_lineobject(FILE *fp) + l->thickness *= round(THICK_SCALE); + l->fill_style = FILL_CONVERT(l->fill_style); + if (INVALID_LINE(l)) { +- put_msg(Err_invalid, "line", line_no); ++ put_msg(Err_invalid, "line", *line_no); + free(l); + return NULL; + } +- note_fill(l->fill_style, &l->fill_color); +- fix_and_note_color(&l->pen_color); ++ note_fill(l->fill_style, &l->fill_color, *line_no); ++ fix_and_note_color(&l->pen_color, *line_no); + if (fa) { +- if (get_line(fp) < 0 || +- sscanf(buf, "%d%d%lf%lf%lf", ++ if (get_line(fp, line, line_len, line_no) < 0 || ++ sscanf(*line, "%d%d%lf%lf%lf", + &type, &style, &thickness, &wid, &ht) != 5) { +- put_msg(Err_incomp, "line", line_no); ++ put_msg(Err_incomp, "line", *line_no); + free(l); + return NULL; + } + if ((l->for_arrow = make_arrow(type, style, thickness, wid, ht)) + == NULL) { +- put_msg(Err_arrow, "forward", line_no); ++ put_msg(Err_arrow, "forward", *line_no); + free(l); + return NULL; + } + } + if (ba) { +- if (get_line(fp) < 0 || +- sscanf(buf, "%d%d%lf%lf%lf", ++ if (get_line(fp, line, line_len, line_no) < 0 || ++ sscanf(*line, "%d%d%lf%lf%lf", + &type, &style, &thickness, &wid, &ht) != 5) { +- put_msg(Err_incomp, "line", line_no); ++ put_msg(Err_incomp, "line", *line_no); + free_linestorage(l); + return NULL; + } + if ((l->back_arrow = make_arrow(type, style, thickness, wid, ht)) + == NULL) { +- put_msg(Err_arrow, "backward", line_no); ++ put_msg(Err_arrow, "backward", *line_no); + free_linestorage(l); + return NULL; + } + } + if (l->type == T_PIC_BOX) { +- char file[BUFSIZ], *c; ++ char *file, *c; ++ int pos; + size_t len; ++ ssize_t chars; + + if ((Pic_malloc(l->pic)) == NULL) { + free(l); +@@ -1000,21 +1082,22 @@ read_lineobject(FILE *fp) + XpmCreateXpmImageFromBuffer("", &l->pic->xpmimage, NULL); + #endif + +- /* %[^\n]: really, read until first '\0' in buf */ +- if (get_line(fp) < 0 || sscanf(buf, "%d %[^\n]", +- &l->pic->flipped, file) != 2) { +- put_msg(Err_incomp, "picture", line_no); +- free(l); +- return NULL; ++ if ((chars = get_line(fp, line, line_len, line_no)) < 0 || ++ sscanf(*line, "%d %n", &l->pic->flipped, &pos) != 1) { ++ put_msg(Err_incomp, "picture", *line_no); ++ free(l); ++ return NULL; + } ++ file = *line + pos; ++ len = chars - pos; /* strlen(file) */ ++ + /* if there is a path in the .fig filename, and the path of the + * imported picture filename is NOT absolute, prepend the + * .fig file path to it + */ + if (from && (c = strrchr(from, '/')) && file[0] != '/') { +- if ((l->pic->file = malloc((size_t)(c - from + 2) + +- (len = strlen(file)))) == +- NULL) { ++ if ((l->pic->file = malloc((size_t)(c - from + 2) + len)) == ++ NULL) { + put_msg(Err_mem); + free(l); /* Points not read yet. */ + return NULL; +@@ -1023,8 +1106,8 @@ read_lineobject(FILE *fp) + memcpy(l->pic->file + (c - from + 1), file, len + 1); + } else { + /* either absolute picture path or no path in .fig filename */ +- l->pic->file = malloc(len = strlen(file) + 1); +- memcpy(l->pic->file, file, len); ++ l->pic->file = malloc(len + 1); ++ memcpy(l->pic->file, file, len + 1); + } + } + +@@ -1036,9 +1119,9 @@ read_lineobject(FILE *fp) + p->next = NULL; + + /* read first point of line */ +- ++line_no; ++ ++(*line_no); + if (fscanf(fp, "%d%d", &p->x, &p->y) != 2) { +- put_msg(Err_incomp, "line", line_no); ++ put_msg(Err_incomp, "line", *line_no); + free_linestorage(l); + return NULL; + } +@@ -1046,9 +1129,9 @@ read_lineobject(FILE *fp) + if (!v30_flag) + npts = 1000000; + for (--npts; npts > 0; --npts) { +- count_lines_correctly(fp); ++ count_lines_correctly(fp, line_no); + if (fscanf(fp, "%d%d", &x, &y) != 2) { +- put_msg(Err_incomp, "line", line_no); ++ put_msg(Err_incomp, "line", *line_no); + free_linestorage(l); + return NULL; + } +@@ -1077,7 +1160,7 @@ read_lineobject(FILE *fp) + l->last[1].y = o->y; + } + +- if (sanitize_lineobject(l, p)) { ++ if (sanitize_lineobject(l, p, *line_no)) { + free_linestorage(l); + return NULL; + } +@@ -1089,7 +1172,8 @@ read_lineobject(FILE *fp) + } + + static F_spline * +-read_splineobject(FILE *fp) ++read_splineobject(FILE *fp, char **restrict line, size_t *line_len, ++ int *line_no) + { + F_spline *s; + F_line *l; +@@ -1111,58 +1195,58 @@ read_splineobject(FILE *fp) + s->next = NULL; + + if (v30_flag) { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d", ++ n = sscanf(*line, "%*d%d%d%d%d%d%d%d%d%lf%d%d%d%d", + &s->type, &s->style, &s->thickness, + &s->pen_color, &s->fill_color, + &s->depth, &s->pen, &s->fill_style, &s->style_val, + &s->cap_style, &fa, &ba, &npts); + } else { +- n = sscanf(buf, "%*d%d%d%d%d%d%d%d%lf%d%d", ++ n = sscanf(*line, "%*d%d%d%d%d%d%d%d%lf%d%d", + &s->type, &s->style, &s->thickness, &s->pen_color, + &s->depth, &s->pen, &s->fill_style, &s->style_val, &fa, &ba); + s->fill_color = s->pen_color; + s->cap_style = 0; /* butt line cap */ + } + if ((v30_flag && n != 13) || (!v30_flag && n != 10)) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free(s); + return NULL; + } + s->thickness *= round(THICK_SCALE); + s->fill_style = FILL_CONVERT(s->fill_style); + if (INVALID_SPLINE(s)) { +- put_msg(Err_invalid, "spline", line_no); ++ put_msg(Err_invalid, "spline", *line_no); + free(s); + return NULL; + } +- note_fill(s->fill_style, &s->fill_color); +- fix_and_note_color(&s->pen_color); ++ note_fill(s->fill_style, &s->fill_color, *line_no); ++ fix_and_note_color(&s->pen_color, *line_no); + if (fa) { +- if (get_line(fp) < 0 || +- sscanf(buf, "%d%d%lf%lf%lf", ++ if (get_line(fp, line, line_len, line_no) < 0 || ++ sscanf(*line, "%d%d%lf%lf%lf", + &type, &style, &thickness, &wid, &ht) != 5) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free(s); + return NULL; + } + if ((s->for_arrow = make_arrow(type, style, thickness, wid, ht)) + == NULL) { +- put_msg(Err_arrow, "forward", line_no); ++ put_msg(Err_arrow, "forward", *line_no); + free(s); + return NULL; + } + } + if (ba) { +- if (get_line(fp) < 0 || +- sscanf(buf, "%d%d%lf%lf%lf", ++ if (get_line(fp, line, line_len, line_no) < 0 || ++ sscanf(*line, "%d%d%lf%lf%lf", + &type, &style, &thickness, &wid, &ht) != 5) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free_splinestorage(s); + return NULL; + } + if ((s->back_arrow = make_arrow(type, style, thickness, wid, ht)) + == NULL) { +- put_msg(Err_arrow, "backward", line_no); ++ put_msg(Err_arrow, "backward", *line_no); + free_splinestorage(s); + return NULL; + } +@@ -1170,9 +1254,9 @@ read_splineobject(FILE *fp) + + /* Read points */ + /* read first point of line */ +- ++line_no; ++ ++(*line_no); + if ((n = fscanf(fp, "%d%d", &x, &y)) != 2) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free_splinestorage(s); + return NULL; + }; +@@ -1186,15 +1270,15 @@ read_splineobject(FILE *fp) + if (!v30_flag) + npts = 1000000; + if (npts < 2) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free_splinestorage(s); + return NULL; + } + for (--npts; npts > 0; --npts) { + /* keep track of newlines for line counter */ +- count_lines_correctly(fp); ++ count_lines_correctly(fp, line_no); + if (fscanf(fp, "%d%d", &x, &y) != 2) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free_splinestorage(s); + return NULL; + }; +@@ -1224,9 +1308,9 @@ read_splineobject(FILE *fp) + ptr = s->controls; + while (ptr) { /* read controls */ + /* keep track of newlines for line counter */ +- count_lines_correctly(fp); ++ count_lines_correctly(fp, line_no); + if ((n = fscanf(fp, "%lf", &control_s)) != 1) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free_splinestorage(s); + return NULL; + } +@@ -1249,9 +1333,9 @@ read_splineobject(FILE *fp) + } + /* Read controls from older versions */ + /* keep track of newlines for line counter */ +- count_lines_correctly(fp); ++ count_lines_correctly(fp, line_no); + if ((n = fscanf(fp, "%lf%lf%lf%lf", &lx, &ly, &rx, &ry)) != 4) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + free_splinestorage(s); + return NULL; + } +@@ -1264,9 +1348,9 @@ read_splineobject(FILE *fp) + cp->rx = rx; cp->ry = ry; + while (--c) { + /* keep track of newlines for line counter */ +- count_lines_correctly(fp); ++ count_lines_correctly(fp, line_no); + if (fscanf(fp, "%lf%lf%lf%lf", &lx, &ly, &rx, &ry) != 4) { +- put_msg(Err_incomp, "spline", line_no); ++ put_msg(Err_incomp, "spline", *line_no); + cp->next = NULL; + free_splinestorage(s); + return NULL; +@@ -1289,13 +1373,37 @@ read_splineobject(FILE *fp) + return s; + } + ++static char * ++find_end(const char *str, int v30flag) ++{ ++ const char endmark[] = "\\001"; ++ char *end; ++ ++ if (v30flag) { ++ /* A string is terminated with the literal '\001', ++ and 8-bit characters may be represented as \xxx */ ++ end = strstr(str, endmark); ++ /* is this not '\\001', or '\\\\001', etc? */ ++ while (end && backslash_count(str, end - str) % 2 == 0) ++ end = strstr(end + 3, endmark); ++ } else { ++ /* The text object is terminated by a CONTROL-A. ++ If there is no CONTROL-A on this line, then this ++ must be a multi-line text object. */ ++ end = strchr(str, '\1'); ++ } ++ return end; ++} ++ ++ + static F_text * +-read_textobject(FILE *fp) ++read_textobject(FILE *fp, char **restrict line, size_t *line_len, int *line_no) + { + F_text *t; +- int n, ignore = 0; +- char s[BUFSIZ], s_temp[BUFSIZ], junk[2]; +- int more, len, l; ++ bool freestart = false; ++ int i, n; ++ char *end, *start; ++ size_t len; + + Text_malloc(t); + t->font = 0; +@@ -1303,32 +1411,101 @@ read_textobject(FILE *fp) + t->comments = NULL; + t->next = NULL; + +- if (v30_flag) { /* order of parms is more like other objects now, +- string is now terminated with the literal '\001', +- and 8-bit characters are represented as \xxx */ +- +- n = sscanf(buf, "%*d%d%d%d%d%d%lf%lf%d%lf%lf%d%d%[^\n]", +- &t->type, &t->color, &t->depth, &t->pen, +- &t->font, &t->size, &t->angle, +- &t->flags, &t->height, &t->length, +- &t->base_x, &t->base_y, s); ++ n = sscanf(*line, "%*d%d%d%d%d%d%lf%lf%d%lf%lf%d%d %n", ++ &t->type, &t->color, &t->depth, &t->pen, &t->font, ++ &t->size, &t->angle, &t->flags, &t->height, &t->length, ++ &t->base_x, &t->base_y, &i); ++ if (n != 12) { ++ put_msg(Err_incomp, "text", *line_no); ++ free(t); ++ return NULL; ++ } ++ start = *line + i; ++ end = find_end(start, v30_flag); ++ ++ if (end) { ++ *end = '\0'; ++ len = end - start; + } else { +- /* The text object is terminated by a CONTROL-A, so we read +- everything up to the CONTROL-A and then read that character. +- If we do not find the CONTROL-A on this line then this must +- be a multi-line text object and we will have to read more. */ +- +- n = sscanf(buf,"%*d%d%d%lf%d%d%d%lf%d%lf%lf%d%d%[^\1]%1[\1]", +- &t->type, &t->font, &t->size, &t->pen, +- &t->color, &t->depth, &t->angle, +- &t->flags, &t->height, &t->length, +- &t->base_x, &t->base_y, s, junk); +- } +- if ((n != 14) && (n != 13)) { +- put_msg(Err_incomp, "text", line_no); +- free(t); +- return NULL; ++ ssize_t chars; ++ char *next; ++ ++ len = strlen(start); ++ start[len++] = '\n'; /* put back the newline */ ++ ++ /* allocate plenty of space */ ++ next = malloc(len + BUFSIZ); ++ if (next == NULL) { ++ put_msg(Err_mem); ++ free(t); ++ return NULL; ++ } ++ memcpy(next, start, len); ++ ++ while ((chars = getline(line, line_len, fp)) != -1) { ++ ++(*line_no); ++ end = find_end(*line, v30_flag); ++ if (end) { ++ *end = '\0'; ++ next = realloc(next, len + end - *line + 1); ++ memcpy(next + len, *line, end - *line + 1); ++ len += end - *line; ++ break; ++ } else { ++ if (**line + chars - 1 == '\n' && chars > 1 && ++ **line + chars - 2 == '\r') ++ (*line)[chars-- - 2] = '\n'; ++ next = realloc(next, len + chars + 1); ++ memcpy(next + len, *line, chars + 1); ++ len += chars; ++ } ++ } ++ start = next; ++ freestart = true; ++ } ++ ++ /* convert any \xxx to characters */ ++ if (v30_flag && (end = strchr(start, '\\'))) { ++ unsigned char num; ++ char *c = start; ++ size_t l; ++ ++ len = end - start; ++ l = len; ++ while (c[l] != '\0') { ++ if (c[l] == '\\') { ++ /* convert 3 digit octal value */ ++ if (isdigit(c[l+1]) && c[l+2] != '\0' && ++ c[l+3] != '\0') { ++ if (sscanf(c+l+1, "%3hho", &num) != 1) { ++ put_msg("Error in parsing text string on line %d", ++ *line_no); ++ return NULL; ++ } ++ /* no check of unsigned char overflow */ ++ c[len++] = num; ++ l += 3; ++ } else { ++ /* an escaped char is un-escaped */ ++ c[len++] = c[++l]; ++ } ++ } else { ++ c[len++] = c[l]; ++ } ++ ++l; ++ } ++ c[len] = '\0'; /* terminate */ ++ } ++ ++ t->cstring = malloc(len + 1); ++ if (t->cstring == NULL) { ++ put_msg(Err_mem); ++ free(t); ++ return NULL; + } ++ memcpy(t->cstring, start, len + 1); ++ if (freestart) ++ free(start); + + if (font_size != 0.0) { + /* scale length/height of text by ratio of requested font size to actual size */ +@@ -1338,89 +1515,6 @@ read_textobject(FILE *fp) + } + if (t->size <= 0.0) + t->size = (float) DEFAULT_FONT_SIZE; +- more = 0; +- if (!v30_flag && n == 13) +- more = 1; /* in older xfig there is more if ^A wasn't found yet */ +- else if (v30_flag) { /* in 3.0 there is more if \001 wasn't found */ +- len = strlen(s); +- if ((strcmp(&s[len-4],"\\001") == 0) && /* if we find '\000' */ +- !(backslash_count(s, len-5) % 2)) { /* and not '\\000' */ +- more = 0; /* then there are no more lines */ +- s[len-4]='\0'; /* and get rid of the '\001' */ +- } else { +- more = 1; +- s[len++]='\n'; /* put back the end of line char */ +- s[len] = '\0'; /* and terminate it */ +- } +- } +- if (more) { +- /* Read in the subsequent lines of the text if there are more */ +- do { +- ++line_no; /* As is done in get_line */ +- if (fgets(s_temp, BUFSIZ, fp) == NULL) +- break; +- len = strlen(s_temp)-1; /* ignore newline */ +- if (len > 0 && s_temp[len-1] == '\r') { /* strip any trailing CR */ +- s_temp[len-1] = '\0'; +- len--; +- } +- if (v30_flag) { +- if ((strncmp(&s_temp[len-4],"\\001",4) == 0) && +- !(backslash_count(s_temp, len-5) % 2)) { +- n=0; /* found the '\001', set n to stop */ +- s_temp[len-4]='\0'; /* and get rid of the '\001' */ +- } else { +- n=1; /* keep going (more lines) */ +- } +- } else { +- n = sscanf(buf, "%[^\1]%[\1]", s_temp, junk); +- } +- /* Safety check */ +- if (strlen(s)+1 + strlen(s_temp)+1 > BUFSIZ) { +- /* Too many characters. Ignore the rest. */ +- ignore = 1; +- } +- if (!ignore) +- strcat(s, s_temp); +- } while (n == 1); +- } +- +- if (v30_flag) { /* now convert any \xxx to ascii characters */ +- if (strchr(s,'\\')) { +- unsigned int num; +- len = strlen(s); +- for (l=0,n=0; l < len; ++l) { +- if (s[l]=='\\') { +- /* a backslash, see if a digit follows */ +- if (l < len && isdigit(s[l+1])) { +- /* yes, scan for 3 digit octal value */ +- if (sscanf(&s[l+1],"%3o",&num)!=1) { +- put_msg("Error in parsing text string on line %d", +- line_no); +- return NULL; +- } +- buf[n++]= (unsigned char) num; /* put char in */ +- l += 3; /* skip over digits */ +- } else { +- buf[n++] = s[++l]; /* some other escaped character */ +- } +- } else { +- buf[n++] = s[l]; /* ordinary character */ +- } +- } +- buf[n]='\0'; /* terminate */ +- strcpy(s,buf); /* copy back to s */ +- } +- } +- if (strlen(s) == 0) +- (void)strcpy(s, " "); +- t->cstring = calloc((unsigned)(strlen(s)), sizeof(char)); +- if (NULL == t->cstring) { +- put_msg(Err_mem); +- free(t); +- return NULL; +- } +- (void)strcpy(t->cstring, s+1); + + if (!v21_flag && (t->font == 0 || t->font == DEFAULT)) + t->flags = ((t->flags != DEFAULT) ? t->flags : 0) +@@ -1431,11 +1525,11 @@ read_textobject(FILE *fp) + | PSFONT_TEXT; + + if (INVALID_TEXT(t)) { +- put_msg(Err_invalid, "text", line_no); ++ put_msg(Err_invalid, "text", *line_no); + free_text(&t); + return NULL; + } +- fix_and_note_color(&t->color); ++ fix_and_note_color(&t->color, *line_no); + t->comments = attach_comments(); /* attach any comments */ + return t; + } +@@ -1443,18 +1537,19 @@ read_textobject(FILE *fp) + + /* count consecutive backslashes backwards */ + +-static int +-backslash_count(char cp[], int start) ++static ptrdiff_t ++backslash_count(const char *restrict cp, ptrdiff_t start) + { +- int i, count = 0; ++ ptrdiff_t i; ++ ptrdiff_t count = 0; + +- for(i=start; i>=0; i--) { +- if (cp[i] == '\\') +- count++; +- else +- break; +- } +- return count; ++ for(i = start; i >= 0; --i) { ++ if (cp[i] == '\\') ++ ++count; ++ else ++ break; ++ } ++ return count; + } + + /* attach comments in linked list */ +@@ -1483,55 +1578,64 @@ attach_comments(void) + return icomp; + } + ++/* save a comment line to be stored with the *subsequent* object */ ++ + static int +-get_line(FILE *fp) ++save_comment(char *restrict line, size_t len) + { +- int len; +- while (1) { +- if (NULL == fgets(buf, BUFSIZ, fp)) { +- return -1; +- } +- ++line_no; +- if (*buf == '#') { /* save any comments */ +- if (save_comment() < 0) +- return -1; +- /* skip empty lines */ +- } else if (*buf != '\n' || !(*buf == '\r' && buf[1] == '\n')) { +- len = strlen(buf); +- /* remove newline and possibly a carriage return */ +- if (buf[len-1] == '\n') +- buf[len - (buf[len-2] == '\r' ? 2 : 1)] = '\0'; +- return 1; +- } +- } +-} ++ int i; + +-/* save a comment line to be stored with the *subsequent* object */ ++ /* skip too many comment lines */ ++ if (numcom == MAXCOMMENTS) ++ return 2; ++ ++ /* remove one leading blank from the comment, if there is one */ ++ i = 1; ++ if (line[i] == ' ') ++ i = 2; ++ ++ /* see if we've allocated space for this comment */ ++ if (comments[numcom]) ++ free(comments[numcom]); ++ if ((comments[numcom] = malloc(len + (1 - i))) == NULL) ++ return -1; + +-static int +-save_comment(void) ++ strcpy(comments[numcom++], &line[i]); ++ return 1; ++} ++ ++static ssize_t ++get_line(FILE *fp, char **restrict line, size_t *line_len, int *line_no) + { +- int i; ++ ssize_t chars; + +- /* skip too many comment lines */ +- if (numcom == MAXCOMMENTS) +- return 2; +- i=strlen(buf); +- /* see if we've allocated space for this comment */ +- if (comments[numcom]) +- free(comments[numcom]); +- if ((comments[numcom] = malloc(i+1)) == NULL) +- return -1; +- /* remove any newline */ +- if (buf[i-1] == '\n') +- buf[i-1] = '\0'; +- i=1; +- if (buf[1] == ' ') /* remove one leading blank from the comment, if there is one */ +- i=2; +- strcpy(comments[numcom++], &buf[i]); +- return 1; ++ while ((chars = getline(line, line_len, fp)) != -1) { ++ ++(*line_no); ++ /* skip empty lines */ ++ if (**line == '\n' || (**line == '\r' && ++ chars == 2 && (*line)[1] == '\n')) ++ continue; ++ /* remove newline and possibly a carriage return */ ++ if ((*line)[chars-1] == '\n') { ++ chars -= (*line)[chars - 2] == '\r' ? 2 : 1; ++ (*line)[chars] = '\0'; ++ } ++ /* save any comments */ ++ if (**line == '#') { ++ if (save_comment(*line, (size_t)chars) < 0) ++ return -1; ++ continue; ++ } ++ /* return the line */ ++ return chars; ++ } ++ /* chars == -1 */ ++ return chars; ++ /* getline() only fails with EINVAL, and probably ENOMEM from malloc(). ++ No use to check for errno. */ + } + ++ + /* skip to the end of the current line and any subsequent blank lines */ + + static void +@@ -1688,15 +1792,15 @@ static int pop() { + */ + + static void +-count_lines_correctly(FILE *fp) ++count_lines_correctly(FILE *fp, int *line_no) + { + int cc; + do { +- cc = getc(fp); +- if (cc == '\n') { +- ++line_no; +- cc=getc(fp); +- } ++ cc = getc(fp); ++ if (cc == '\n') { ++ ++(*line_no); ++ cc=getc(fp); ++ } + } while (cc == ' ' || cc == '\t'); + ungetc(cc,fp); + } +--- a/fig2dev/read1_3.c ++++ b/fig2dev/read1_3.c +@@ -51,8 +51,6 @@ + + extern F_arrow *forward_arrow(void), *backward_arrow(void); + extern int figure_modified; +-//extern int line_no; +-extern int num_object; + + static F_ellipse *read_ellipseobject(FILE *fp); + static F_line *read_lineobject(FILE *fp); +@@ -103,7 +101,6 @@ read_1_3_objects(FILE *fp, F_compound *o + ll = (ll->next = l); + else + ll = obj->lines = l; +- num_object++; + break; + case OBJ_SPLINE : + if ((s = read_splineobject(fp)) == NULL) return(-1); +@@ -111,7 +108,6 @@ read_1_3_objects(FILE *fp, F_compound *o + ls = (ls->next = s); + else + ls = obj->splines = s; +- num_object++; + break; + case OBJ_ELLIPSE : + if ((e = read_ellipseobject(fp)) == NULL) return(-1); +@@ -119,7 +115,6 @@ read_1_3_objects(FILE *fp, F_compound *o + le = (le->next = e); + else + le = obj->ellipses = e; +- num_object++; + break; + case OBJ_ARC : + if ((a = read_arcobject(fp)) == NULL) return(-1); +@@ -127,7 +122,6 @@ read_1_3_objects(FILE *fp, F_compound *o + la = (la->next = a); + else + la = obj->arcs = a; +- num_object++; + break; + case OBJ_TEXT : + if ((t = read_textobject(fp)) == NULL) return(-1); +@@ -135,7 +129,6 @@ read_1_3_objects(FILE *fp, F_compound *o + lt = (lt->next = t); + else + lt = obj->texts = t; +- num_object++; + break; + case OBJ_COMPOUND : + if ((c = read_compoundobject(fp)) == NULL) return(-1); +@@ -143,7 +136,6 @@ read_1_3_objects(FILE *fp, F_compound *o + lc = (lc->next = c); + else + lc = obj->compounds = c; +- num_object++; + break; + default: + put_msg("Incorrect object code %d", object); diff -Nru fig2dev-3.2.7a/debian/patches/series fig2dev-3.2.7a/debian/patches/series --- fig2dev-3.2.7a/debian/patches/series 2019-12-04 21:12:49.000000000 +0000 +++ fig2dev-3.2.7a/debian/patches/series 2020-01-07 18:53:09.000000000 +0000 @@ -13,3 +13,5 @@ 38_omit_showpage.patch 40_circle_arrowhead.patch 41_CVE-2019-19555.patch +42_CVE-2019-19746.patch +43_fgets2getline.patch