# ini.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD 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, version 2.
    00006  * OpenTTD 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.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "debug.h"
    00014 #include "ini_type.h"
    00015 #include "string_func.h"
    00016 #include "fileio_func.h"
    00017
    00018 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500)
    00019 # define WITH_FDATASYNC
    00020 # include <unistd.h>
    00021 #endif
    00022
    00023 #ifdef WIN32
    00024 # include <windows.h>
    00025 # include <shellapi.h>
    00026 # include "core/mem_func.hpp"
    00027 #endif
    00028
    00033 IniFile::IniFile(const char * const *list_group_names) : IniLoadFile(list_group_names)
    00034 {
    00035 }
    00036
    00042 bool IniFile::SaveToDisk(const char *filename)
    00043 {
    00044   /*
    00045    * First write the configuration to a (temporary) file and then rename
    00046    * that file. This to prevent that when OpenTTD crashes during the save
    00047    * you end up with a truncated configuration file.
    00048    */
    00049   char file_new[MAX_PATH];
    00050
    00051   strecpy(file_new, filename, lastof(file_new));
    00052   strecat(file_new, ".new", lastof(file_new));
    00053   FILE *f = fopen(file_new, "w");
    00054   if (f == NULL) return false;
    00055
    00056   for (const IniGroup *group = this->group; group != NULL; group = group->next) {
    00057     if (group->comment) fputs(group->comment, f);
    00058     fprintf(f, "[%s]\n", group->name);
    00059     for (const IniItem *item = group->item; item != NULL; item = item->next) {
    00060       if (item->comment != NULL) fputs(item->comment, f);
    00061
    00062       /* protect item->name with quotes if needed */
    00063       if (strchr(item->name, ' ') != NULL ||
    00064           item->name[0] == '[') {
    00065         fprintf(f, "\"%s\"", item->name);
    00066       } else {
    00067         fprintf(f, "%s", item->name);
    00068       }
    00069
    00070       fprintf(f, " = %s\n", item->value == NULL ? "" : item->value);
    00071     }
    00072   }
    00073   if (this->comment) fputs(this->comment, f);
    00074
    00075 /*
    00076  * POSIX (and friends) do not guarantee that when a file is closed it is
    00077  * flushed to the disk. So we manually flush it do disk if we have the
    00078  * APIs to do so. We only need to flush the data as the metadata itself
    00079  * (modification date etc.) is not important to us; only the real data is.
    00080  */
    00081 #ifdef WITH_FDATASYNC
    00082   int ret = fdatasync(fileno(f));
    00083   fclose(f);
    00084   if (ret != 0) return false;
    00085 #else
    00086   fclose(f);
    00087 #endif
    00088
    00089 #if defined(WIN32) || defined(WIN64)
    00090   /* Allocate space for one more \0 character. */
    00091   TCHAR tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1];
    00092   _tcsncpy(tfilename, OTTD2FS(filename), MAX_PATH);
    00093   _tcsncpy(tfile_new, OTTD2FS(file_new), MAX_PATH);
    00094   /* SHFileOperation wants a double '\0' terminated string. */
    00095   tfilename[MAX_PATH - 1] = '\0';
    00096   tfile_new[MAX_PATH - 1] = '\0';
    00097   tfilename[_tcslen(tfilename) + 1] = '\0';
    00098   tfile_new[_tcslen(tfile_new) + 1] = '\0';
    00099
    00100   /* Rename file without any user confirmation. */
    00101   SHFILEOPSTRUCT shfopt;
    00102   MemSetT(&shfopt, 0);
    00103   shfopt.wFunc  = FO_MOVE;
    00104   shfopt.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_SILENT;
    00105   shfopt.pFrom  = tfile_new;
    00106   shfopt.pTo    = tfilename;
    00107   SHFileOperation(&shfopt);
    00108 #else
    00109   rename(file_new, filename);
    00110 #endif
    00111
    00112   return true;
    00113 }
    00114
    00115 /* virtual */ FILE *IniFile::OpenFile(const char *filename, Subdirectory subdir, size_t *size)
    00116 {
    00117   /* Open the text file in binary mode to prevent end-of-line translations
    00118    * done by ftell() and friends, as defined by K&R. */
    00119   return FioFOpenFile(filename, "rb", subdir, size);
    00120 }
    00121
    00122 /* virtual */ void IniFile::ReportFileError(const char * const pre, const char * const buffer, const char * const post)
    00123 {
    00124   ShowInfoF("%s%s%s", pre, buffer, post);
    00125 }