1 /* file.c -- file loading and saving for xspringies
2  * Copyright (C) 1991,1992  Douglas M. DeCarlo
3  *
4  * This file is part of XSpringies, a mass and spring simulation system for X
5  *
6  * XSpringies is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * XSpringies is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with XSpringies; see the file COPYING.  If not, write to
18  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21 
22 #include "defs.h"
23 #include "obj.h"
24 #include <pwd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 
28 #define USERNAMELEN	8
29 #define MAGIC_CMD	"#1.0"
30 #define FILE_EXT	".xsp"
31 
32 #ifndef COMPRESS
33 #define COMPRESS	"gzip"
34 #define UNCOMPRESS	"gunzip -c"
35 #endif
36 
37 /* tilde_expand: expand ~/.. and ~bar/.. in filenames and put
38    the result in filename
39    filename is assumed to be null terminated, and after the
40    expansion will not exceed MAXPATH in length
41 
42    tilde_expand returns its argument, or NULL if the user
43    is unknown
44    */
tilde_expand(fname)45 char *tilde_expand(fname)
46 char *fname;
47 {
48     register int prelen, len, restlen, i;
49     register char *prefix, *s, *u;
50     struct passwd *pw;
51     char user[USERNAMELEN+1];
52     extern char *getenv();
53 
54     if (*fname == '~') {
55 	if (*(fname + 1) == '/' || !*(fname + 1)) {
56 	    len = 1;
57 	    /* Do ~/ expansion */
58 	    if ((prefix = getenv("HOME")) == NULL) {
59 		/* Use . as home directory if HOME not set */
60 		prefix = ".";
61 	    }
62 	} else {
63 	    /* Do ~user/ expansion */
64 	    for (len = 1, s = fname + 1, u = user; len <= USERNAMELEN && *s && *s != '/'; *u++ = *s++, len++);
65 	    *u = '\0';
66 
67 	    /* Get name */
68 	    if ((pw = getpwnam(user)) == NULL)
69 	      return NULL;
70 	    prefix = pw->pw_dir;
71 	}
72 
73 	prelen = strlen(prefix);
74 	restlen = strlen(fname + len);
75 
76 	/* Move over pathname */
77 	if (prelen < len) {
78 	    for (i = 0; i <= restlen; i++) {
79 		fname[prelen+i] = fname[len+i];
80 	    }
81 	} else {
82 	    for (i = restlen; i >= 0; i--) {
83 		fname[prelen+i] = fname[len+i];
84 	    }
85 	}
86 	/* Copy in prefix */
87 	(void)memcpy(fname, prefix, prelen);
88     }
89 
90     return fname;
91 }
92 
skip_to_eol(f)93 void skip_to_eol(f)
94 FILE *f;
95 {
96     int ch;
97 
98     while ((ch = fgetc(f)) != EOF && ch != '\n');
99 }
100 
extend_file(file)101 char *extend_file(file)
102 char *file;
103 {
104     static char buf[MAXPATH];
105     int len, felen;
106 
107     strcpy(buf, file);
108 
109     felen = strlen(FILE_EXT);
110     len = strlen(buf);
111 
112     if (len < felen || strcmp(buf + len - felen, FILE_EXT)) {
113 	strcat(buf, FILE_EXT);
114     }
115 
116     return buf;
117 }
118 
read_file(char * file,boolean * p)119 FILE *read_file(char *file, boolean *p)
120 {
121     FILE *f;
122     char buff[MAXPATH];
123     struct stat sbuf;
124 
125     if ((f = fopen(file, "r")) != NULL) {
126 	*p = FALSE;
127 	return f;
128     }
129 
130 #ifdef COMPRESS
131     sprintf(buff, "%s%s", file, COMPR_EXT);
132     if (stat(buff, &sbuf) == 0) {
133 	sprintf(buff, "%s %s%s", UNCOMPRESS, file, COMPR_EXT);
134 
135 	if ((f = popen(buff, "r")) != NULL) {
136 	    *p = TRUE;
137 	    return f;
138 	}
139     }
140 #endif
141 
142     return NULL;
143 }
144 
compress_file(char * file)145 void compress_file(char *file)
146 {
147     char buff[MAXPATH];
148 
149 #ifdef COMPRESS
150     sprintf(buff, "%s %s", COMPRESS, file);
151 
152     system(buff);
153 #endif
154 }
155 
156 #define IS_CMD(s)	(!strncmp(cmd, s, 4))
157 
file_command(file,command)158 boolean file_command(file, command)
159 char *file;
160 int command;
161 {
162     FILE *f;
163     char cmd[5];
164     int i, which, spring_start, temp;
165     int *mapfrom, *mapto, num_map, num_map_alloc;
166     boolean selectnew = FALSE, pipe_file = FALSE;
167 
168     if (strlen(file) == 0)
169       return FALSE;
170 
171     tilde_expand(file);
172 
173     switch (command) {
174       case F_INSERT:
175       case F_LOAD:
176 	if ((f = read_file(extend_file(file), &pipe_file)) == NULL) {
177 	    return FALSE;
178 	}
179 
180 	/* Check first line */
181 	if (fgets(cmd, 5, f) != NULL && IS_CMD(MAGIC_CMD)) {
182 	    skip_to_eol(f);
183 
184 	    if (command == F_LOAD) {
185 		delete_all();
186 		reset_mst();
187 		init_objects();
188 	    } else {
189 		if (!anything_selected())
190 		  selectnew = TRUE;
191 	    }
192 	    spring_start = num_spring;
193 
194 	    num_map = 0;
195 	    num_map_alloc = ALLOC_SIZE;
196 	    mapfrom = (int *)xmalloc(sizeof(int) * num_map_alloc);
197 	    mapto = (int *)xmalloc(sizeof(int) * num_map_alloc);
198 
199 	    while (fgets(cmd, 5, f) != NULL) {
200 		if (IS_CMD("mass")) {
201 		    if (num_map >= num_map_alloc) {
202 			num_map_alloc += ALLOC_SIZE;
203 			mapfrom = (int *)xrealloc(mapfrom, sizeof(int) * num_map_alloc);
204 			mapto = (int *)xrealloc(mapto, sizeof(int) * num_map_alloc);
205 		    }
206 
207 		    which = create_mass();
208 		    mapto[num_map] = which;
209 		    fscanf(f, "%d %lf %lf %lf %lf %lf %lf\n", &mapfrom[num_map], &masses[which].x, &masses[which].y,
210 			   &masses[which].vx, &masses[which].vy, &masses[which].mass, &masses[which].elastic);
211 		    num_map++;
212 		    if (masses[which].mass < 0) {
213 			masses[which].mass = -masses[which].mass;
214 			masses[which].status |= S_FIXED;
215 		    }
216 		    if (masses[which].mass == 0)
217 		      masses[which].mass = 1.0;
218 
219 		    masses[which].radius = mass_radius(masses[which].mass);
220 		    if (selectnew) {
221 			select_object(which, TRUE, FALSE);
222 		    }
223 		} else if (IS_CMD("spng")) {
224 		    int bogus;
225 
226 		    which = create_spring();
227 		    fscanf(f, "%d %d %d %lf %lf %lf\n", &bogus, &springs[which].m1, &springs[which].m2,
228 			   &springs[which].ks, &springs[which].kd, &springs[which].restlen);
229 		    if (selectnew) {
230 			select_object(which, FALSE, FALSE);
231 		    }
232 
233 		} else if (command == F_INSERT) {
234 		    /* skip non mass/spring commands if in insert mode */
235 		} else if (IS_CMD("cmas")) {
236 		    fscanf(f, "%lf\n", &(mst.cur_mass));
237 		} else if (IS_CMD("elas")) {
238 		    fscanf(f, "%lf\n", &(mst.cur_rest));
239 		} else if (IS_CMD("kspr")) {
240 		    fscanf(f, "%lf\n", &(mst.cur_ks));
241 		} else if (IS_CMD("kdmp")) {
242 		    fscanf(f, "%lf\n", &(mst.cur_kd));
243 		} else if (IS_CMD("fixm")) {
244 		    fscanf(f, "%d\n", &temp);
245 		    mst.fix_mass = temp ? TRUE : FALSE;
246 		} else if (IS_CMD("shws")) {
247 		    fscanf(f, "%d\n", &temp);
248 		    mst.show_spring = temp ? TRUE : FALSE;
249 		} else if (IS_CMD("cent")) {
250 		    fscanf(f, "%d\n", &(mst.center_id));
251 
252 		} else if (IS_CMD("frce")) {
253 		    int which, temp;
254 
255 		    fscanf(f, "%d", &which);
256 		    if (which >= 0 && which < BF_NUM) {
257 			fscanf(f, "%d %lf %lf\n", &temp, &(mst.cur_grav_val[which]), &(mst.cur_misc_val[which]));
258 
259 			activate_mbutton(&(mst.bf_mode[which]), temp);
260 		    } else if (which == BF_NUM) {
261 			fscanf(f, "%d", &temp);
262 			activate_mbutton(&(mst.collide), temp);
263 			skip_to_eol(f);
264 		    }
265 		} else if (IS_CMD("visc")) {
266 		    fscanf(f, "%lf\n", &(mst.cur_visc));
267 		} else if (IS_CMD("stck")) {
268 		    fscanf(f, "%lf\n", &(mst.cur_stick));
269 		} else if (IS_CMD("step")) {
270 		    fscanf(f, "%lf\n", &(mst.cur_dt));
271 		} else if (IS_CMD("prec")) {
272 		    fscanf(f, "%lf\n", &(mst.cur_prec));
273 		} else if (IS_CMD("adpt")) {
274 		    fscanf(f, "%d\n", &temp);
275 		    mst.adaptive_step = temp ? TRUE : FALSE;
276 
277 		} else if (IS_CMD("gsnp")) {
278 		    fscanf(f, "%lf %d\n", &(mst.cur_gsnap), &temp);
279 		    mst.grid_snap = temp ? TRUE : FALSE;
280 		} else if (IS_CMD("wall")) {
281 		    int wt, wl, wr, wb;
282 		    fscanf(f, "%d %d %d %d\n", &wt, &wl, &wr, &wb);
283 		    mst.w_top = (boolean)wt;
284 		    mst.w_left = (boolean)wl;
285 		    mst.w_right = (boolean)wr;
286 		    mst.w_bottom = (boolean)wb;
287 		} else {
288 		    /* unknown command */
289 		    fprintf(stderr, "Unknown command: %4.4s\n", cmd);
290 		    skip_to_eol(f);
291 		}
292 	    }
293 
294 	    /* Connect springs to masses */
295 	    for (i = spring_start; i < num_spring; i++) {
296 		int j;
297 		boolean m1done, m2done;
298 
299 		m1done = m2done = FALSE;
300 
301 		if (i == fake_spring)
302 		  continue;
303 
304 		for (j = 0; (!m1done || !m2done) && j < num_map; j++) {
305 		    if (!m1done && springs[i].m1 == mapfrom[j]) {
306 			springs[i].m1 = mapto[j];
307 			m1done = TRUE;
308 		    }
309 		    if (!m2done && springs[i].m2 == mapfrom[j]) {
310 			springs[i].m2 = mapto[j];
311 			m2done = TRUE;
312 		    }
313 		}
314 		if (!m1done && !m2done) {
315 		    /* delete spring */
316 		    delete_spring(i);
317 		    fprintf(stderr, "Spring %d not connected to existing mass\n", i);
318 		}
319 	    }
320 
321 	    free(mapfrom);
322 	    free(mapto);
323 	    reconnect_masses();
324 	    review_system(TRUE);
325 	    redisplay_widgets();
326 	} else {
327 	    return FALSE;
328 	}
329 
330 	if (pipe_file) {
331 	    (void)pclose(f);
332 	} else {
333 	    (void)fclose(f);
334 	}
335 
336 	break;
337       case F_SAVE:
338 	if ((f = fopen(extend_file(file), "w")) == NULL) {
339 	    return FALSE;
340 	}
341 	fprintf(f, "%s *** XSpringies data file\n", MAGIC_CMD);
342 	/* Settings */
343 	fprintf(f, "cmas %.12lg\n", mst.cur_mass);
344 	fprintf(f, "elas %.12lg\n", mst.cur_rest);
345 	fprintf(f, "kspr %.12lg\n", mst.cur_ks);
346 	fprintf(f, "kdmp %.12lg\n", mst.cur_kd);
347 	fprintf(f, "fixm %d\n", mst.fix_mass);
348 	fprintf(f, "shws %d\n", mst.show_spring);
349 	fprintf(f, "cent %d\n", mst.center_id);
350 
351 	for (i = 0; i < BF_NUM; i++)
352 	  fprintf(f, "frce %d %d %.12lg %.12lg\n", i, (mst.bf_mode[i] >= 0) ? 1 : 0, mst.cur_grav_val[i], mst.cur_misc_val[i]);
353 
354 	fprintf(f, "frce %d %d 0 0\n", BF_NUM, mst.collide >= 0 ? 1 : 0);
355 
356 	fprintf(f, "visc %.12lg\n", mst.cur_visc);
357 	fprintf(f, "stck %.12lg\n", mst.cur_stick);
358 	fprintf(f, "step %.12lg\n", mst.cur_dt);
359 	fprintf(f, "prec %.12lg\n", mst.cur_prec);
360 	fprintf(f, "adpt %d\n", mst.adaptive_step);
361 
362 	fprintf(f, "gsnp %.12lg %d\n", mst.cur_gsnap, mst.grid_snap);
363 	fprintf(f, "wall %d %d %d %d\n", (int)mst.w_top, (int)mst.w_left, (int)mst.w_right, (int)mst.w_bottom);
364 
365 	/* Masses and springs */
366 	for (i = 0; i < num_mass; i++) {
367 	    if (masses[i].status & S_ALIVE) {
368 		fprintf(f, "mass %d %.18lg %.18lg %.12lg %.12lg %.8lg %.8lg\n", i, masses[i].x, masses[i].y, masses[i].vx, masses[i].vy,
369 		       (masses[i].status & S_FIXED) ? -masses[i].mass : masses[i].mass, masses[i].elastic);
370 	    }
371 	}
372 	for (i = 0; i < num_spring; i++) {
373 	    if (springs[i].status & S_ALIVE) {
374 		fprintf(f, "spng %d %d %d %.8lg %.8lg %.18lg\n", i, springs[i].m1, springs[i].m2,
375 			springs[i].ks, springs[i].kd, springs[i].restlen);
376 	    }
377 	}
378 
379 	if (fclose(f) == EOF)
380 	  return FALSE;
381 
382 	compress_file(extend_file(file));
383 	break;
384     }
385 
386     return TRUE;
387 }
388