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