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