1 // Config.cpp: implementation of the CConfigFile class.
2 //
3 /////////////////////////////////////////////////////////////////////////////
4 // Name:        config.cpp
5 // Purpose:     Application configuration class, deal with configuration automatically
6 // Author:      PCMan (HZY)   http://pcman.ptt.cc/
7 // E-mail:      pcman.tw@gmail.com
8 // Created:     2004.7.22
9 // Copyright:   (C) 2004 PCMan
10 // Licence:     GPL : http://www.gnu.org/licenses/gpl.html
11 // Modified by:
12 /////////////////////////////////////////////////////////////////////////////
13 
14 #ifdef __GNUG__
15   #pragma implementation "configfile.h"
16 #endif
17 
18 
19 //	Copyright (C) 2004 PCMan
20 //	Author: PCMan (HZY) 2004/07/22	07:51 AM
21 //	I finally came up with a really smart way to maintain ini file.
22 //	Every time I add a variable to CAppConfig, all I need to do is
23 //	adding the variable in my "Config Table," and all the data will
24 //	be load and save automatically.  This is not the most efficient way.
25 //	In my first version I use some more efficient method, but at last I change
26 //	my code to what it is now.  Because I think in a program not time-critical,
27 //	easy-maintaining is much more important.
28 
29 #include "configfile.h"
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 
37 #include <gdk/gdk.h>
38 #include <glib.h>
39 
40 #include "stringutil.h"
41 
42 //////////////////////////////////////////////////////////////////////
43 // Construction/Destruction
44 //////////////////////////////////////////////////////////////////////
45 
46 
CConfigFile(string AppName,int LineBufSize)47 CConfigFile::CConfigFile( string AppName, int LineBufSize )
48 	: m_pCurSect(NULL),
49 	  m_pRootMap(NULL)
50 {
51 	m_AppName = AppName;
52 	m_LineBufSize = LineBufSize;
53 
54 	m_ConfigDirPath = getenv("HOME");
55 	if( m_ConfigDirPath[m_ConfigDirPath.length()-1] != '/' )
56 		m_ConfigDirPath += '/';
57 
58 	m_ConfigDirPath += '.';
59 	m_ConfigDirPath += m_AppName;
60 
61 	if(!g_file_test(m_ConfigDirPath.c_str(),
62 		GFileTest(G_FILE_TEST_IS_DIR|G_FILE_TEST_EXISTS)) )
63 			mkdir(m_ConfigDirPath.c_str(), 0777);
64 
65 	m_ConfigDirPath += "/";
66 
67 	m_DataDirPath = DATADIR "/" + AppName;
68 
69 	if( g_file_test(m_DataDirPath.c_str(),
70 		GFileTest(G_FILE_TEST_IS_DIR|G_FILE_TEST_EXISTS)) )
71 		m_DataDirPath += "/";
72 	else
73 		m_DataDirPath = m_ConfigDirPath;
74 }
75 
~CConfigFile()76 CConfigFile::~CConfigFile()
77 {
78 }
79 
80 
81 
Load()82 bool CConfigFile::Load()
83 {
84 	return DoDataExchange(true);	// call virtual function defined in derived class.
85 }
86 
DoLoad()87 bool CConfigFile::DoLoad()
88 {
89 	FILE* fp = fopen( GetConfigPath().c_str() ,"r");
90 	if(fp)
91 	{
92 		char* line = new char[m_LineBufSize];
93 		while( fgets( line, m_LineBufSize, fp) )
94 		{
95 			char* keyname = strtok( line, " =\n");
96 			if( !keyname )	continue;
97 			if( keyname[0] == '[' )	// This line doesn't contain any value.
98 			{
99 				keyname = strtok( line, "[]\n" );
100 				if( keyname )	// Find a section.
101 				{
102 					CConfigEntry* pent = m_pRootMap;
103 					for( ; pent->m_Name; pent++ )
104 					{
105 						if( 0 == strcmp(keyname, pent->m_Name ) )
106 						{
107 							m_pCurSect = (CConfigEntry*)pent->m_pData;
108 							break;
109 						}
110 					}
111 					continue;
112 				}
113 			}
114 			char* pstrval = strtok( NULL, "=\n");
115 			if( !pstrval )	continue;
116 
117 			CConfigEntry* pent = m_pCurSect;
118 			for( ; pent->m_Name; pent++ )
119 			{
120 				if( 0 == strcmp(keyname, pent->m_Name ) )
121 					break;
122 			}
123 
124 			if( pent->m_Name )
125 			{
126 				switch( pent->m_DataType )
127 				{
128 				case CConfigEntry::VT_BOOL:
129 					*((bool*)pent->m_pData) = atoi(pstrval);
130 					break;
131 				case CConfigEntry::VT_INT:
132 					*((int*)pent->m_pData) = atoi(pstrval);
133 					break;
134 				case CConfigEntry::VT_STR:
135 					*((string*)pent->m_pData) = pstrval;
136 					break;
137 				case CConfigEntry::VT_ESTR:
138 					*((string*)pent->m_pData) = UnEscapeStr( pstrval );
139 					break;
140 				case CConfigEntry::VT_COLOR:
141 					{
142 						int r,g,b;
143 						if( 3 == sscanf( pstrval, "%d,%d,%d", &r, &g, &b ) )
144 						{
145 //							((wxColour*)pent->m_pData)->Set( r, g, b );
146 							GdkColor* clr = (GdkColor*)pent->m_pData;
147 							clr->red = r*(65536/256);
148 							clr->green = g*(65536/256);
149 							clr->blue = b*(65536/256);
150 						}
151 					}
152 					break;
153 				case CConfigEntry::VT_SHORT:
154 					*((short*)pent->m_pData) = atoi(pstrval);
155 					break;
156 				default:
157 					break;	//	This shouldn't happen.
158 				}
159 			}
160 		}
161 		fclose(fp);
162 		delete []line;
163 	}
164 	return fp;
165 }
166 
Save()167 bool CConfigFile::Save()
168 {
169 	return DoDataExchange(false);	// call virtual function defined in derived class.
170 }
171 
DoSave()172 bool CConfigFile::DoSave()
173 {
174 	FILE* fp = fopen( GetConfigPath().c_str() ,"w");
175 	if(fp)
176 	{
177 		string esc_str;
178 		for( ; m_pRootMap->m_Name; m_pRootMap++ )
179 		{
180 			m_pCurSect = (CConfigEntry*)m_pRootMap->m_pData;
181 			fprintf(fp, "[%s]\n", m_pRootMap->m_Name );
182 
183 			char strval[32];	const char* pstrval;
184 			for( ; m_pCurSect->m_Name; m_pCurSect++ )
185 			{
186 				pstrval = strval;
187 				switch( m_pCurSect->m_DataType )
188 				{
189 				case CConfigEntry::VT_BOOL:
190 					sprintf(strval,"%d", (int)*((bool*)m_pCurSect->m_pData));
191 					break;
192 				case CConfigEntry::VT_INT:
193 					sprintf(strval,"%d", *((int*)m_pCurSect->m_pData) );
194 					break;
195 				case CConfigEntry::VT_STR:
196 					pstrval = ((string*)m_pCurSect->m_pData)->c_str();
197 					break;
198 				case CConfigEntry::VT_ESTR:
199 					esc_str = EscapeStr( ((string*)m_pCurSect->m_pData)->c_str() );
200 					pstrval = esc_str.c_str();
201 					break;
202 				case CConfigEntry::VT_COLOR:
203 					{
204 //						wxColour& clr = *((wxColour*)m_pCurSect->m_pData);
205 						GdkColor& clr = *((GdkColor*)m_pCurSect->m_pData);
206 						sprintf( strval, "%d,%d,%d", clr.red*256/65536, clr.green*256/65536, clr.blue*256/65536 );
207 					}
208 					break;
209 				case CConfigEntry::VT_SHORT:
210 					sprintf(strval,"%d", *((short*)m_pCurSect->m_pData) );
211 					break;
212 				default:	// This shouldn't happen.
213 					break;
214 				}
215 				fprintf(fp, "%s=%s\n", m_pCurSect->m_Name, pstrval );
216 			}
217 			fputs( "\n", fp );
218 		}
219 		fclose(fp);
220 	}
221 	return fp;
222 }
223 
DoDataExchange(bool bLoad)224 bool CConfigFile::DoDataExchange( bool bLoad )
225 {
226 	return bLoad ? DoLoad():DoSave();
227 }
228 
GetConfigPath(string FileName)229 string CConfigFile::GetConfigPath( string FileName )
230 {
231 	string path = m_ConfigDirPath;
232 	path += FileName;
233 
234 	return path;
235 }
236 
GetDataPath(string FileName)237 string CConfigFile::GetDataPath( string FileName )
238 {
239 	// Windows NT or UNIX-like systems
240 	string path = GetConfigPath(FileName);
241 //	if( ::wxFileExists( path ) )	// Find the same file in users' home dir first.
242 	FILE *fp;
243 	if( (fp = fopen(path.c_str(), "r")) )
244 	{
245 		fclose(fp);
246 		return path;
247 	}
248 	path = m_DataDirPath;
249 	path += FileName;
250 	return path;
251 }
252 
253 /*
254 string CConfigFile::GetExecPath(const char* exec_name)
255 {
256 	const char* ppaths = getenv("PATH");
257 	if( !ppaths || !*ppaths )
258 		ppaths = "/usr/local/bin:/usr/bin:/bin";
259 	char *dirs = strdup(ppaths);
260 	for( char* dir = strtok(dirs, ":"); dir && *dir; dir=strtok(NULL, ":") )
261 	{
262 		string path(dir);
263 		if( '/' != path[path.length()-1] )
264 			path += '/';
265 		path += exec_name;
266 		if( g_file_test( path.c_str(), G_FILE_TEST_EXISTS )
267 			&& g_file_test( path.c_str(), G_FILE_TEST_IS_EXECUTABLE ) )
268 		{
269 			free(dirs);
270 			return path;
271 		}
272 	}
273 	free(dirs);
274 }
275 */
276