1 
2 /* addendum to session.c, for handling the saving of session info to
3    an XML file, and the re-building of a session from same.
4 */
5 
6 #include "usermat.h"
7 #include "boxplots.h"
8 #include "libset.h"
9 
check_graph_file(const char * fname,int type)10 static int check_graph_file (const char *fname, int type)
11 {
12     char fullname[MAXLEN];
13     FILE *fp;
14     int err = 0;
15 
16     session_file_make_path(fullname, fname);
17     fp = gretl_fopen(fullname, "r");
18 
19     if (fp == NULL) {
20 	file_read_errbox(fname);
21 	err = 1;
22     } else {
23 	fprintf(stderr, "Opened '%s' OK\n", fullname);
24 	if (type == GRETL_OBJ_PLOT) {
25 	    char line[32] = {0};
26 
27 	    if (fgets(line, sizeof line, fp) != NULL) {
28 		if (!strncmp(line, "# boxplot generated", 19)) {
29 		    fprintf(stderr, "Ignoring old boxplot file\n");
30 		    err = 1;
31 		}
32 	    } else {
33 		err = 1;
34 	    }
35 	    fclose(fp);
36 	} else {
37 	    fclose(fp);
38 	}
39     }
40 
41     return err;
42 }
43 
44 /* Arrange things so that when a session file is opened, any graph
45    files are numbered consecutively, starting at 1.  This avoids a
46    situation where adding a graph to an existing session results in
47    over-writing an existing graph file.  (The graph files in a saved
48    session may not be numbered consecutively if some graphs were
49    initially saved, then deleted; and it's easier to fix this on
50    opening a session file than on saving one, since on opening we're
51    constructing the graph list de novo.)
52 */
53 
normalize_graph_filename(char * fname,int gnum)54 static void normalize_graph_filename (char *fname, int gnum)
55 {
56     char s[16];
57     int i;
58 
59     if (sscanf(fname, "%15[^.].%d", s, &i) == 2) {
60 	char oldname[MAXLEN];
61 	char newname[MAXLEN];
62 	gchar *tmp = NULL;
63 
64 	if (i != gnum && (!strcmp(s, "graph") || !strcmp(s, "plot"))) {
65 	    session_file_make_path(oldname, fname);
66 	    tmp = g_strdup_printf("graph.%d", gnum);
67 	    session_file_make_path(newname, tmp);
68 	}
69 
70 	if (tmp != NULL) {
71 	    gretl_rename(oldname, newname);
72 	    strcpy(fname, tmp);
73 	    g_free(tmp);
74 	}
75     }
76 }
77 
restore_session_graphs(xmlNodePtr node)78 static int restore_session_graphs (xmlNodePtr node)
79 {
80     xmlNodePtr cur;
81     int gnum = 0;
82     int errs = 0;
83 
84     /* reset prior to parsing */
85     session.ngraphs = 0;
86 
87     cur = node->xmlChildrenNode;
88 
89     fprintf(stderr, "Doing restore_session_graphs\n");
90 
91     while (cur != NULL) {
92 	SESSION_GRAPH *sg;
93 	xmlChar *name = NULL;
94 	xmlChar *fname = NULL;
95 	int type, err = 0;
96 
97 	name = xmlGetProp(cur, (XUC) "name");
98 	if (name == NULL) {
99 	    err = 1;
100 	}
101 
102 	if (!err && !gretl_xml_get_prop_as_int(cur, "type", &type)) {
103 	    err = 1;
104 	}
105 
106 	if (!err) {
107 	    fname = xmlGetProp(cur, (XUC) "fname");
108 	    if (fname == NULL) {
109 		err = 1;
110 	    } else {
111 		fprintf(stderr, "checking '%s'\n", name);
112 		err = check_graph_file((const char *) fname, type);
113 		if (!err) {
114 		    normalize_graph_filename((char *) fname, ++gnum);
115 		}
116 	    }
117 	}
118 
119 	if (!err) {
120 	    sg = session_append_graph((const char *) name,
121 				      (const char *) fname,
122 				      type);
123 	    err = (sg == NULL);
124 	}
125 
126 	if (!err) {
127 	    int inpage, has_datafile;
128 
129 	    if (gretl_xml_get_prop_as_int(cur, "inpage", &inpage)) {
130 		graph_page_add_file((const char *) fname); /* FIXME path? */
131 	    }
132 	    if (gretl_xml_get_prop_as_int(cur, "has_datafile", &has_datafile)) {
133 		sg->has_datafile = 1;
134 	    }
135 	}
136 
137 	if (err) {
138 	    errs++;
139 	}
140 
141 	free(name);
142 	free(fname);
143 
144 	cur = cur->next;
145     }
146 
147     return errs;
148 }
149 
restore_session_texts(xmlNodePtr node,xmlDocPtr doc)150 static int restore_session_texts (xmlNodePtr node, xmlDocPtr doc)
151 {
152     xmlNodePtr cur;
153     int errs = 0;
154 
155     session.ntexts = 0;
156 
157     cur = node->xmlChildrenNode;
158 
159     while (cur != NULL) {
160 	xmlChar *name = NULL;
161 	xmlChar *buf = NULL;
162 	int err = 0;
163 
164 	name = xmlGetProp(cur, (XUC) "name");
165 	if (name == NULL) {
166 	    err = 1;
167 	} else {
168 	    buf = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
169 	    if (buf == NULL) {
170 		err = 1;
171 	    } else {
172 		err = session_append_text((const char *) name,
173 					  (char *) buf);
174 	    }
175 	}
176 
177 	if (err) {
178 	    errs++;
179 	}
180 
181 	free(name);
182 
183 	cur = cur->next;
184     }
185 
186     return errs;
187 }
188 
data_submask_from_xml(xmlNodePtr node,xmlDocPtr doc,struct sample_info * sinfo)189 static int data_submask_from_xml (xmlNodePtr node, xmlDocPtr doc,
190 				  struct sample_info *sinfo)
191 {
192     char *mask;
193     int err;
194 
195     err = gretl_xml_get_submask(node, doc, &mask);
196     if (!err) {
197 	sinfo->mask = mask;
198     }
199 
200     return err;
201 }
202 
data_restrict_from_xml(xmlNodePtr node,xmlDocPtr doc,struct sample_info * sinfo)203 static int data_restrict_from_xml (xmlNodePtr node, xmlDocPtr doc,
204 				   struct sample_info *sinfo)
205 {
206     char *s = NULL;
207 
208     if (gretl_xml_node_get_trimmed_string(node, doc, &s)) {
209 	sinfo->restriction = s;
210     }
211 
212     return 0;
213 }
214 
rebuild_session_model(const char * fname,const char * name,GretlObjType type,int tablepos)215 static int rebuild_session_model (const char *fname,
216 				  const char *name,
217 				  GretlObjType type,
218 				  int tablepos)
219 {
220     gpointer ptr = NULL;
221     SavedObjectFlags flags = 0;
222     xmlDocPtr doc;
223     xmlNodePtr node;
224     int iflag, err;
225 
226     err = gretl_xml_open_doc_root(fname,
227 				  (type == GRETL_OBJ_EQN)? "gretl-model" :
228 				  (type == GRETL_OBJ_VAR)? "gretl-VAR" :
229 				  "gretl-equation-system", &doc, &node);
230     if (err) {
231 	return err;
232     }
233 
234     if (gretl_xml_get_prop_as_int(node, "saveflags", &iflag)) {
235 	flags = iflag;
236     } else {
237 	flags = IN_GUI_SESSION | IN_NAMED_STACK;
238     }
239 
240     if (type == GRETL_OBJ_EQN) {
241 	ptr = gretl_model_from_XML(node, doc, dataset, &err);
242     } else if (type == GRETL_OBJ_VAR) {
243 	ptr = gretl_VAR_from_XML(node, doc, dataset, &err);
244     } else {
245 	ptr = equation_system_from_XML(node, doc, &err);
246     }
247 
248     xmlFreeDoc(doc);
249 
250     if (!err && ptr != NULL) {
251 #if 1
252 	fprintf(stderr, "rebuild_session_model: IN_GUI_SESSION %s, IN_MODEL_TABLE %s\n"
253 		" IN_NAMED_STACK %s, IS_LAST_MODEL %s\n",
254 		(flags & IN_GUI_SESSION)? "yes" : "no",
255 		(flags & IN_MODEL_TABLE)? "yes" : "no",
256 		(flags & IN_NAMED_STACK)? "yes" : "no",
257 		(flags & IS_LAST_MODEL)? "yes" : "no");
258 #endif
259 
260 	if (flags & IN_GUI_SESSION) {
261 	    SESSION_MODEL *smod;
262 
263 	    smod = session_model_new(ptr, name, type);
264 	    if (smod == NULL) {
265 		fprintf(stderr, "error from session_model_new\n");
266 		err = E_ALLOC;
267 	    } else {
268 		err = session_append_model(smod);
269 		fprintf(stderr, "error %d from session_append_model\n", err);
270 	    }
271 	}
272 
273 	if (!err && (flags & IN_MODEL_TABLE)) {
274 	    add_to_model_table(ptr, MODEL_ADD_BY_CMD, tablepos, NULL);
275 	}
276 
277 	if (!err && (flags & IN_NAMED_STACK)) {
278 	    err = gretl_stack_object(ptr, type);
279 	}
280 
281 	if (!err && (flags & IS_LAST_MODEL)) {
282 	    set_as_last_model(ptr, type);
283 	}
284     }
285 
286     /* need to clean up on error here (also: clean up XML parser?) */
287 
288     return err;
289 }
290 
restore_session_models(xmlNodePtr node,xmlDocPtr doc)291 static int restore_session_models (xmlNodePtr node, xmlDocPtr doc)
292 {
293     char fullname[MAXLEN];
294     xmlNodePtr cur;
295     int errs = 0;
296 
297     /* reset prior to parsing */
298     session.nmodels = 0;
299 
300     cur = node->xmlChildrenNode;
301 
302     while (cur != NULL) {
303 	xmlChar *fname = NULL;
304 	xmlChar *name = NULL;
305 	int type = GRETL_OBJ_EQN;
306 	int tablepos = 0;
307 	int err = 0;
308 
309 	fname = xmlGetProp(cur, (XUC) "fname");
310 	if (fname == NULL) {
311 	    err = 1;
312 	} else {
313 	    name = xmlGetProp(cur, (XUC) "name");
314 	    if (name == NULL) {
315 		err = 1;
316 	    }
317 	}
318 
319 	if (!err) {
320 	    session_file_make_path(fullname, (const char *) fname);
321 	    gretl_xml_get_prop_as_int(cur, "type", &type);
322 	    if (type == GRETL_OBJ_EQN) {
323 		gretl_xml_get_prop_as_int(cur, "tablepos", &tablepos);
324 	    }
325 	    fprintf(stderr, "model file: fname='%s', type=%d\n", fullname, type);
326 	    err = rebuild_session_model(fullname, (const char *) name,
327 					type, tablepos);
328 	}
329 
330 	if (!err) {
331 	    model_count_plus();
332 	} else {
333 	    fprintf(stderr, "rebuild_session_model: failed on %s (err = %d)\n",
334 		    fullname, err);
335 	    errs++;
336 	}
337 
338 	free(fname);
339 	free(name);
340 
341 	cur = cur->next;
342     }
343 
344 #if SESSION_DEBUG
345     fprintf(stderr, "restore_session_models: %d errors\n", errs);
346 #endif
347 
348     return errs;
349 }
350 
351 /* peek inside the session file and retrieve the name of the
352    data file, only */
353 
get_session_datafile_name(const char * fname,struct sample_info * sinfo,int * nodata)354 static int get_session_datafile_name (const char *fname, struct sample_info *sinfo,
355 				      int *nodata)
356 {
357     xmlDocPtr doc = NULL;
358     xmlNodePtr cur = NULL;
359     xmlChar *tmp;
360     int err = 0;
361 
362     err = gretl_xml_open_doc_root(fname, "gretl-session", &doc, &cur);
363     if (err) {
364 	gui_errmsg(err);
365 	return 1;
366     }
367 
368     /* read datafile attribute, if present */
369     tmp = xmlGetProp(cur, (XUC) "datafile");
370     if (tmp != NULL) {
371 	if (!strcmp((const char *) tmp, "none")) {
372 	    *nodata = 1;
373 	} else {
374 	    strcpy(sinfo->datafile, (char *) tmp);
375 	}
376 	free(tmp);
377     }
378 
379     if (doc != NULL) {
380 	xmlFreeDoc(doc);
381     }
382 
383     return err;
384 }
385 
386 /* (having previously grabbed the data file name) get the rest
387    of the info from session.xml */
388 
389 static int
read_session_xml(const char * fname,struct sample_info * sinfo)390 read_session_xml (const char *fname, struct sample_info *sinfo)
391 {
392     xmlDocPtr doc = NULL;
393     xmlNodePtr cur = NULL;
394     xmlChar *tmp;
395     int object_errs = 0;
396     int err = 0;
397 
398     err = gretl_xml_open_doc_root(fname, "gretl-session", &doc, &cur);
399     if (err) {
400 	gui_errmsg(err);
401 	return err;
402     }
403 
404     /* Now walk the tree */
405     cur = cur->xmlChildrenNode;
406     while (cur != NULL && !err) {
407         if (!xmlStrcmp(cur->name, (XUC) "sample")) {
408 	    tmp = xmlGetProp(cur, (XUC) "t1");
409 	    if (tmp != NULL) {
410 		sinfo->t1 = atoi((const char *) tmp);
411 		free(tmp);
412 	    } else {
413 		err = 1;
414 	    }
415 	    tmp = xmlGetProp(cur, (XUC) "t2");
416 	    if (tmp != NULL) {
417 		sinfo->t2 = atoi((const char *) tmp);
418 		free(tmp);
419 	    } else {
420 		err = 1;
421 	    }
422 	} else if (!xmlStrcmp(cur->name, (XUC) "submask")) {
423 	    err = data_submask_from_xml(cur, doc, sinfo);
424 	} else if (!xmlStrcmp(cur->name, (XUC) "restriction")) {
425 	    err = data_restrict_from_xml(cur, doc, sinfo);
426 	} else if (!xmlStrcmp(cur->name, (XUC) "resample")) {
427 	    tmp = xmlGetProp(cur, (XUC) "seed");
428 	    if (tmp != NULL) {
429 		sinfo->seed = (unsigned) atoi((const char *) tmp);
430 		free(tmp);
431 	    }
432 	    tmp = xmlGetProp(cur, (XUC) "n");
433 	    if (tmp != NULL) {
434 		sinfo->resample_n = atoi((const char *) tmp);
435 		free(tmp);
436 	    }
437 	    if (sinfo->resample_n <= 0) {
438 		sinfo->seed = 0;
439 		sinfo->resample_n = 0;
440 	    }
441 	} else if (!xmlStrcmp(cur->name, (XUC) "models")) {
442 	    tmp = xmlGetProp(cur, (XUC) "count");
443 	    if (tmp != NULL) {
444 		session.nmodels = atoi((const char *) tmp);
445 		free(tmp);
446 		if (session.nmodels > 0) {
447 		    object_errs += restore_session_models(cur, doc);
448 		}
449 	    }
450         } else if (!xmlStrcmp(cur->name, (XUC) "graphs")) {
451 	    tmp = xmlGetProp(cur, (XUC) "count");
452 	    if (tmp != NULL) {
453 		session.ngraphs = atoi((const char *) tmp);
454 		free(tmp);
455 		if (session.ngraphs > 0) {
456 		    object_errs += restore_session_graphs(cur);
457 		}
458 	    }
459 	} else if (!xmlStrcmp(cur->name, (XUC) "texts")) {
460 	    tmp = xmlGetProp(cur, (XUC) "count");
461 	    if (tmp != NULL) {
462 		session.ntexts = atoi((const char *) tmp);
463 		free(tmp);
464 		if (session.ntexts > 0) {
465 		    object_errs += restore_session_texts(cur, doc);
466 		}
467 	    }
468 	} else if (!xmlStrcmp(cur->name, (XUC) "notes")) {
469 	    session.notes =
470 		(char *) xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
471 	    if (session.notes == NULL) {
472 		object_errs++;
473 	    } else {
474 		tmp = xmlGetProp(cur, (XUC) "auto-show");
475 		if (tmp != NULL) {
476 		    session.show_notes = 1;
477 		    free(tmp);
478 		}
479 	    }
480 	}
481 	if (!err) {
482 	    cur = cur->next;
483 	}
484     }
485 
486     if (doc != NULL) {
487 	xmlFreeDoc(doc);
488     }
489 
490     if (!err && object_errs > 0) {
491 	errbox_printf("%d session object(s) could not be rebuilt", object_errs);
492     }
493 
494     return err;
495 }
496 
maybe_read_functions_file(const char * fname)497 static int maybe_read_functions_file (const char *fname)
498 {
499     FILE *fp;
500 
501     fp = gretl_fopen(fname, "r");
502     if (fp == NULL) {
503 	/* nothing to be read */
504 	return 0;
505     }
506 
507     fclose(fp);
508 
509     return read_session_functions_file(fname);
510 }
511 
maybe_read_settings_file(const char * fname)512 static int maybe_read_settings_file (const char *fname)
513 {
514     FILE *fp;
515 
516     fp = gretl_fopen(fname, "r");
517     if (fp == NULL) {
518 	/* nothing to be read */
519 	return 0;
520     }
521 
522     fclose(fp);
523 
524     return libset_read_script(fname);
525 }
526 
model_in_session(const void * ptr)527 static int model_in_session (const void *ptr)
528 {
529     int i;
530 
531     for (i=0; i<session.nmodels; i++) {
532 	if (session.models[i]->ptr == ptr) {
533 	    return 1;
534 	}
535     }
536 
537     return 0;
538 }
539 
model_save_flags(const void * ptr,GretlObjType type)540 static SavedObjectFlags model_save_flags (const void *ptr,
541 					  GretlObjType type)
542 {
543     SavedObjectFlags flags = 0;
544 
545     if (model_in_session(ptr)) {
546 	flags |= IN_GUI_SESSION;
547     }
548 
549     if (object_is_on_stack(ptr)) {
550 	flags |= IN_NAMED_STACK;
551     }
552 
553     if (get_last_model(NULL) == ptr) {
554 	flags |= IS_LAST_MODEL;
555     }
556 
557     if (type == GRETL_OBJ_EQN && in_model_table(ptr)) {
558 	flags |= IN_MODEL_TABLE;
559     }
560 
561     return flags;
562 }
563 
maybe_write_function_file(char * fullname)564 static int maybe_write_function_file (char *fullname)
565 {
566     session_file_make_path(fullname, "functions.xml");
567     return write_loaded_functions_file(fullname, 0);
568 }
569 
write_settings_file(char * fullname)570 static int write_settings_file (char *fullname)
571 {
572     session_file_make_path(fullname, "settings.inp");
573     return libset_write_script(fullname);
574 }
575 
get_xmlname(char * objname,int * err)576 static char *get_xmlname (char *objname, int *err)
577 {
578     char *ret;
579 
580     if (gretl_xml_validate(objname)) {
581 	ret = objname;
582     } else {
583 	ret = gretl_xml_encode(objname);
584 	if (ret == NULL) {
585 	    *err = E_ALLOC;
586 	}
587     }
588 
589     return ret;
590 }
591 
session_graph_wanted(const char * fname)592 static int session_graph_wanted (const char *fname)
593 {
594     int i;
595 
596     for (i=0; i<session.ngraphs; i++) {
597 	if (!strcmp(session.graphs[i]->fname, fname)) {
598 	    return 1;
599 	}
600     }
601 
602     return 0;
603 }
604 
605 /* on re-saving a session, avoid keeping model files for
606    models that have been dropped from the session, and
607    similarly for graph files
608 */
609 
trash_old_session_files(const char * path)610 static void trash_old_session_files (const char *path)
611 {
612     GDir *sdir = gretl_opendir(path);
613 
614     if (sdir != NULL) {
615 	const gchar *dname;
616 	char tmp[2*MAXLEN];
617 	int fnum;
618 
619 	while ((dname = g_dir_read_name(sdir)) != NULL) {
620 	    if (!strncmp(dname, "model.", 6)) {
621 		fnum = atoi(dname + 6);
622 		sprintf(tmp, "model.%d", fnum);
623 		if (!strcmp(dname, tmp)) {
624 		    sprintf(tmp, "%s%cmodel.%d", path, SLASH, fnum);
625 		    gretl_remove(tmp);
626 		}
627 	    } else if (!strncmp(dname, "graph.", 6)) {
628 		fnum = atoi(dname + 6);
629 		sprintf(tmp, "graph.%d", fnum);
630 		if (!strcmp(dname, tmp) &&
631 		    !session_graph_wanted(tmp)) {
632 		    sprintf(tmp, "%s%cgraph.%d", path, SLASH, fnum);
633 		    gretl_remove(tmp);
634 		}
635 	    }
636 	}
637 	g_dir_close(sdir);
638     }
639 }
640 
write_session_xml(const char * datname)641 static int write_session_xml (const char *datname)
642 {
643     MODEL *pmod;
644     char fname[2*MAXLEN];
645     char tmpname[2*MAXLEN];
646     char *objname, *xmlname;
647     PRN *prn;
648     int nmodels;
649     int tabmodels;
650     int i, modnum;
651     int err = 0;
652 
653     /* we should be in dotdir already when this is called */
654 
655     sprintf(fname, "%s%csession.xml", session.dirname, SLASH);
656     prn = gretl_print_new_with_filename(fname, &err);
657 
658     if (err) {
659 	fprintf(stderr, " write_session_xml: failed on '%s'\n", fname);
660 	file_write_errbox(fname);
661 	return err;
662     }
663 
664     gretl_xml_header(prn);
665 
666     if (*datname != '\0') {
667 	pprintf(prn, "<gretl-session datafile=\"%s\" date=\"%s\">\n",
668 		datname, print_today());
669     } else {
670 	pprintf(prn, "<gretl-session date=\"%s\">\n", print_today());
671     }
672 
673     if (data_status) {
674 	pprintf(prn, " <sample t1=\"%d\" t2=\"%d\"/>\n", dataset->t1, dataset->t2);
675 	write_dataset_submask(dataset, prn);
676     }
677 
678     nmodels = session.nmodels;
679     tabmodels = model_table_n_models();
680 
681     for (i=0; i<tabmodels; i++) {
682 	pmod = model_table_model_by_index(i);
683 	if (!model_in_session(pmod)) {
684 	    nmodels++;
685 	}
686     }
687 
688     trash_old_session_files(session.dirname);
689 
690     pprintf(prn, " <models count=\"%d\">\n", nmodels);
691 
692     modnum = 1;
693 
694     for (i=0; i<session.nmodels && !err; i++) {
695 	int type = session.models[i]->type;
696 	void *ptr = session.models[i]->ptr;
697 	SavedObjectFlags sflags;
698 	int tablepos = 0;
699 	PRN *pm;
700 
701 	sprintf(tmpname, "%s%cmodel.%d", session.dirname, SLASH, modnum);
702 	pm = gretl_print_new_with_filename(tmpname, &err);
703 
704 	if (err) {
705 	    file_write_errbox(tmpname);
706 	} else {
707 	    sprintf(tmpname, "model.%d", modnum++);
708 	    objname = session.models[i]->name;
709 	    xmlname = get_xmlname(objname, &err);
710 	    if (err) {
711 		break;
712 	    }
713 	    if (type == GRETL_OBJ_EQN) {
714 		tablepos = model_table_position(ptr);
715 	    }
716 	    if (tablepos > 0) {
717 		pprintf(prn, "  <session-model name=\"%s\" fname=\"%s\" type=\"%d\" tablepos=\"%d\"/>\n",
718 			xmlname, tmpname, type, tablepos);
719 	    } else {
720 		pprintf(prn, "  <session-model name=\"%s\" fname=\"%s\" type=\"%d\"/>\n",
721 			xmlname, tmpname, type);
722 	    }
723 	    if (xmlname != objname) {
724 		free(xmlname);
725 	    }
726 	    gretl_xml_header(pm);
727 	    sflags = model_save_flags(ptr, type);
728 	    if (type == GRETL_OBJ_EQN) {
729 		gretl_model_serialize(ptr, sflags, pm);
730 	    } else if (type == GRETL_OBJ_VAR) {
731 		gretl_VAR_serialize(ptr, sflags, pm);
732 	    } else if (type == GRETL_OBJ_SYS) {
733 		equation_system_serialize(ptr, sflags, pm);
734 	    }
735 	    gretl_print_destroy(pm);
736 	}
737     }
738 
739     for (i=0; i<tabmodels && !err; i++) {
740 	PRN *pm;
741 
742 	pmod = model_table_model_by_index(i);
743 	if (!model_in_session(pmod)) {
744 	    sprintf(tmpname, "%s%cmodel.%d", session.dirname, SLASH, modnum);
745 	    pm = gretl_print_new_with_filename(tmpname, &err);
746 
747 	    if (err) {
748 		file_write_errbox(tmpname);
749 	    } else {
750 		int tablepos = model_table_position(pmod);
751 
752 		if (pmod->name == NULL) {
753 		    objname = xmlname = NULL;
754 		} else {
755 		    objname = pmod->name;
756 		    xmlname = get_xmlname(objname, &err);
757 		}
758 		if (err) {
759 		    break;
760 		}
761 		sprintf(tmpname, "model.%d", modnum++);
762 		pprintf(prn, "  <session-model name=\"%s\" fname=\"%s\" type=\"%d\" tablepos=\"%d\"/>\n",
763 			(xmlname != NULL)? xmlname : "none", tmpname, GRETL_OBJ_EQN, tablepos);
764 		if (xmlname != NULL && xmlname != objname) {
765 		    free(xmlname);
766 		}
767 		gretl_xml_header(pm);
768 		gretl_model_serialize(pmod, model_save_flags(pmod, GRETL_OBJ_EQN),
769 				      pm);
770 		gretl_print_destroy(pm);
771 	    }
772 	}
773     }
774 
775     if (err) {
776 	gretl_print_destroy(prn);
777 	gretl_remove(fname);
778 	return err;
779     }
780 
781     pputs(prn, " </models>\n");
782 
783     pprintf(prn, " <graphs count=\"%d\">\n", session.ngraphs);
784 
785     for (i=0; i<session.ngraphs; i++) {
786 	objname = session.graphs[i]->name;
787 	xmlname = get_xmlname(objname, &err);
788 	if (err) {
789 	    break;
790 	}
791 	pprintf(prn, "  <session-graph name=\"%s\" fname=\"%s\" type=\"%d\"",
792 		xmlname, session.graphs[i]->fname, session.graphs[i]->type);
793 	if (xmlname != objname) {
794 	    free(xmlname);
795 	}
796 	if (in_graph_page(session.graphs[i]->fname)) {
797 	    pputs(prn, " inpage=\"1\"");
798 	}
799 	if (session.graphs[i]->has_datafile) {
800 	    pputs(prn, " has_datafile=\"1\"");
801 	}
802 	pputs(prn, "/>\n");
803     }
804     pputs(prn, " </graphs>\n");
805 
806     pprintf(prn, " <texts count=\"%d\">\n", session.ntexts);
807 
808     for (i=0; i<session.ntexts; i++) {
809 	objname = session.texts[i]->name;
810 	xmlname = get_xmlname(objname, &err);
811 	if (err) {
812 	    break;
813 	}
814 	pprintf(prn, "  <session-text name=\"%s\">", xmlname);
815 	if (xmlname != objname) {
816 	    free(xmlname);
817 	}
818 	gretl_xml_put_string(session.texts[i]->buf, prn);
819 	pputs(prn, "</session-text>\n");
820     }
821     pputs(prn, " </texts>\n");
822 
823     if (session.notes != NULL) {
824 	if (session.show_notes) {
825 	    pputs(prn, "<notes auto-show=\"true\">");
826 	} else {
827 	    pputs(prn, "<notes>");
828 	}
829 	gretl_xml_put_string(session.notes, prn);
830 	pputs(prn, "</notes>\n");
831     }
832 
833     pputs(prn, "</gretl-session>\n");
834 
835     gretl_print_destroy(prn);
836 
837     serialize_user_vars(session.dirname);
838 
839     maybe_write_function_file(tmpname);
840     write_settings_file(tmpname);
841 
842     return 0;
843 }
844