1#!/usr/bin/perl -w
2
3# settings.pl: generate settings.c from settings.dat
4# Copyright (c) 2002-2018 Philip Kendall, BogDan Vatra, Alistair Cree
5
6# This program 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 2 of the License, or
9# (at your option) any later version.
10
11# This program 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 along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20# Author contact information:
21
22# E-mail: philip-fuse@shadowmagic.org.uk
23
24use strict;
25
26use Fuse;
27
28sub hashline ($) { '#line ', $_[0] + 1, '"', __FILE__, "\"\n" }
29
30my %options;
31
32while(<>) {
33
34    next if /^\s*$/;
35    next if /^\s*#/;
36
37    chomp;
38
39    my( $name, $type, $default, $short, $commandline, $configfile ) =
40	split /\s*,\s*/;
41
42    if( ( not defined $commandline ) || ( $commandline eq '' ) ) {
43	$commandline = $name;
44	$commandline =~ s/_/-/g;
45    }
46
47    if( ( not defined $configfile ) || ( $configfile eq '' ) ) {
48	$configfile = $commandline;
49	$configfile =~ s/-//g;
50    }
51
52    $options{$name} = { type => $type, default => $default, short => $short,
53			commandline => $commandline,
54			configfile => $configfile };
55}
56
57print Fuse::GPL( 'settings.c: Handling configuration settings',
58		 '2002 Philip Kendall' );
59
60print hashline( __LINE__ ), << 'CODE';
61
62/* This file is autogenerated from settings.dat by settings.pl.
63   Do not edit unless you know what will happen! */
64
65#include <config.h>
66
67#include <errno.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <sys/types.h>
72#include <sys/stat.h>
73#include <unistd.h>
74
75#ifdef HAVE_GETOPT_LONG		/* Did our libc include getopt_long? */
76#include <getopt.h>
77#elif defined AMIGA || defined __MORPHOS__            /* #ifdef HAVE_GETOPT_LONG */
78/* The platform uses GNU getopt, but not getopt_long, so we get
79   symbol clashes on this platform. Just use getopt */
80#else				/* #ifdef HAVE_GETOPT_LONG */
81#include "compat.h"		/* If not, use ours */
82#endif				/* #ifdef HAVE_GETOPT_LONG */
83
84#ifdef HAVE_LIB_XML2
85#include <libxml/xmlmemory.h>
86#include <libxml/parser.h>
87#endif				/* #ifdef HAVE_LIB_XML2 */
88
89#include "fuse.h"
90#include "infrastructure/startup_manager.h"
91#include "machine.h"
92#include "settings.h"
93#include "spectrum.h"
94#include "ui/ui.h"
95#include "utils.h"
96
97/* The name of our configuration file */
98#ifdef WIN32
99#define CONFIG_FILE_NAME "fuse.cfg"
100#else				/* #ifdef WIN32 */
101#define CONFIG_FILE_NAME ".fuserc"
102#endif				/* #ifdef WIN32 */
103
104/* The current settings of options, etc */
105settings_info settings_current;
106
107/* The default settings of options, etc */
108settings_info settings_default = {
109CODE
110
111    foreach my $name ( sort keys %options ) {
112	next if $options{$name}->{type} eq 'null';
113        if( $options{$name}->{type} eq 'string' ) {
114	  print "  /* $name */ (char *)$options{$name}->{default},\n";
115	} else {
116	  print "  /* $name */ $options{$name}->{default},\n";
117	}
118    }
119
120print hashline( __LINE__ ), << 'CODE';
121  /* show_help */ 0,
122  /* show_version */ 0,
123};
124
125static int read_config_file( settings_info *settings );
126
127#ifdef HAVE_LIB_XML2
128static int parse_xml( xmlDocPtr doc, settings_info *settings );
129#else				/* #ifdef HAVE_LIB_XML2 */
130static int parse_ini( utils_file *file, settings_info *settings );
131#endif				/* #ifdef HAVE_LIB_XML2 */
132
133static int settings_command_line( settings_info *settings, int *first_arg,
134				  int argc, char **argv );
135
136static void settings_copy_internal( settings_info *dest, settings_info *src );
137
138/* Called on emulator startup */
139int
140settings_init( int *first_arg, int argc, char **argv )
141{
142  int error;
143
144  settings_defaults( &settings_current );
145
146  error = read_config_file( &settings_current );
147  if( error ) return error;
148
149  error = settings_command_line( &settings_current, first_arg, argc, argv );
150  if( error ) return error;
151
152  return 0;
153}
154
155/* Fill the settings structure with sensible defaults */
156void settings_defaults( settings_info *settings )
157{
158  settings_copy_internal( settings, &settings_default );
159}
160
161#ifdef HAVE_LIB_XML2
162
163/* Read options from the config file (if libxml2 is available) */
164
165static int
166read_config_file( settings_info *settings )
167{
168  const char *cfgdir; char path[ PATH_MAX ];
169
170  xmlDocPtr doc;
171
172  cfgdir = compat_get_config_path(); if( !cfgdir ) return 1;
173
174  snprintf( path, PATH_MAX, "%s/%s", cfgdir, CONFIG_FILE_NAME );
175
176  /* See if the file exists; if doesn't, it's not a problem */
177  if( !compat_file_exists( path ) ) {
178      return 0;
179  }
180
181  doc = xmlReadFile( path, NULL, 0 );
182  if( !doc ) {
183    ui_error( UI_ERROR_ERROR, "error reading config file" );
184    return 1;
185  }
186
187  if( parse_xml( doc, settings ) ) {
188    xmlFreeDoc( doc );
189    return 1;
190  }
191
192  xmlFreeDoc( doc );
193
194  return 0;
195}
196
197static int
198parse_xml( xmlDocPtr doc, settings_info *settings )
199{
200  xmlNodePtr node;
201  xmlChar *xmlstring;
202
203  node = xmlDocGetRootElement( doc );
204  if( xmlStrcmp( node->name, (const xmlChar*)"settings" ) ) {
205    ui_error( UI_ERROR_ERROR, "config file's root node is not 'settings'" );
206    return 1;
207  }
208
209  node = node->xmlChildrenNode;
210  while( node ) {
211
212CODE
213
214foreach my $name ( sort keys %options ) {
215
216    my $type = $options{$name}->{type};
217
218    if( $type eq 'boolean' or $type eq 'numeric' ) {
219
220	print << "CODE";
221    if( !strcmp( (const char*)node->name, "$options{$name}->{configfile}" ) ) {
222      xmlstring = xmlNodeListGetString( doc, node->xmlChildrenNode, 1 );
223      if( xmlstring ) {
224        settings->$name = atoi( (char*)xmlstring );
225        xmlFree( xmlstring );
226      }
227    } else
228CODE
229
230    } elsif( $type eq 'string' ) {
231
232	    print << "CODE";
233    if( !strcmp( (const char*)node->name, "$options{$name}->{configfile}" ) ) {
234      xmlstring = xmlNodeListGetString( doc, node->xmlChildrenNode, 1 );
235      if( xmlstring ) {
236        libspectrum_free( settings->$name );
237        settings->$name = utils_safe_strdup( (char*)xmlstring );
238        xmlFree( xmlstring );
239      }
240    } else
241CODE
242
243    } elsif( $type eq 'null' ) {
244
245	    print << "CODE";
246    if( !strcmp( (const char*)node->name, "$options{$name}->{configfile}" ) ) {
247      /* Do nothing */
248    } else
249CODE
250
251    } else {
252	die "Unknown setting type `$type'";
253    }
254}
255
256print hashline( __LINE__ ), << 'CODE';
257    if( !strcmp( (const char*)node->name, "text" ) ) {
258      /* Do nothing */
259    } else {
260      ui_error( UI_ERROR_WARNING, "Unknown setting '%s' in config file",
261		node->name );
262    }
263
264    node = node->next;
265  }
266
267  return 0;
268}
269
270int
271settings_write_config( settings_info *settings )
272{
273  const char *cfgdir; char path[ PATH_MAX ], buffer[80];
274
275  xmlDocPtr doc; xmlNodePtr root;
276
277  cfgdir = compat_get_config_path(); if( !cfgdir ) return 1;
278
279  snprintf( path, PATH_MAX, "%s/%s", cfgdir, CONFIG_FILE_NAME );
280
281  /* Create the XML document */
282  doc = xmlNewDoc( (const xmlChar*)"1.0" );
283
284  root = xmlNewNode( NULL, (const xmlChar*)"settings" );
285  xmlDocSetRootElement( doc, root );
286CODE
287
288foreach my $name ( sort keys %options ) {
289
290    my $type = $options{$name}->{type};
291
292    if( $type eq 'boolean' ) {
293
294	print "  xmlNewTextChild( root, NULL, (const xmlChar*)\"$options{$name}->{configfile}\", (const xmlChar*)(settings->$name ? \"1\" : \"0\") );\n";
295
296    } elsif( $type eq 'string' ) {
297	print << "CODE";
298  if( settings->$name )
299    xmlNewTextChild( root, NULL, (const xmlChar*)"$options{$name}->{configfile}", (const xmlChar*)settings->$name );
300CODE
301    } elsif( $type eq 'numeric' ) {
302	print << "CODE";
303  snprintf( buffer, 80, "%d", settings->$name );
304  xmlNewTextChild( root, NULL, (const xmlChar*)"$options{$name}->{configfile}", (const xmlChar*)buffer );
305CODE
306    } elsif( $type eq 'null' ) {
307	# Do nothing
308    } else {
309	die "Unknown setting type `$type'";
310    }
311}
312
313  print hashline( __LINE__ ), << 'CODE';
314
315  xmlSaveFormatFile( path, doc, 1 );
316
317  xmlFreeDoc( doc );
318
319  return 0;
320}
321
322#else				/* #ifdef HAVE_LIB_XML2 */
323
324/* Read options from the config file as ini file (if libxml2 is not available) */
325
326static int
327read_config_file( settings_info *settings )
328{
329  const char *cfgdir; char path[ PATH_MAX ];
330  struct stat stat_info;
331  int error;
332
333  utils_file file;
334
335  cfgdir = compat_get_config_path(); if( !cfgdir ) return 1;
336
337  snprintf( path, PATH_MAX, "%s/%s", cfgdir, CONFIG_FILE_NAME );
338
339  /* See if the file exists; if doesn't, it's not a problem */
340  if( stat( path, &stat_info ) ) {
341    if( errno == ENOENT ) {
342      return 0;
343    } else {
344      ui_error( UI_ERROR_ERROR, "couldn't stat '%s': %s", path,
345		strerror( errno ) );
346      return 1;
347    }
348  }
349
350  error = utils_read_file( path, &file );
351  if( error ) {
352    ui_error( UI_ERROR_ERROR, "error reading config file" );
353    return 1;
354  }
355
356  if( parse_ini( &file, settings ) ) { utils_close_file( &file ); return 1; }
357
358  utils_close_file( &file );
359
360  return 0;
361}
362
363static int
364settings_var( settings_info *settings, unsigned char *name, unsigned char *last,
365              int **val_int, char ***val_char, unsigned char **next  )
366{
367  unsigned char* cpos;
368  size_t n;
369
370  *val_int = NULL;
371  *val_char = NULL;
372
373  *next = name;
374  while( name < last && ( *name == ' ' || *name == '\t' || *name == '\r' ||
375                          *name == '\n' ) ) {
376    *next = ++name;					/* seek to first char */
377  }
378  cpos = name;
379
380  while( cpos < last && ( *cpos != '=' && *cpos != ' ' && *cpos != '\t' &&
381                          *cpos != '\r' && *cpos != '\n' ) ) cpos++;
382  *next = cpos;
383  n = cpos - name;		/* length of name */
384
385  while( *next < last && **next != '=' ) {		/* search for '=' */
386    if( **next != ' ' && **next != '\t' && **next != '\r' && **next != '\n' )
387      return 1;	/* error in value */
388    (*next)++;
389  }
390  if( *next < last) (*next)++;		/* set after '=' */
391/*  ui_error( UI_ERROR_WARNING, "Config: (%5s): ", name ); */
392
393CODE
394my %type = ('null' => 0, 'boolean' => 1, 'numeric' => 1, 'string' => 2 );
395foreach my $name ( sort keys %options ) {
396    my $len = length $options{$name}->{configfile};
397
398    print << "CODE";
399  if( n == $len && !strncmp( (const char *)name, "$options{$name}->{configfile}", n ) ) {
400CODE
401    print "    *val_int = \&settings->$name;\n" if( $options{$name}->{type} eq 'boolean' or $options{$name}->{type} eq 'numeric' );
402    print "    *val_char = \&settings->$name;\n" if( $options{$name}->{type} eq 'string' );
403    print "/*    *val_null = \&settings->$name; */\n" if( $options{$name}->{type} eq 'null' );
404    print << "CODE";
405    return 0;
406  }
407CODE
408}
409    print << "CODE";
410  return 1;
411}
412
413static int
414parse_ini( utils_file *file, settings_info *settings )
415{
416  unsigned char *cpos, *cpos_new;
417  int *val_int;
418  char **val_char;
419
420  cpos = file->buffer;
421
422  /* Read until the end of file */
423  while( cpos < file->buffer + file->length ) {
424    if( settings_var( settings, cpos, file->buffer + file->length, &val_int,
425                      &val_char, &cpos_new ) ) {
426      /* error in name or something else ... */
427      cpos = cpos_new + 1;
428      ui_error( UI_ERROR_WARNING,
429                "Unknown and/or invalid setting '%s' in config file", cpos );
430      continue;
431    }
432    cpos = cpos_new;
433    if( val_int ) {
434	*val_int = atoi( (char *)cpos );
435	while( cpos < file->buffer + file->length &&
436		( *cpos != '\\0' && *cpos != '\\r' && *cpos != '\\n' ) ) cpos++;
437    } else if( val_char ) {
438	char *value = (char *)cpos;
439	size_t n = 0;
440	while( cpos < file->buffer + file->length &&
441		( *cpos != '\\0' && *cpos != '\\r' && *cpos != '\\n' ) ) cpos++;
442	n = (char *)cpos - value;
443	if( n > 0 ) {
444	  if( *val_char != NULL ) {
445	    libspectrum_free( *val_char );
446	    *val_char = NULL;
447	  }
448	  *val_char = libspectrum_new( char, n + 1 );
449	  (*val_char)[n] = '\\0';
450	  memcpy( *val_char, value, n );
451	}
452    }
453    /* skip 'new line' like chars */
454    while( ( cpos < ( file->buffer + file->length ) ) &&
455           ( *cpos == '\\r' || *cpos == '\\n' ) ) cpos++;
456
457CODE
458print hashline( __LINE__ ), << 'CODE';
459  }
460
461  return 0;
462}
463
464static int
465settings_file_write( compat_fd fd, const char *buffer, size_t length )
466{
467  return compat_file_write( fd, (const unsigned char *)buffer, length );
468}
469
470static int
471settings_string_write( compat_fd doc, const char* name, const char* config )
472{
473  if( config != NULL &&
474      ( settings_file_write( doc, name, strlen( name ) ) ||
475        settings_file_write( doc, "=", 1 ) ||
476        settings_file_write( doc, config, strlen( config ) ) ||
477        settings_file_write( doc, FUSE_EOL, strlen( FUSE_EOL ) ) ) )
478    return 1;
479  return 0;
480}
481
482static int
483settings_boolean_write( compat_fd doc, const char* name, int config )
484{
485  return settings_string_write( doc, name, config ? "1" : "0" );
486}
487
488static int
489settings_numeric_write( compat_fd doc, const char* name, int config )
490{
491  char buffer[80];
492  snprintf( buffer, sizeof( buffer ), "%d", config );
493  return settings_string_write( doc, name, buffer );
494}
495
496int
497settings_write_config( settings_info *settings )
498{
499  const char *cfgdir; char path[ PATH_MAX ];
500
501  compat_fd doc;
502
503  cfgdir = compat_get_config_path(); if( !cfgdir ) return 1;
504
505  snprintf( path, PATH_MAX, "%s/%s", cfgdir, CONFIG_FILE_NAME );
506
507  doc = compat_file_open( path, 1 );
508  if( doc == COMPAT_FILE_OPEN_FAILED ) {
509    ui_error( UI_ERROR_ERROR, "couldn't open `%s' for writing: %s\n",
510	      path, strerror( errno ) );
511    return 1;
512  }
513
514CODE
515
516foreach my $name ( sort keys %options ) {
517
518    my $type = $options{$name}->{type};
519    my $len = length "$options{$name}->{configfile}";
520
521    if( $type eq 'boolean' ) {
522
523	print << "CODE";
524  if( settings_boolean_write( doc, "$options{$name}->{configfile}",
525                              settings->$name ) )
526    goto error;
527CODE
528
529    } elsif( $type eq 'string' ) {
530	print << "CODE";
531  if( settings_string_write( doc, "$options{$name}->{configfile}",
532                             settings->$name ) )
533    goto error;
534CODE
535
536    } elsif( $type eq 'numeric' ) {
537	print << "CODE";
538  if( settings_numeric_write( doc, "$options{$name}->{configfile}",
539                              settings->$name ) )
540    goto error;
541CODE
542
543    } elsif( $type eq 'null' ) {
544	# Do nothing
545    } else {
546	die "Unknown setting type `$type'";
547    }
548}
549
550  print hashline( __LINE__ ), << 'CODE';
551
552  compat_file_close( doc );
553
554  return 0;
555error:
556  compat_file_close( doc );
557
558  return 1;
559}
560
561#endif				/* #ifdef HAVE_LIB_XML2 */
562
563/* Read options from the command line */
564static int
565settings_command_line( settings_info *settings, int *first_arg,
566                       int argc, char **argv )
567{
568#ifdef GEKKO
569  /* No argv on the Wii. Just return */
570  return 0;
571#endif
572
573#if !defined AMIGA && !defined __MORPHOS__
574
575  struct option long_options[] = {
576
577CODE
578
579my $fake_short_option = 256;
580
581foreach my $name ( sort keys %options ) {
582
583    my $type = $options{$name}->{type};
584    my $commandline = $options{$name}->{commandline};
585    my $short = $options{$name}->{short};
586
587    unless( $type eq 'boolean' or $short ) { $short = $fake_short_option++ }
588
589    if( $type eq 'boolean' ) {
590
591	print << "CODE";
592    {    "$commandline", 0, &(settings->$name), 1 },
593    { "no-$commandline", 0, &(settings->$name), 0 },
594CODE
595    } elsif( $type eq 'string' or $type eq 'numeric' ) {
596
597	print "    { \"$commandline\", 1, NULL, $short },\n";
598    } elsif( $type eq 'null' ) {
599	# Do nothing
600    } else {
601	die "Unknown setting type `$type'";
602    }
603}
604
605print hashline( __LINE__ ), << 'CODE';
606
607    { "help", 0, NULL, 'h' },
608    { "version", 0, NULL, 'V' },
609
610    { 0, 0, 0, 0 }		/* End marker: DO NOT REMOVE */
611  };
612
613#endif      /* #ifndef AMIGA */
614
615  while( 1 ) {
616
617    int c;
618
619#if defined AMIGA || defined __MORPHOS__
620    c = getopt( argc, argv, "d:hm:o:p:f:r:s:t:v:g:j:V" );
621#else                    /* #ifdef AMIGA */
622    c = getopt_long( argc, argv, "d:hm:o:p:f:r:s:t:v:g:j:V", long_options, NULL );
623#endif                   /* #ifdef AMIGA */
624
625    if( c == -1 ) break;	/* End of option list */
626
627    switch( c ) {
628
629    case 0: break;	/* Used for long option returns */
630
631CODE
632
633$fake_short_option = 256;
634
635foreach my $name ( sort keys %options ) {
636
637    my $type = $options{$name}->{type};
638    my $short = $options{$name}->{short};
639
640    unless( $type eq 'boolean' or $short ) { $short = $fake_short_option++ }
641
642    if( $type eq 'boolean' ) {
643	# Do nothing
644    } elsif( $type eq 'string' ) {
645	print "    case $short: settings_set_string( &settings->$name, optarg ); break;\n";
646    } elsif( $type eq 'numeric' ) {
647	print "    case $short: settings->$name = atoi( optarg ); break;\n";
648    } elsif( $type eq 'null' ) {
649	# Do nothing
650    } else {
651	die "Unknown setting type `$type'";
652    }
653}
654
655print hashline( __LINE__ ), << 'CODE';
656
657    case 'h': settings->show_help = 1; break;
658    case 'V': settings->show_version = 1; break;
659
660    case ':':
661    case '?':
662      break;
663
664    default:
665      fprintf( stderr, "%s: getopt_long returned `%c'\n",
666	       fuse_progname, (char)c );
667      break;
668
669    }
670  }
671
672  /* Store the location of the first non-option argument */
673  *first_arg = optind;
674
675  return 0;
676}
677
678/* Copy one settings object to another */
679static void
680settings_copy_internal( settings_info *dest, settings_info *src )
681{
682  settings_free( dest );
683
684CODE
685
686foreach my $name ( sort keys %options ) {
687
688    my $type = $options{$name}->{type};
689
690    if( $type eq 'boolean' or $type eq 'numeric' ) {
691	print "  dest->$name = src->$name;\n";
692    } elsif( $type eq 'string' ) {
693	print << "CODE";
694  dest->$name = NULL;
695  if( src->$name ) {
696    dest->$name = utils_safe_strdup( src->$name );
697  }
698CODE
699    }
700}
701
702print hashline( __LINE__ ), << 'CODE';
703}
704
705/* Copy one settings object to another */
706void settings_copy( settings_info *dest, settings_info *src )
707{
708  settings_defaults( dest );
709  settings_copy_internal( dest, src );
710}
711
712char **
713settings_get_rom_setting( settings_info *settings, size_t which,
714			  int is_peripheral )
715{
716  if( !is_peripheral ) {
717    switch( which ) {
718    case  0: return &( settings->rom_16       );
719    case  1: return &( settings->rom_48       );
720    case  2: return &( settings->rom_128_0    );
721    case  3: return &( settings->rom_128_1    );
722    case  4: return &( settings->rom_plus2_0  );
723    case  5: return &( settings->rom_plus2_1  );
724    case  6: return &( settings->rom_plus2a_0 );
725    case  7: return &( settings->rom_plus2a_1 );
726    case  8: return &( settings->rom_plus2a_2 );
727    case  9: return &( settings->rom_plus2a_3 );
728    case 10: return &( settings->rom_plus3_0  );
729    case 11: return &( settings->rom_plus3_1  );
730    case 12: return &( settings->rom_plus3_2  );
731    case 13: return &( settings->rom_plus3_3  );
732    case 14: return &( settings->rom_plus3e_0 );
733    case 15: return &( settings->rom_plus3e_1 );
734    case 16: return &( settings->rom_plus3e_2 );
735    case 17: return &( settings->rom_plus3e_3 );
736    case 18: return &( settings->rom_tc2048   );
737    case 19: return &( settings->rom_tc2068_0 );
738    case 20: return &( settings->rom_tc2068_1 );
739    case 21: return &( settings->rom_ts2068_0 );
740    case 22: return &( settings->rom_ts2068_1 );
741    case 23: return &( settings->rom_pentagon_0 );
742    case 24: return &( settings->rom_pentagon_1 );
743    case 25: return &( settings->rom_pentagon_2 );
744    case 26: return &( settings->rom_pentagon512_0 );
745    case 27: return &( settings->rom_pentagon512_1 );
746    case 28: return &( settings->rom_pentagon512_2 );
747    case 29: return &( settings->rom_pentagon512_3 );
748    case 30: return &( settings->rom_pentagon1024_0 );
749    case 31: return &( settings->rom_pentagon1024_1 );
750    case 32: return &( settings->rom_pentagon1024_2 );
751    case 33: return &( settings->rom_pentagon1024_3 );
752    case 34: return &( settings->rom_scorpion_0 );
753    case 35: return &( settings->rom_scorpion_1 );
754    case 36: return &( settings->rom_scorpion_2 );
755    case 37: return &( settings->rom_scorpion_3 );
756    case 38: return &( settings->rom_spec_se_0 );
757    case 39: return &( settings->rom_spec_se_1 );
758    default: return NULL;
759    }
760  } else {
761    switch( which ) {
762    case  0: return &( settings->rom_interface_1 );
763    case  1: return &( settings->rom_beta128 );
764    case  2: return &( settings->rom_plusd );
765    case  3: return &( settings->rom_didaktik80 );
766    case  4: return &( settings->rom_disciple );
767    case  5: return &( settings->rom_multiface1 );
768    case  6: return &( settings->rom_multiface128 );
769    case  7: return &( settings->rom_multiface3 );
770    case  8: return &( settings->rom_opus );
771    case  9: return &( settings->rom_speccyboot );
772    case 10: return &( settings->rom_ttx2000s );
773    case 11: return &( settings->rom_usource );
774    default: return NULL;
775    }
776  }
777}
778
779void
780settings_set_string( char **string_setting, const char *value )
781{
782  /* No need to do anything if the two strings are in fact the
783     same pointer */
784  if( *string_setting == value ) return;
785
786  if( *string_setting ) libspectrum_free( *string_setting );
787  *string_setting = utils_safe_strdup( value );
788}
789
790int
791settings_free( settings_info *settings )
792{
793CODE
794
795foreach my $name ( sort keys %options ) {
796    if( $options{$name}->{type} eq 'string' ) {
797	print "  if( settings->$name ) libspectrum_free( settings->$name );\n";
798    }
799}
800
801print hashline( __LINE__ ), << 'CODE';
802
803  return 0;
804}
805
806static void
807settings_end( void )
808{
809  if( settings_current.autosave_settings )
810    settings_write_config( &settings_current );
811
812  settings_free( &settings_current );
813
814#ifdef HAVE_LIB_XML2
815  xmlCleanupParser();
816#endif				/* #ifdef HAVE_LIB_XML2 */
817}
818
819void
820settings_register_startup( void )
821{
822  /* settings_init not yet managed by the startup manager */
823
824  startup_manager_module dependencies[] = {
825  /* Fuse for OS X requires that settings_end is called before memory is
826     deallocated as settings need to look up machine names etc */
827    /* STARTUP_MANAGER_MODULE_MEMORY, */
828    STARTUP_MANAGER_MODULE_SETUID,
829  };
830  startup_manager_register( STARTUP_MANAGER_MODULE_SETTINGS_END, dependencies,
831                            ARRAY_SIZE( dependencies ), NULL, NULL,
832                            settings_end );
833}
834
835CODE
836