1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2015 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  * Parts Copyright (c) 2016-2021 by Thomas Loimer
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13  * the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 #if defined HAVE_CONFIG_H && !defined VERSION
20 #include "config.h"
21 #endif
22 
23 #include <errno.h>
24 #include <time.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_STRINGS_H
29 #include <strings.h>
30 #endif
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <X11/Xlib.h>
34 
35 #include "resources.h"
36 #include "object.h"
37 #include "mode.h"
38 #include "f_neuclrtab.h"
39 #include "f_read.h"
40 #include "f_save.h"		/* write_file() */
41 #include "f_util.h"
42 #include "u_create.h"		/* new_string() */
43 #include "u_fonts.h"		/* psfontnum() */
44 #include "w_file.h"		/* renamefile() */
45 #include "w_color.h"		/* YStoreColors(), alloc_color_cells() */
46 #include "w_cursor.h"
47 #include "w_msgpanel.h"
48 #include "w_util.h"		/* popup_query(), panel_get_value() */
49 #include "w_zoom.h"		/* display_zoomscale */
50 #include "xfig_math.h"
51 
52 
53 /* LOCALS */
54 
55 int	count_colors(void);
56 int	count_pixels(void);
57 
58 
59 /* PROCEDURES */
60 
61 void beep (void);
62 void alloc_imagecolors (int num);
63 void add_all_pixels (void);
64 void remap_image_colormap (void);
65 void extract_cmap (void);
66 void readjust_cmap (void);
67 void free_pixmaps (F_compound *obj);
68 void add_recent_file (char *file);
69 int strain_out (char *name);
70 void finish_update_xfigrc (void);
71 
72 int
emptyname(char * name)73 emptyname(char *name)
74 {
75     if  (name == NULL || *name == '\0') {
76 	return (1);
77     } else {
78 	return (0);
79     }
80 }
81 
82 int
emptyname_msg(char * name,char * msg)83 emptyname_msg(char *name, char *msg)
84 {
85 	int	returnval;
86 
87 	if ((returnval = emptyname(name))) {
88 		put_msg("No file name specified, %s command ignored", msg);
89 		beep();
90 	}
91 	return returnval;
92 }
93 
94 int
emptyfigure(void)95 emptyfigure(void)
96 {
97 	if (objects.texts != NULL)
98 		return 0;
99 	if (objects.lines != NULL)
100 		return 0;
101 	if (objects.ellipses != NULL)
102 		return 0;
103 	if (objects.splines != NULL)
104 		return 0;
105 	if (objects.arcs != NULL)
106 		return 0;
107 	if (objects.compounds != NULL)
108 		return 0;
109 	return 1;
110 }
111 
112 int
emptyfigure_msg(char * msg)113 emptyfigure_msg(char *msg)
114 {
115 	int	returnval;
116 
117 	if ((returnval = emptyfigure())) {
118 		put_msg("Empty figure, %s command ignored", msg);
119 		beep();
120 	}
121 	return returnval;
122 }
123 
124 int
change_directory(char * path)125 change_directory(char *path)
126 {
127     static char	cwd[PATH_MAX];
128 
129     if (!strcmp(path, cwd) || path == NULL || *path == '\0')
130 	return 0;
131     if (chdir(path) == -1) {
132 	file_msg("Can't go to directory %s, : %s", path, strerror(errno));
133 	return 1;
134     }
135     strcpy(cwd, path);
136     return 0;
137 }
138 
get_directory(char * direct)139 int get_directory(char *direct)
140 {
141 
142 #ifdef HAVE_GETCWD
143     extern char	   *getcwd(char *, size_t);
144 
145     if (getcwd(direct, PATH_MAX) == NULL) {	/* get current working dir */
146 	file_msg("Can't get current directory");
147 	beep();
148 #else
149     extern char	   *getwd();
150 
151     if (getwd(direct) == NULL) {		/* get current working dir */
152 	file_msg("%s", direct);			/* err msg is in direct var */
153 	beep();
154 #endif
155 	*direct = '\0';
156 	return 0;
157     }
158     return 1;
159 }
160 
161 #ifndef S_IWUSR
162 #define S_IWUSR 0000200
163 #endif /* S_IWUSR */
164 
165 #ifndef S_IWGRP
166 #define S_IWGRP 0000020
167 #endif /* S_IWGRP */
168 
169 #ifndef S_IWOTH
170 #define S_IWOTH 0000002
171 #endif /* S_IWOTH */
172 
173 int
174 ok_to_write(char *file_name, char *op_name)
175 {
176     struct stat	    file_status;
177     char	    string[180];
178 
179     if (stat(file_name, &file_status) == 0) {	/* file exists */
180 	if (file_status.st_mode & S_IFDIR) {
181 	    put_msg("\"%s\" is a directory", file_name);
182 	    beep();
183 	    return 0;
184 	}
185 	if (file_status.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) {
186 	    /* writing is permitted by SOMEONE */
187 	    if (access(file_name, W_OK)) {
188 		put_msg("Write permission for \"%s\" is denied", file_name);
189 		beep();
190 		return 0;
191 	    } else {
192 		if (warnexist) {
193 		    sprintf(string, "\"%s\" already exists.\nDo you want to overwrite it?", file_name);
194 		    if (popup_query(QUERY_YESNO, string) != RESULT_YES) {
195 			put_msg("%s cancelled", op_name);
196 			return 0;
197 		    }
198 		} else {
199 		    return 1;
200 		}
201 	    }
202 	} else {
203 	    put_msg("\"%s\" is read only", file_name);
204 	    beep();
205 	    return 0;
206 	}
207     } else {
208 	if (errno != ENOENT)
209 	    return 0;		/* file does exist but stat fails */
210     }
211 
212     return  1;
213 }
214 
215 /* for systems without basename() (e.g. SunOS 4.1.3) */
216 /* strip any path from filename */
217 
218 char *
219 xf_basename(char *filename)
220 {
221 	char	*p;
222 	if (filename == NULL || *filename == '\0')
223 		return filename;
224 	if ((p = strrchr(filename,'/')))
225 		return ++p;
226 	else
227 		return filename;
228 }
229 
230 static int	  scol, ncolors;
231 static int	  num_oldcolors = -1;
232 static Boolean	  usenet;
233 static int	  npixels;
234 
235 #define REMAP_MSG	"Remapping picture colors..."
236 #define REMAP_MSG2	"Remapping picture colors...Done"
237 
238 /* remap the colors for all the pictures in the picture repository */
239 
240 void remap_imagecolors(void)
241 {
242     int		    i;
243 
244     /* if monochrome, return */
245     if (tool_cells <= 2 || appres.monochrome)
246 	return;
247 
248     npixels = 0;
249 
250     /* first see if there are enough colorcells for all image colors */
251     usenet = False;
252 
253     /* see if the total number of colors will fit without using the neural net */
254     ncolors = count_colors();
255     if (ncolors == 0)
256 	return;
257 
258     put_msg(REMAP_MSG);
259     set_temp_cursor(wait_cursor);
260     app_flush();
261 
262     if (ncolors > appres.max_image_colors) {
263 	if (appres.DEBUG)
264 		fprintf(stderr,"More colors (%d) than allowed (%d), using neural net\n",
265 				ncolors,appres.max_image_colors);
266 	ncolors = appres.max_image_colors;
267 	usenet = True;
268     }
269 
270     /* if this is the first image, allocate the number of colorcells we need */
271     if (num_oldcolors != ncolors) {
272 	if (num_oldcolors != -1) {
273 	    unsigned long   pixels[MAX_USR_COLS];
274 	    for (i=0; i<num_oldcolors; i++)
275 		pixels[i] = image_cells[i].pixel;
276 	    if (tool_vclass == PseudoColor)
277 		XFreeColors(tool_d, tool_cm, pixels, num_oldcolors, 0);
278 	}
279 	alloc_imagecolors(ncolors);
280 	/* hmm, we couldn't get that number of colors anyway; use the net, Luke */
281 	if (ncolors > avail_image_cols) {
282 	    usenet = True;
283 	    if (appres.DEBUG)
284 		fprintf(stderr,"More colors (%d) than available (%d), using neural net\n",
285 				ncolors,avail_image_cols);
286 	}
287 	num_oldcolors = avail_image_cols;
288 	if (avail_image_cols < 2 && ncolors >= 2) {
289 	    file_msg("Cannot allocate even 2 colors for pictures");
290 	    reset_cursor();
291 	    num_oldcolors = -1;
292 	    reset_cursor();
293 	    put_msg(REMAP_MSG2);
294 	    app_flush();
295 	    return;
296 	}
297     }
298     reset_cursor();
299 
300     if (usenet) {
301 	int	stat;
302 	int	mult = 1;
303 
304 	/* check if user pressed cancel button (in file preview) */
305 	if (check_cancel())
306 	    return;
307 
308 	/* count total number of pixels in all the pictures */
309 	npixels = count_pixels();
310 
311 	/* check if user pressed cancel button */
312 	if (check_cancel())
313 	    return;
314 
315 	/* initialize the neural network */
316 	/* -1 means can't alloc memory, -2 or more means must have that many times
317 		as many pixels */
318 	set_temp_cursor(wait_cursor);
319 	if ((stat=neu_init(npixels)) <= -2) {
320 	    mult = -stat;
321 	    npixels *= mult;
322 	    /* try again with more pixels */
323 	    stat = neu_init2(npixels);
324 	}
325 	if (stat == -1) {
326 	    /* couldn't alloc memory for network */
327 	    fprintf(stderr,"Can't alloc memory for neural network\n");
328 	    reset_cursor();
329 	    put_msg(REMAP_MSG2);
330 	    app_flush();
331 	    return;
332 	}
333 	/* now add all pixels to the samples */
334 	for (i=0; i<mult; i++)
335 	    add_all_pixels();
336 
337 	/* make a new colortable with the optimal colors */
338 	avail_image_cols = neu_clrtab(avail_image_cols);
339 
340 	/* now change the color cells with the new colors */
341 	/* clrtab[][] is the colormap produced by neu_clrtab */
342 	for (i=0; i<avail_image_cols; i++) {
343 	    image_cells[i].red   = (unsigned short) clrtab[i][N_RED] << 8;
344 	    image_cells[i].green = (unsigned short) clrtab[i][N_GRN] << 8;
345 	    image_cells[i].blue  = (unsigned short) clrtab[i][N_BLU] << 8;
346 	}
347 	YStoreColors(tool_cm, image_cells, avail_image_cols);
348 	reset_cursor();
349 
350 	/* check if user pressed cancel button */
351 	if (check_cancel())
352 	    return;
353 
354 	/* get the new, mapped indices for the image colormap */
355 	remap_image_colormap();
356     } else {
357 	/*
358 	 * Extract the RGB values from the image's colormap and allocate
359 	 * the appropriate X colormap entries.
360 	 */
361 	scol = 0;	/* global color counter */
362 	set_temp_cursor(wait_cursor);
363 	extract_cmap();
364 	for (i=0; i<scol; i++) {
365 	    image_cells[i].flags = DoRed|DoGreen|DoBlue;
366 	}
367 	YStoreColors(tool_cm, image_cells, scol);
368 	scol = 0;	/* global color counter */
369 	readjust_cmap();
370 	if (appres.DEBUG)
371 	    fprintf(stderr,"Able to use %d colors without neural net\n",scol);
372 	reset_cursor();
373     }
374     put_msg(REMAP_MSG2);
375     app_flush();
376 }
377 
378 /* allocate the color cells for the pictures */
379 
380 void alloc_imagecolors(int num)
381 {
382     int		    i;
383 
384     /* see if we can get all user wants */
385     avail_image_cols = num;
386     for (i=0; i<avail_image_cols; i++) {
387 	image_cells[i].flags = DoRed|DoGreen|DoBlue;
388 	if (!alloc_color_cells(&image_cells[i].pixel, 1)) {
389 	    break;
390 	}
391     }
392     avail_image_cols = i;
393 }
394 
395 /* count the number of colors in all the pictures in the picture repository */
396 
397 int
398 count_colors(void)
399 {
400 	int		ncolors = 0;
401 	struct _pics	*pics;
402 
403 	for (pics = pictures; pics; pics = pics->next)
404 		if (pics->bitmap != NULL && pics->numcols > 0)
405 			ncolors += pics->numcols;
406 	return ncolors;
407 }
408 
409 int
410 count_pixels(void)
411 {
412 	int		npixels = 0;
413 	struct _pics	*pics;
414 
415 	for (pics = pictures; pics; pics = pics->next)
416 		if (pics->bitmap != NULL && pics->numcols > 0)
417 			npixels += pics->bit_size.x * pics->bit_size.y;
418 	return npixels;
419 }
420 
421 void
422 readjust_cmap(void)
423 {
424 	struct _pics	*pics;
425 	int		i, j;
426 
427 	/* first adjust the colormaps in the repository */
428 	for (pics = pictures; pics; pics = pics->next)
429 		if (pics->bitmap != NULL && pics->numcols > 0) {
430 			for (i = 0; i < pics->numcols; ++i) {
431 				j = pics->cmap[i].pixel;
432 				pics->cmap[i].pixel = image_cells[j].pixel;
433 				++scol;
434 			}
435 		}
436 
437 	/* now free up all pixmaps in picture objects */
438 	/* start with main list */
439 	free_pixmaps(&objects);
440 }
441 
442 void
443 free_pixmaps(F_compound *obj)
444 {
445 	F_line		*l;
446 	F_compound	*c;
447 
448 	/* traverse the compounds in this compound */
449 	for (c = obj->compounds; c != NULL; c = c->next) {
450 		free_pixmaps(c);
451 	}
452 	for (l = obj->lines; l != NULL; l = l->next) {
453 		if (l->type != T_PICTURE)
454 			continue;
455 		if (l->pic->pixmap != (Pixmap)0 &&
456 					l->pic->pic_cache->numcols > 0) {
457 			XFreePixmap(tool_d, l->pic->pixmap);
458 			/* this will force regeneration of the pixmap */
459 			l->pic->pixmap = (Pixmap)0;
460 			if (l->pic->mask != (Pixmap)0)
461 				XFreePixmap(tool_d, l->pic->mask);
462 			l->pic->mask = (Pixmap)0;
463 		}
464 	}
465 }
466 
467 void
468 extract_cmap(void)
469 {
470 	struct _pics	*pics;
471 	int		i;
472 
473 	/* extract the colormaps in the repository */
474 	for (pics = pictures; pics; pics = pics->next)
475 		if (pics->bitmap != NULL && pics->numcols > 0) {
476 			for (i = 0; i < pics->numcols; ++i) {
477 				image_cells[scol].red = pics->cmap[i].red << 8;
478 				image_cells[scol].green=pics->cmap[i].green <<8;
479 				image_cells[scol].blue = pics->cmap[i].blue <<8;
480 				pics->cmap[i].pixel = scol;
481 				++scol;
482 			}
483 		}
484 	/* now free up the pixmaps */
485 	free_pixmaps(&objects);
486 }
487 
488 void add_all_pixels(void)
489 {
490     struct _pics   *pics;
491     BYTE	   col[3];
492     int		   i, npix;
493     register unsigned char byte;
494 
495     for (pics = pictures; pics; pics = pics->next)
496 	if (pics->bitmap != NULL && pics->numcols > 0) {
497 	    /* now add each pixel to the sample list */
498 	    npix = pics->bit_size.x * pics->bit_size.y;
499 	    for (i=0; i < npix; i++) {
500 		/* check if user pressed cancel button */
501 		if (i%1000==0 && check_cancel())
502 		    return;
503 		byte = pics->bitmap[i];
504 		col[N_RED] = pics->cmap[byte].red;
505 		col[N_GRN] = pics->cmap[byte].green;
506 		col[N_BLU] = pics->cmap[byte].blue;
507 		neu_pixel(col);
508 	    }
509 	}
510 }
511 
512 void remap_image_colormap(void)
513 {
514     struct _pics   *pics;
515     BYTE	   col[3];
516     int		   i;
517     int		   p;
518 
519     for (pics = pictures; pics; pics = pics->next)
520 	if (pics->bitmap != NULL && pics->numcols > 0) {
521 	    for (i=0; i<pics->numcols; i++) {
522 		/* real color from the image */
523 		col[N_RED] = pics->cmap[i].red;
524 		col[N_GRN] = pics->cmap[i].green;
525 		col[N_BLU] = pics->cmap[i].blue;
526 		/* X color index from the mapping */
527 		p = neu_map_pixel(col);
528 		pics->cmap[i].pixel = image_cells[p].pixel;
529 	    }
530 	}
531     free_pixmaps(&objects);
532 }
533 
534 /* map the bytes in pic->pic_cache->bitmap to bits for monochrome display */
535 /* DESTROYS original pic->pic_cache->bitmap */
536 /* uses a Floyd-Steinberg algorithm from the pbmplus package */
537 
538 /* Here is the copyright notice:
539 **
540 ** Copyright (C) 1989 by Jef Poskanzer.
541 **
542 ** Permission to use, copy, modify, and distribute this software and its
543 ** documentation for any purpose and without fee is hereby granted, provided
544 ** that the above copyright notice appear in all copies and that both that
545 ** copyright notice and this permission notice appear in supporting
546 ** documentation.  This software is provided "as is" without express or
547 ** implied warranty.
548 **
549 */
550 
551 #define FS_SCALE 1024
552 #define HALF_FS_SCALE 512
553 #define MAXVAL (256*256)
554 
555 void map_to_mono(F_pic *pic)
556 {
557 	unsigned char *dptr = pic->pic_cache->bitmap;	/* 8-bit wide data pointer */
558 	unsigned char *bptr;			/* 1-bit wide bitmap pointer */
559 	int	   bitp;
560 	int	   col, row, limitcol;
561 	int	   width, height;
562 	long	   grey, threshval, sum;
563 	long	  *thiserr, *nexterr, *temperr;
564 	unsigned char *cP, *bP;
565 	Boolean	   fs_direction;
566 	int	   sbit;
567 
568 	width = pic->pic_cache->bit_size.x;
569 	height = pic->pic_cache->bit_size.y;
570 
571 	/* allocate space for 1-bit bitmap */
572 	if ((bptr = (unsigned char*)
573 	     malloc(sizeof(unsigned char) * (width+7)/8*height)) == NULL)
574 		return;
575 	thiserr = (long *) malloc(sizeof(long) * (width+2));
576 	nexterr = (long *) malloc(sizeof(long) * (width+2));
577 	/* initialize random seed */
578 	srandom( (int) (time((time_t *)NULL)^getpid()) );
579 	for (col=0; col<width+2; col++) {
580 	    /* (random errors in [-FS_SCALE/8 .. FS_SCALE/8]) */
581 	    thiserr[col] = ( random() % FS_SCALE - HALF_FS_SCALE) / 4;
582 	}
583 	fs_direction = True;
584 	threshval = FS_SCALE/2;
585 
586 	/* starting bit for left-hand scan */
587 	sbit = 1 << (7-((width-1)%8));
588 
589 	for (row=0; row<height; row++) {
590 	    for (col=0; col<width+2; col++)
591 		nexterr[col] = 0;
592 	    if (fs_direction) {
593 		col = 0;
594 		limitcol = width;
595 		cP = &dptr[row*width];
596 		bP = &bptr[row*(int)((width+7)/8)];
597 		bitp = 0x80;
598 	    } else {
599 		col = width - 1;
600 		limitcol = -1;
601 		cP = &dptr[row*width+col];
602 		bP = &bptr[(row+1)*(int)((width+7)/8) - 1];
603 		bitp = sbit;
604 	    }
605 	    do {
606 		grey =  pic->pic_cache->cmap[*cP].red   * 77  +	/* 0.30 * 256 */
607 			pic->pic_cache->cmap[*cP].green * 151 +	/* 0.59 * 256 */
608 			pic->pic_cache->cmap[*cP].blue  * 28;	/* 0.11 * 256 */
609 		sum = ( grey * FS_SCALE ) / MAXVAL + thiserr[col+1];
610 		if (sum >= threshval) {
611 		    *bP |= bitp;		/* white bit */
612 		    sum = sum - threshval - HALF_FS_SCALE;
613 		} else {
614 		    *bP &= ~bitp;		/* black bit */
615 		}
616 		if (fs_direction) {
617 		    bitp >>= 1;
618 		    if (bitp <= 0) {
619 			bP++;
620 			bitp = 0x80;
621 		    }
622 		} else {
623 		    bitp <<= 1;
624 		    if (bitp > 0x80) {
625 			bP--;
626 			bitp = 0x01;
627 		    }
628 		}
629 		if ( fs_direction )
630 		    {
631 		    thiserr[col + 2] += ( sum * 7 ) / 16;
632 		    nexterr[col    ] += ( sum * 3 ) / 16;
633 		    nexterr[col + 1] += ( sum * 5 ) / 16;
634 		    nexterr[col + 2] += ( sum     ) / 16;
635 
636 		    ++col;
637 		    ++cP;
638 		    }
639 		else
640 		    {
641 		    thiserr[col    ] += ( sum * 7 ) / 16;
642 		    nexterr[col + 2] += ( sum * 3 ) / 16;
643 		    nexterr[col + 1] += ( sum * 5 ) / 16;
644 		    nexterr[col    ] += ( sum     ) / 16;
645 
646 		    --col;
647 		    --cP;
648 		    }
649 		}
650 	    while ( col != limitcol );
651 	    temperr = thiserr;
652 	    thiserr = nexterr;
653 	    nexterr = temperr;
654 	    fs_direction = ! fs_direction;
655 	}
656 	free((char *) pic->pic_cache->bitmap);
657 	free((char *) thiserr);
658 	free((char *) nexterr);
659 	pic->pic_cache->bitmap = bptr;
660 	/* monochrome */
661 	pic->pic_cache->numcols = 0;
662 
663 	return;
664 }
665 
666 void beep(void)
667 {
668 	XBell(tool_d,0);
669 }
670 
671 /* this routine will safely copy overlapping strings */
672 /* p2 is copied to p1 and p1 is returned */
673 /* p1 must be < p2 */
674 
675 char *
676 safe_strcpy(char *p1, char *p2)
677 {
678     char *c1;
679     c1 = p1;
680     for ( ; *p2; )
681 	*p1++ = *p2++;
682     *p1 = '\0';
683     return c1;
684 }
685 
686 /* gunzip file if necessary */
687 
688 Boolean
689 uncompress_file(char *name)
690 {
691     char	    plainname[PATH_MAX];
692     char	    dirname[PATH_MAX];
693     char	    tmpfile[PATH_MAX];
694     char	    unc[PATH_MAX+20];	/* temp buffer for uncompress/gunzip command */
695     char	   *c;
696     struct stat	    status;
697 
698     strcpy(tmpfile, name);		/* save original name */
699     strcpy(plainname, name);
700     c = strrchr(plainname, '.');
701     if (c) {
702       if (strcmp(c, ".gz") == 0 || strcmp(c, ".Z") == 0 || strcmp(c, ".z") == 0)
703 	*c = '\0';
704     }
705 
706     if (stat(name, &status)) {    /* first see if file exists AS NAMED */
707       /* no, try without .gz etc suffix */
708       strcpy(name, plainname);
709       if (stat(name, &status)) {
710 	/* no, try with a .z */
711 	sprintf(name, "%s.z", plainname);
712 	if (stat(name, &status)) {
713 	  /* no, try with .Z suffix */
714 	  sprintf(name, "%s.Z", plainname);
715 	  if (stat(name, &status)) {
716 	    /* no, try .gz */
717 	    sprintf(name, "%s.gz", plainname);
718 	    if (stat(name, &status)) {
719 	      /* none of the above, return original name and False status */
720 	      strcpy(name, tmpfile);
721 	      return False;
722 	    }
723 	  }
724 	}
725       }
726     }
727     /* file doesn't have .gz etc suffix anymore, return modified name */
728     if (strcmp(name, plainname) == 0) return True;
729 
730     strcpy(dirname, name);
731     c = strrchr(dirname, '/');
732     if (c) *c = '\0';
733     else strcpy(dirname, ".");
734 
735     if (access(dirname, W_OK) == 0) {  /* OK - the directory is writable */
736       sprintf(unc, "gunzip -q %s", name);
737       if (system(unc) != 0)
738 	file_msg("Couldn't uncompress the file: \"%s\"", unc);
739       strcpy(name, plainname);
740     } else {  /* the directory is not writable */
741       /* create a path to TMPDIR in case we need to uncompress a read-only file to there */
742       c = strrchr(plainname, '/');
743       if (c)
744 	  sprintf(tmpfile, "%s%s", TMPDIR, c);
745       else
746 	  sprintf(tmpfile, "%s/%s", TMPDIR, plainname);
747       sprintf(unc, "gunzip -q -c %s > %s", name, tmpfile);
748       if (system(unc) != 0)
749 	  file_msg("Couldn't uncompress the file: \"%s\"", unc);
750       file_msg ("Uncompressing file %s in %s because it is in a read-only directory",
751 		name, TMPDIR);
752       strcpy(name, tmpfile);
753     }
754     return True;
755 }
756 
757 /*************************************************************/
758 /* Read the user's .xfigrc file in his home directory to get */
759 /* settings from previous session.                           */
760 /*************************************************************/
761 
762 #define RC_BUFSIZ 1000
763 
764 void read_xfigrc(void)
765 {
766     char  line[RC_BUFSIZ+1], *word, *opnd;
767     int	  i, len;
768     FILE *xfigrc;
769 
770     num_recent_files = 0;
771     max_recent_files = DEF_RECENT_FILES;	/* default if none found in .xfigrc */
772 
773     /* make the filename from the user's home path and ".xfigrc" */
774     strcpy(xfigrc_name,userhome);
775     strcat(xfigrc_name,"/.xfigrc");
776     xfigrc = fopen(xfigrc_name,"r");
777     if (xfigrc == 0)
778 	return;		/* no .xfigrc file */
779 
780     /* there must not be any whitespace between word and ":" */
781     while (fgets(line, RC_BUFSIZ, xfigrc) != NULL) {
782 	word = strtok(line, ": \t");
783 	opnd = strtok(NULL, "\n");		/* parse operand and remove newline */
784 	if (!word || !opnd)
785 	    continue;
786 	/* find first non-blank */
787 	for (i=0, len=strlen(opnd); i<len; i++, opnd++)
788 	    if (*opnd != ' ' && *opnd != '\t')
789 		break;
790 	/* nothing, do next */
791 	if (i==len)
792 	    continue;
793 	if (strcasecmp(word, "max_recent_files") == 0)
794 	    max_recent_files = min2(MAX_RECENT_FILES, atoi(opnd));
795 	else if (strcasecmp(word, "file") == 0)
796 	    add_recent_file(opnd);
797     }
798     fclose(xfigrc);
799 }
800 
801 /* add next token from 'line' to recent file list */
802 
803 void add_recent_file(char *file)
804 {
805     char  *name;
806 
807     if (file == NULL)
808 	return;
809     if (num_recent_files >= MAX_RECENT_FILES || num_recent_files >= max_recent_files)
810 	return;
811     name = new_string(strlen(file)+3);	/* allow for file number (1), blank (1) and NUL */
812     sprintf(name,"%1d %s",num_recent_files+1,file);
813     recent_files[num_recent_files].name = name;
814     num_recent_files++;
815 }
816 
817 
818 static FILE	*xfigrc, *tmpf;
819 static char	tmpname[PATH_MAX];
820 
821 /* rewrite .xfigrc file with current list of recent files */
822 
823 void update_recent_files(void)
824 {
825     int	    i;
826 
827     /* copy all lines without the "file" spec into a temp file */
828     if (strain_out("file") != 0) {
829 	if (tmpf != 0)
830 	    fclose(tmpf);
831 	if (xfigrc != 0)
832 	    fclose(xfigrc);
833 	return;	/* problem creating temp file */
834     }
835 
836     /* now append file list */
837     for (i=0; i<num_recent_files; i++)
838 	fprintf(tmpf, "file: %s\n",&recent_files[i].name[2]);  /* point past the number */
839     /* close and rename the files */
840     finish_update_xfigrc();
841 }
842 
843 /* update a named entry in the user's .xfigrc file */
844 
845 void update_xfigrc(char *name, char *string)
846 {
847     /* first copy all lines except the one we want to the temp file */
848     strain_out(name);
849     /* add the new name/string value to the file */
850     fprintf(tmpf, "%s: %s\n",name,string);
851     /* close and rename the files */
852     finish_update_xfigrc();
853 }
854 
855 /* copy all lines from .xfigrc without the "name" spec into a temp file (global tmpf) */
856 /* temp file is left open after copy */
857 
858 int strain_out(char *name)
859 {
860     char    line[RC_BUFSIZ+1], *tok;
861     int fd;
862 
863     /* make a temp filename in the user's home directory so we
864        can just rename it to .xfigrc after creating it */
865     snprintf(tmpname, sizeof(tmpname), "%s/xfig-xfigrc.XXXXXX", userhome);
866 
867     if ((fd = mkstemp(tmpname)) == -1 || (tmpf = fdopen(fd, "wb")) == NULL) {
868        file_msg("Can't make temporary file for .xfigrc - error: %s",
869               strerror(errno));
870        if (fd != -1) {
871           unlink(tmpname);
872           close(fd);
873        }
874        return -1;
875     }
876     /* read the .xfigrc file and write all to temp file except file names */
877     xfigrc = fopen(xfigrc_name,"r");
878     /* does the .xfigrc file exist? */
879     if (xfigrc == 0) {
880 	/* no, create one */
881 	xfigrc = fopen(xfigrc_name,"wb");
882 	if (xfigrc == 0) {
883 	    file_msg("Can't create ~/.xfigrc - error: %s",strerror(errno));
884 	    return -1;
885 	}
886 
887 	fclose(xfigrc);
888 	xfigrc = (FILE *) 0;
889 	return 0;
890     }
891     while (fgets(line, RC_BUFSIZ, xfigrc) != NULL) {
892 	/* look for sane input */
893 	if (line[0] == '\0')
894 	    break;
895 	/* make copy of line to look for token because strtok modifies the line */
896 	tok = strdup(line);
897 	if (strcasecmp(strtok(tok, ": \t"), name) == 0)
898 	    continue;			/* match, skip */
899 	fputs(line, tmpf);
900 	free(tok);
901     }
902     return 0;
903 }
904 
905 void finish_update_xfigrc(void)
906 {
907     fclose(tmpf);
908     if (xfigrc != 0)
909 	fclose(xfigrc);
910     /* delete original .xfigrc and move temp file to .xfigrc */
911     if (unlink(xfigrc_name) != 0) {
912 	file_msg("Can't update your .xfigrc file - error: %s",strerror(errno));
913 	return;
914     }
915     if (rename(tmpname, xfigrc_name) != 0)
916 	file_msg("Can't rename %s to .xfigrc - error: %s",tmpname, strerror(errno));
917 }
918 
919 /************************************************/
920 /* Copy initial appres settings to current vars */
921 /************************************************/
922 
923 void init_settings(void)
924 {
925     /* also initialize print/export grid strings */
926     minor_grid[0] = major_grid[0] = '\0';
927 
928     if (appres.startfontsize >= 1.0)
929 	cur_fontsize = round(appres.startfontsize);
930 
931     /* allow "Modern" for "Sans Serif" and allow "SansSerif" (no space) */
932     if (appres.startlatexFont) {
933         if (strcmp(appres.startlatexFont,"Modern")==0 ||
934 	    strcmp(appres.startlatexFont,"SansSerif")==0)
935 	      cur_latex_font = latexfontnum ("Sans Serif");
936     } else {
937 	    cur_latex_font = latexfontnum (appres.startlatexFont);
938     }
939 
940     cur_ps_font = psfontnum (appres.startpsFont);
941 
942     if (appres.startarrowtype >= 0)
943 	cur_arrowtype = appres.startarrowtype;
944 
945     if (appres.startarrowthick > 0.0) {
946 	use_abs_arrowvals = True;
947 	cur_arrowthick = appres.startarrowthick;
948     }
949 
950     if (appres.startarrowwidth > 0.0) {
951 	use_abs_arrowvals = True;
952 	cur_arrowwidth = appres.startarrowwidth;
953     }
954 
955     if (appres.startarrowlength > 0.0) {
956 	use_abs_arrowvals = True;
957 	cur_arrowheight = appres.startarrowlength;
958     }
959 
960     if (appres.starttextstep > 0.0)
961 	cur_textstep = appres.starttextstep;
962 
963     if (appres.startfillstyle >= 0)
964 	cur_fillstyle = min2(appres.startfillstyle,NUMFILLPATS-1);
965 
966     if (appres.startlinewidth >= 0)
967 	cur_linewidth = min2(appres.startlinewidth,MAX_LINE_WIDTH);
968 
969     if (appres.startgridmode >= 0)
970 	cur_gridmode = min2(appres.startgridmode,GRID_ISO_4);
971 
972     if (appres.startgridtype >= 0)					// isometric grid
973 	cur_gridtype = min2(appres.startgridtype,GRID_ISO);
974 
975     if (appres.startposnmode >= 0)
976 	cur_pointposn = min2(appres.startposnmode,P_GRID4);
977 
978     /* choose grid units (1/16", 1/10", MM) */
979     /* default */
980     grid_unit = FRACT_UNIT;
981 
982     /* if inches is desired set grid unit to user spec (1/16" or 1/10") */
983     if (appres.INCHES) {
984 	/* make sure user specified one */
985 	if (strcasecmp(appres.tgrid_unit, "default") != 0) {
986 	    if (strcasecmp(appres.tgrid_unit, "tenth") == 0 ||
987 		strcasecmp(appres.tgrid_unit, "ten") == 0 ||
988 		strcasecmp(appres.tgrid_unit, "1/10") == 0 ||
989 		strcasecmp(appres.tgrid_unit, "10") == 0)
990 		    grid_unit = TENTH_UNIT;
991 		else
992 		    grid_unit = FRACT_UNIT;
993 	}
994     } else {
995 	grid_unit = MM_UNIT;
996     }
997 
998     /* set current and "old" grid unit */
999     old_gridunit = cur_gridunit = grid_unit;
1000 
1001     /* turn off PSFONT_TEXT flag if user specified -latexfonts */
1002     if (appres.latexfonts)
1003 	cur_textflags = cur_textflags & (~PSFONT_TEXT);
1004     if (appres.specialtext)
1005 	cur_textflags = cur_textflags | SPECIAL_TEXT;
1006     if (appres.rigidtext)
1007 	cur_textflags = cur_textflags | RIGID_TEXT;
1008     if (appres.hiddentext)
1009 	cur_textflags = cur_textflags | HIDDEN_TEXT;
1010 
1011     /* turn off PSFONT_TEXT flag if user specified -latexfonts */
1012     if (appres.latexfonts)
1013 	cur_textflags = cur_textflags & (~PSFONT_TEXT);
1014 
1015     if (appres.userunit)
1016 	strncpy(cur_fig_units, appres.userunit, sizeof(cur_fig_units)-1);
1017     else
1018 	cur_fig_units[0] = '\0';
1019 
1020     strcpy(cur_library_dir, appres.library_dir);
1021     strcpy(cur_spellchk, appres.spellcheckcommand);
1022     strcpy(cur_image_editor, appres.image_editor);
1023     strcpy(cur_browser, appres.browser);
1024     strcpy(cur_pdfviewer, appres.pdf_viewer);
1025 
1026     /* assume color to start */
1027     all_colors_available = True;
1028 
1029     /* check if monochrome screen */
1030     if (tool_cells == 2 || appres.monochrome)
1031 	all_colors_available = False;
1032 
1033 } /* init_settings() */
1034 
1035 /* This is called to read a list of Fig files specified in the command line
1036    and write them back (renaming the original to xxxx.fig.bak) so that they
1037    are updated to the current version.
1038    If the file is already in the current version it is untouched.
1039 */
1040 
1041 int
1042 update_fig_files(int argc, char **argv)
1043 {
1044     fig_settings    settings;
1045     char	    file[PATH_MAX];
1046     int		    i,col;
1047     Boolean	    status;
1048     int		    allstat;
1049 
1050     /* overall status - if any one file can't be read, return status is 1 */
1051     allstat = 0;
1052 
1053     for (i=1; i<argc; i++) {
1054 	/* skip any other options the user may have given */
1055 	if (argv[i][0] == '-') {
1056 	    continue;
1057 	}
1058 	strcpy(file,argv[i]);
1059 	fprintf(stderr,"* Reading %s ... ",file);
1060 	/* reset user colors */
1061 	for (col=0; col<MAX_USR_COLS; col++)
1062 	    n_colorFree[col] = True;
1063 
1064 	/* v1.3 fig files query display_zoomscale in read_1_3_textobject()
1065 	   in f_readold.c */
1066 	display_zoomscale = 1.0f;
1067 	/* v1.3 fig files need some appres values in readfp_fig() in f_read.c */
1068 	appres.landscape = True;
1069 	appres.flushleft = False;
1070 	appres.INCHES = True;
1071 	appres.papersize = 0;
1072 	appres.magnification = 100.0f;
1073 	appres.multiple = False;
1074 	appres.transparent = -2;
1075 	/* and use scalable fonts, if available */
1076 	appres.scalablefonts = True;
1077 	/* but do not set correct_font_size; Originally, font sizes were given
1078 	   in pixel, and xfig displayed with 80 pixels to the inch. */
1079 
1080 	/* read Fig file but don't import any images */
1081 	status = read_fig(file, &objects, DONT_MERGE, 0, 0, &settings);
1082 	if (status != 0) {
1083 	    fprintf(stderr," *** Error in reading, not updating this file\n");
1084 	    allstat = 1;
1085 	} else {
1086 	    fprintf(stderr,"Ok. Renamed to %s.bak. ",file);
1087 	    /* now rename original file to file.bak */
1088 	    renamefile(file);
1089 	    fprintf(stderr,"Writing as protocol %s ... ",PROTOCOL_VERSION);
1090 	    /* first update the settings from appres */
1091 	    appres.landscape = settings.landscape;
1092 	    appres.flushleft = settings.flushleft;
1093 	    appres.INCHES = settings.units;
1094 	    appres.papersize = settings.papersize;
1095 	    appres.magnification = settings.magnification;
1096 	    appres.multiple = settings.multiple;
1097 	    appres.transparent = settings.transparent;
1098 	    /* copy user colors */
1099 	    for (col=0; col<MAX_USR_COLS; col++) {
1100 		colorUsed[col] = !n_colorFree[col];
1101 		user_colors[col].red = n_user_colors[col].red;
1102 		user_colors[col].green = n_user_colors[col].green;
1103 		user_colors[col].blue = n_user_colors[col].blue;
1104 	    }
1105 	    /* now write out the new one */
1106 	    num_usr_cols = MAX_USR_COLS;
1107 	    write_file(file, False);
1108 	    fprintf(stderr,"Ok\n");
1109 	}
1110     }
1111     return allstat;
1112 }
1113 
1114 /* replace all "%f" in "program" with value in filename */
1115 
1116 char *
1117 build_command(char *program, char *filename)
1118 {
1119     char *cmd = new_string(PATH_MAX*2);
1120     char  cmd2[PATH_MAX*2];
1121     char *c1;
1122     Boolean repl = False;
1123 
1124     if (!cmd)
1125 	return (char *) NULL;
1126     strcpy(cmd,program);
1127     while ((c1 = strstr(cmd,"%f"))) {
1128 	repl = True;
1129 	strcpy(cmd2, c1+2);		/* save tail */
1130 	strcpy(c1, filename);		/* change %f to filename */
1131 	strcat(c1, cmd2);		/* append tail */
1132     }
1133     /* if no %f was found in the resource, just append the filename to the command */
1134     if (!repl) {
1135 	strcat(cmd," ");
1136 	strcat(cmd,filename);
1137     }
1138     /* append "2> /dev/null" to send stderr to /dev/null and "&" to run in background */
1139     strcat(cmd," 2> /dev/null &");
1140     return cmd;
1141 }
1142 
1143 /* define the strerror() function to return str_errlist[] if
1144    the system doesn't have the strerror() function already */
1145 
1146 #ifndef HAVE_STRERROR
1147 char *
1148 strerror(e)
1149 	int e;
1150 {
1151 	return sys_errlist[e];
1152 }
1153 #endif /* HAVE_STRERROR */
1154 
1155 
1156 /* for images with no palette, we'll use neural net to reduce to 256 colors with palette */
1157 
1158 Boolean
1159 map_to_palette(F_pic *pic)
1160 {
1161 	int	 w,h,x,y;
1162 	int	 mult, neu_stat, size;
1163 	unsigned char *old;
1164 	BYTE	 col[3];
1165 
1166 	w = pic->pic_cache->bit_size.x;
1167 	h = pic->pic_cache->bit_size.y;
1168 
1169 	mult = 1;
1170 	if ((neu_stat=neu_init(w*h)) <= -2) {
1171 	    mult = -neu_stat;
1172 	    /* try again with more pixels */
1173 	    neu_stat = neu_init2(w*h*mult);
1174 	}
1175 	if (neu_stat == -1) {
1176 	    /* couldn't alloc memory for network */
1177 	    fprintf(stderr,"Can't alloc memory for neural network\n");
1178 	    free(pic->pic_cache->bitmap);
1179 	    return False;
1180 	}
1181 	/* now add all pixels to the samples */
1182 	size = w*h*3;
1183 	for (x=0; x<size;) {
1184 	    col[N_BLU] = pic->pic_cache->bitmap[x++];
1185 	    col[N_GRN] = pic->pic_cache->bitmap[x++];
1186 	    col[N_RED] = pic->pic_cache->bitmap[x++];
1187 	    for (y=0; y<mult; y++) {
1188 		neu_pixel(col);
1189 	    }
1190 	}
1191 
1192 	/* make a new colortable with the optimal colors */
1193 	pic->pic_cache->numcols = neu_clrtab(256);
1194 
1195 	/* now change the color cells with the new colors */
1196 	/* clrtab[][] is the colormap produced by neu_clrtab */
1197 	for (x=0; x<pic->pic_cache->numcols; x++) {
1198 	    pic->pic_cache->cmap[x].red   = (unsigned short) clrtab[x][N_RED];
1199 	    pic->pic_cache->cmap[x].green = (unsigned short) clrtab[x][N_GRN];
1200 	    pic->pic_cache->cmap[x].blue  = (unsigned short) clrtab[x][N_BLU];
1201 	}
1202 
1203 	/* now alloc a 1-byte/per/pixel array for the final colormapped image */
1204 	/* save orig */
1205 	old = pic->pic_cache->bitmap;
1206 	if ((pic->pic_cache->bitmap=malloc(w*(h+2)))==NULL)
1207 	    return False;
1208 
1209 	/* and change the 3-byte pixels to the 1-byte */
1210 	for (x=0, y=0; x<size; x+=3, y++) {
1211 	    col[N_BLU] = old[x];
1212 	    col[N_GRN] = old[x+1];
1213 	    col[N_RED] = old[x+2];
1214 	    pic->pic_cache->bitmap[y] = neu_map_pixel(col);
1215 	}
1216 	/* free 3-byte/pixel array */
1217 	free(old);
1218 	return True;
1219 }
1220 
1221 /* return pointers to the line components of a dimension line.
1222    If passed dimline is not a dimension line, the result is False */
1223 
1224 Boolean
1225 dimline_components(F_compound *dimline, F_line **line, F_line **tick1, F_line **tick2, F_line **poly)
1226 {
1227     F_line *l;
1228 
1229     if (!dimline->comments || strncmp(dimline->comments,"Dimension line:",15) !=0 )
1230 	return False;
1231 
1232     *line = *tick1 = *tick2 = *poly = (F_line *) NULL;
1233     for (l = dimline->lines; l; l=l->next) {
1234 	if (!l->comments)
1235 		continue;
1236 	if (strcmp(l->comments,"main dimension line")==0)
1237 	    *line = l;
1238 	else if (strcmp(l->comments,"text box")==0)
1239 	    *poly = l;
1240 	else if (strcmp(l->comments,"tick")==0) {
1241 	    if (*tick1 == 0)
1242 		*tick1 = l;
1243 	    else
1244 		*tick2 = l;
1245 	}
1246     }
1247     return True;
1248 }
1249 
1250 int
1251 find_smallest_depth(F_compound *compound)
1252 {
1253 	F_line	 *l;
1254 	F_spline	 *s;
1255 	F_ellipse	 *e;
1256 	F_arc	 *a;
1257 	F_text	 *t;
1258 	F_compound *c;
1259 	int	  smallest, d1;
1260 
1261 	smallest = MAX_DEPTH;
1262 	for (l = compound->lines; l != NULL; l = l->next) {
1263 	    if (l->depth < smallest) smallest = l->depth;
1264 	}
1265 	for (s = compound->splines; s != NULL; s = s->next) {
1266 	    if (s->depth < smallest) smallest = s->depth;
1267 	}
1268 	for (e = compound->ellipses; e != NULL; e = e->next) {
1269 	    if (e->depth < smallest) smallest = e->depth;
1270 	}
1271 	for (a = compound->arcs; a != NULL; a = a->next) {
1272 	    if (a->depth < smallest) smallest = a->depth;
1273 	}
1274 	for (t = compound->texts; t != NULL; t = t->next) {
1275 	    if (t->depth < smallest) smallest = t->depth;
1276 	}
1277 	for (c = compound->compounds; c != NULL; c = c->next) {
1278 	    d1 = find_smallest_depth(c);
1279 	    if (d1 < smallest)
1280 		smallest = d1;
1281 	}
1282 	return smallest;
1283 }
1284 
1285 int
1286 find_largest_depth(F_compound *compound)
1287 {
1288 	F_line	 *l;
1289 	F_spline	 *s;
1290 	F_ellipse	 *e;
1291 	F_arc	 *a;
1292 	F_text	 *t;
1293 	F_compound *c;
1294 	int	  largest, d1;
1295 
1296 	largest = MIN_DEPTH;
1297 	for (l = compound->lines; l != NULL; l = l->next) {
1298 	    if (l->depth > largest) largest = l->depth;
1299 	}
1300 	for (s = compound->splines; s != NULL; s = s->next) {
1301 	    if (s->depth > largest) largest = s->depth;
1302 	}
1303 	for (e = compound->ellipses; e != NULL; e = e->next) {
1304 	    if (e->depth > largest) largest = e->depth;
1305 	}
1306 	for (a = compound->arcs; a != NULL; a = a->next) {
1307 	    if (a->depth > largest) largest = a->depth;
1308 	}
1309 	for (t = compound->texts; t != NULL; t = t->next) {
1310 	    if (t->depth > largest) largest = t->depth;
1311 	}
1312 	for (c = compound->compounds; c != NULL; c = c->next) {
1313 	    d1 = find_largest_depth(c);
1314 	    if (d1 > largest)
1315 		largest = d1;
1316 	}
1317 	return largest;
1318 }
1319 
1320 /* get grid params and assemble into fig2dev parm */
1321 void
1322 get_grid_spec(char *grid, Widget minor_grid_panel, Widget major_grid_panel)
1323 {
1324 	char	   *c1;
1325 
1326 	/* if panel hasn't been up yet */
1327 	if (minor_grid_panel == (Widget) 0 || major_grid_panel == (Widget) 0) {
1328 	    sprintf(grid,"%s:%s%s",
1329 				strlen(minor_grid)==0? "0": minor_grid,
1330 				strlen(major_grid)==0? "0": major_grid,
1331 				appres.INCHES? "in":"mm");
1332 	    return;
1333 	}
1334 
1335 	/* get minor grid spec from panel */
1336 	strcpy(grid, panel_get_value(minor_grid_panel));
1337 	if (strcasecmp(grid,"none") == 0) {
1338 	    grid[0]='\0';
1339 	}
1340 	/* now major */
1341 	c1 = panel_get_value(major_grid_panel);
1342 	if (strcasecmp(c1,"none") != 0 && strlen(c1)) {
1343 	    strcat(grid,":");
1344 	    strcat(grid,c1);
1345 	}
1346 	if (strlen(grid))
1347 	    strcat(grid,appres.INCHES? "in":"mm");
1348 }
1349 
1350 /* get the timestamp (mtime) of the filename passed */
1351 
1352 time_t
1353 file_timestamp(char *file)
1354 {
1355     struct stat	    file_status;
1356 
1357     if (stat(file, &file_status) != 0)
1358 	return -1;
1359     return file_status.st_mtime;
1360 }
1361