/* gcompris - properties.c * * Copyright (C) 2000,2003 Bruno Coudoin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #define G_STDIO_NO_WRAP_ON_UNIX #include #include #include #include "gcompris.h" /* This should be detected in the configure for good portability */ #define HAVE_SETENV 1 #ifdef WIN32 # define WIN32_LEAN_AND_MEAN # include #endif /* * return 1 if parsing OK, 0 otherwise * the return value is returned in retval */ static guint scan_get_int(GScanner *scanner, int *retval) { GTokenType token = g_scanner_get_next_token (scanner); token = g_scanner_get_next_token (scanner); if(token == G_TOKEN_INT) { /* we got it */ GTokenValue value = g_scanner_cur_value(scanner); *retval = value.v_int; return 1; } return 0; } /* * return String if parsing OK, NULL otherwise */ static gchar * scan_get_string(GScanner *scanner) { GTokenType token = g_scanner_get_next_token (scanner); token = g_scanner_get_next_token (scanner); if(token == G_TOKEN_STRING) { /* we got it */ GTokenValue value = g_scanner_cur_value(scanner); return (g_strdup(value.v_string)); } return NULL; } /* get the gcompris config directory name * * \return a newly allocated string * * \warning Architecture dependant: "gcompris" in Win9x, * "/.gcompris" in POSIX compliant systems */ static gchar * gc_prop_default_config_directory_get () { #ifdef WIN32 if ( ! G_WIN32_IS_NT_BASED() ) /* Win9x */ return g_strdup("../gcompris"); else #endif { const gchar *home_dir = g_get_home_dir(); const gchar *xdg_config_home = g_getenv("XDG_CONFIG_HOME"); if(xdg_config_home) return g_strconcat(xdg_config_home, "/.gcompris", NULL); gchar *xdg_config= g_strconcat(home_dir, "/.config", NULL); gc_util_create_rootdir(xdg_config); g_free(xdg_config); return g_strconcat(home_dir, "/.config/gcompris", NULL); } } /* get the gcompris data directory name * * \return a newly allocated string * * \warning Architecture dependant: "gcompris" in Win9x, * "/MyGCompris" in POSIX compliant systems */ static gchar * gc_prop_default_user_directory_get () { char *name = "My GCompris"; #ifdef WIN32 if ( ! G_WIN32_IS_NT_BASED() ) /* Win9x */ return g_strconcat("../", name, NULL); else #endif { const gchar *home_dir = g_get_home_dir(); return g_strconcat(home_dir, "/", name, NULL); } } /** return the name of the configuration file used. * must not be freed. * */ static const gchar * gc_prop_config_file_get() { /* Was never called, must calculate it */ #ifdef WIN32 if (! G_WIN32_IS_NT_BASED() ) { return("gcompris.cfg"); } else #endif return("gcompris.conf"); } /** Create a new GcomprisProperties struct with it's default values * * * \return a newly allocated GcomprisProperties */ GcomprisProperties * gc_prop_new () { GcomprisProperties *tmp; tmp = (GcomprisProperties *) malloc (sizeof (GcomprisProperties)); tmp->music = 1; tmp->fx = 1; tmp->screensize = 1; tmp->fullscreen = 0; tmp->noxf86vm = FALSE; tmp->timer = 1; tmp->skin = g_strdup("babytoy"); tmp->key = g_strdup("default"); tmp->locale = NULL; tmp->difficulty_max = 0; tmp->filter_style = GCOMPRIS_FILTER_NONE; /* No difficulty filter by default */ tmp->difficulty_filter = 1; /* No difficulty filter by default */ tmp->disable_quit = 0; /* Used to remove the quit button from the bar. Use it for kiosk mode */ tmp->disable_config = 0; /* Used to remove the config button from the bar. Use it for kiosk mode */ tmp->display_resource = 0; tmp->root_menu = g_strdup("/"); tmp->local_directory = NULL; tmp->profile = NULL; tmp->logged_user = NULL; tmp->administration = FALSE; tmp->reread_menu = FALSE; tmp->experimental = FALSE; tmp->server = NULL; tmp->package_data_dir = NULL; tmp->package_locale_dir = NULL; tmp->package_plugin_dir = NULL; tmp->package_python_plugin_dir = NULL; tmp->system_icon_dir = NULL; tmp->cache_dir = NULL; tmp->drag_mode = GC_DRAG_MODE_GRAB; tmp->config_dir = gc_prop_default_config_directory_get(); tmp->user_dir = gc_prop_default_user_directory_get(); tmp->database = NULL; return (tmp); } void gc_prop_old_config_migration(GcomprisProperties *props) { char *old; char *new; new = g_strconcat(props->config_dir, "/", gc_prop_config_file_get(), NULL); if (!g_file_test(new, G_FILE_TEST_IS_REGULAR)) { old = g_strconcat(g_get_home_dir(), "/.gcompris/gcompris.conf", NULL); if (g_file_test(old, G_FILE_TEST_IS_REGULAR)) { printf("Config file migration '%s' -> '%s'\n", old, new); if (!g_file_test(props->config_dir, G_FILE_TEST_IS_DIR)) gc_util_create_rootdir(props->config_dir); g_rename(old, new); } g_free(old); } g_free(new); /* DATA BASE FILE MIGRATION */ new = g_strconcat(props->config_dir, "/gcompris_sqlite.db", NULL); if (!g_file_test(new, G_FILE_TEST_IS_REGULAR)) { old = g_strconcat(g_get_home_dir(), "/.gcompris/shared/profiles/gcompris_sqlite.db", NULL); if (g_file_test(old, G_FILE_TEST_IS_REGULAR)) { printf("Database migration '%s' -> '%s'\n", old, new); g_rename(old, new); } g_free(old); } g_free(new); /* LOG FILE MIGRATION */ new = g_strconcat(props->config_dir, "/gcompris.log", NULL); if (!g_file_test(new, G_FILE_TEST_IS_REGULAR)) { old = g_strconcat(g_get_home_dir(), "/.gcompris/gcompris.log", NULL); if (g_file_test(old, G_FILE_TEST_IS_REGULAR)) { printf("Logs migration '%s' -> '%s'\n", old, new); g_rename(old, new); } g_free(old); } g_free(new); /* User Images Migration */ new = g_strconcat(props->user_dir, NULL); if (!g_file_test(new, G_FILE_TEST_IS_DIR)) gc_util_create_rootdir(new); g_free(new); new = g_strconcat(props->user_dir, "/Images", NULL); if (!g_file_test(new, G_FILE_TEST_IS_DIR)) { old = g_strconcat(g_get_home_dir(), "/.gcompris/user_data/images", NULL); if (g_file_test(old, G_FILE_TEST_IS_DIR)) { int retval = g_rename(old, new); printf("Image directory migration (%d) '%s' -> '%s'\n", retval, old, new); } g_free(old); } g_free(new); /* User Activity Data Migration */ old = g_strconcat(g_get_home_dir(), "/.gcompris/users", NULL); if (g_file_test(old, G_FILE_TEST_IS_DIR)) { GDir *diruser = g_dir_open(old, 0, NULL); const gchar *user; while((user = g_dir_read_name(diruser))) { char *old2 = g_strconcat(old, "/", user, NULL); GDir *diractivity = g_dir_open(old2, 0, NULL); const gchar *activity; while((activity = g_dir_read_name(diractivity))) { char *old3 = g_strconcat(old, "/", user, "/", activity, NULL); GDir *dirfile = g_dir_open(old3, 0, NULL); const gchar *file; while((file = g_dir_read_name(dirfile))) { gchar *oldfullfile = g_strconcat(old3, "/", file, NULL); gchar *newfulldir = g_strconcat(props->user_dir, "/", activity, NULL); gchar *newfullfile = g_strconcat(props->user_dir, "/", activity, "/", file, NULL); gc_util_create_rootdir(newfulldir); /* Oops, the file already exists, prepend the user name */ if(g_file_test(newfullfile, G_FILE_TEST_IS_REGULAR)) { g_free(newfullfile); newfullfile = g_strconcat(props->user_dir, "/", activity, "/", user, "_", file, NULL); printf("Duplicate file, prepending user name %s\n", newfullfile); } int retval = g_rename(oldfullfile, newfullfile); printf("Data file migration (%d) '%s' -> '%s'\n", retval, oldfullfile, newfullfile); g_free(oldfullfile); g_free(newfulldir); g_free(newfullfile); } g_free(old3); g_dir_close(dirfile); } g_dir_close(diractivity); g_free(old2); } g_dir_close(diruser); } g_free(old); } void gc_prop_load (GcomprisProperties *props, GCPropSourceConf source_conf) { char *config_file = NULL; GScanner *scanner; gchar *content; gsize length; #ifndef WIN32 const gchar *locale; #endif switch(source_conf) { case GC_PROP_FROM_SYSTEM_CONF: config_file = g_strconcat(SYSTEM_CONFIG_DIR, "/", gc_prop_config_file_get(), NULL); break; case GC_PROP_FROM_USER_CONF: config_file = g_strconcat(props->config_dir, "/", gc_prop_config_file_get(), NULL); break; } if(g_file_get_contents(config_file, &content, &length, NULL)) { g_warning("Loading config file '%s'", config_file); /* create a new scanner */ scanner = g_scanner_new(NULL); /* set up the scanner to read from the file */ g_scanner_input_text(scanner, content, length); /* while the next token is something else other than end of file */ while(g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) { /* get the next token */ GTokenType tokent = g_scanner_get_next_token(scanner); switch(tokent) { case G_TOKEN_IDENTIFIER: { gchar *token; /* if we have a symbol, check it's ours */ GTokenValue value = g_scanner_cur_value(scanner); token = g_strdup(value.v_identifier); if(!strcmp(value.v_identifier, "music")) { if(!scan_get_int(scanner, &props->music)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "fx")) { if(!scan_get_int(scanner, &props->fx)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "screensize")) { if(!scan_get_int(scanner, &props->screensize)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "fullscreen")) { if(!scan_get_int(scanner, &props->fullscreen)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "noxf86vm")) { if(!scan_get_int(scanner, &props->noxf86vm)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "timer")) { if(!scan_get_int(scanner, &props->timer)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "difficulty_filter")) { if(!scan_get_int(scanner, &props->difficulty_filter)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "disable_quit")) { if(!scan_get_int(scanner, &props->disable_quit)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "disable_config")) { if(!scan_get_int(scanner, &props->disable_config)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "filter_style")) { if(!scan_get_int(scanner, &props->filter_style)) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "skin")) { g_free(props->skin); props->skin = scan_get_string(scanner); if(!props->skin) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "user_dir")) { g_free(props->user_dir); props->user_dir = scan_get_string(scanner); if(!props->user_dir) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "config_dir")) { g_free(props->config_dir); props->config_dir = scan_get_string(scanner); if(!props->config_dir) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "locale")) { props->locale = scan_get_string(scanner); if(!props->locale) g_warning("Config file parsing error on token %s", token); } else if(!strcmp(value.v_identifier, "key")) { g_free(props->key); props->key = scan_get_string(scanner); if(!props->key) g_warning("Config file parsing error on token %s", token); } g_free(token); break; } default: break; } } /* destroy the scanner */ g_scanner_destroy(scanner); g_free(content); } /* * Warning, gcompris need a proper locale prefix to find suitable dataset * Some system use LOCALE 'C' for english. We have to set it explicitly */ if(!props->locale) { #if defined WIN32 props->locale = g_win32_getlocale(); #else locale = g_getenv("LC_ALL"); if(locale == NULL) locale = g_getenv("LC_CTYPE"); if(locale == NULL) locale = g_getenv("LANG"); if (locale != NULL && !strcmp(locale, "C")) { props->locale = "en_US.UTF-8"; } #endif } if(!props->locale) { /* No user specified locale = '' */ props->locale = strdup(""); } g_free(config_file); } /** \brief One the properties have been loaded and overloaded by the user params * call this function to create the directory structure mandatory to make GCompris * works. * */ void gc_prop_activate(GcomprisProperties *props) { char *tmp; if (!g_file_test(props->config_dir, G_FILE_TEST_IS_DIR)) gc_util_create_rootdir(props->config_dir); if (!g_file_test(props->user_dir, G_FILE_TEST_IS_DIR)) gc_util_create_rootdir(props->user_dir); tmp = g_strconcat(props->user_dir, "/Images", NULL); if (!g_file_test(tmp, G_FILE_TEST_IS_DIR)) gc_util_create_rootdir(tmp); g_free(tmp); tmp = g_strconcat(props->user_dir, "/", _("readme"), ".txt", NULL); g_file_set_contents(tmp, _("This directory contains the files you create with the GCompris educational suite\n"), -1, NULL); g_free(tmp); tmp = g_strconcat(props->user_dir, "/Images/", _("readme"), ".txt", NULL); g_file_set_contents(tmp, _("Put any number of images in this directory.\n" "You can include these images in your drawings and animations.\n" "The image formats supported are jpeg, png and svg.\n"), -1, NULL); g_free(tmp); } void gc_prop_destroy (GcomprisProperties *props) { if(!props) return; g_free(props->package_data_dir); g_free(props->package_locale_dir); g_free(props->package_plugin_dir); g_free(props->package_python_plugin_dir); g_free(props->system_icon_dir); g_free(props->cache_dir); g_free(props->locale); g_free(props->skin); g_free(props->key); gc_profile_destroy(props->profile); gc_user_destroy(props->logged_user); g_free(props->database); g_free(props->config_dir); g_free(props->user_dir); g_free(props->server); g_free(props->root_menu); g_free(props); g_warning("properties free"); } void gc_prop_save (GcomprisProperties *props) { char *config_file = g_strconcat(props->config_dir, "/", gc_prop_config_file_get(), NULL); FILE *filefd; filefd = g_fopen(config_file, "w+"); if(!filefd) { g_warning("cannot open '%s', configuration file not saved\n",(char *) config_file); return; } fprintf(filefd, "%s=%d\n", "music", props->music); fprintf(filefd, "%s=%d\n", "fx", props->fx); fprintf(filefd, "%s=%d\n", "screensize", props->screensize); fprintf(filefd, "%s=%d\n", "fullscreen", props->fullscreen); fprintf(filefd, "%s=%d\n", "timer", props->timer); fprintf(filefd, "%s=\"%s\"\n", "skin", props->skin); fprintf(filefd, "%s=\"%s\"\n", "locale", props->locale); fprintf(filefd, "%s=\"%s\"\n", "key", props->key); fclose(filefd); g_free(config_file); } int gc_setenv (const char * name, const char * value) { #if defined WIN32 size_t namelen = strlen(name); size_t valuelen = (value==NULL ? 0 : strlen(value)); /* On Woe32, each process has two copies of the environment variables, one managed by the OS and one managed by the C library. We set the value in both locations, so that other software that looks in one place or the other is guaranteed to see the value. Even if it's a bit slow. See also */ if (!SetEnvironmentVariableA(name,value)) return -1; //#endif //#if defined(HAVE_PUTENV) char* buffer = (char*)malloc(namelen+1+valuelen+1); if (!buffer) return -1; /* no need to set errno = ENOMEM */ memcpy(buffer,name,namelen); if (value != NULL) { buffer[namelen] = '='; memcpy(buffer+namelen+1,value,valuelen); buffer[namelen+1+valuelen] = 0; } else buffer[namelen] = 0; return putenv(buffer); #elif defined(HAVE_SETENV) return setenv(name,value,1); #else /* Uh oh, neither putenv() nor setenv() ... */ return -1; #endif }