wmpop3: Use wmgeneral from libdockapp.
[dockapps.git] / wmail / src / config.c
blob80df0f27b86d51eae86431606828a4f3dce17375
1 ///////////////////////////////////////////////////////////////////////////////
2 // config.c
3 // configuration file parser, part of wmail
4 //
5 // Copyright 2000~2002, Sven Geisenhainer <sveng@informatik.uni-jena.de>.
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions
10 // are met:
11 // 1. Redistributions of source code must retain the above copyright
12 // notice, this list of conditions, and the following disclaimer.
13 // 2. Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions, and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 // 3. The name of the author may not be used to endorse or promote products
17 // derived from this software without specific prior written permission.
19 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <limits.h>
35 #include "common.h"
36 #include "config.h"
39 ///////////////////////////////////////////////////////////////////////////////
40 // wmailrc file format
42 // # - comment-lines
43 // Window.Display = "string"
44 // Window.NonShaped = On|Off
45 // Window.Button.Command = "string"
46 // Mail.MailBox = "string"
47 // Mail.ChecksumFile = "string"
48 // Mail.CheckIntervall = number
49 // Mail.ShowOnlyNew = On|Off
50 // Mail.SkipSender = "string"
51 // Mail.OnNew.Command = "string"
52 // Mail.UseStatusField = On|Off
53 // Ticker.Mode = Address|NickName|FamilyName
54 // Ticker.Frames = number
55 // Ticker.BoldFont = On|Off
56 // Ticker.X11Font = "string"
57 // Colors.Symbols = "string"
58 // Colors.Font = "string"
59 // Colors.Backlight = "string"
60 // Colors.OffLight = "string"
61 // Colors.NonShapedFrame = "string"
65 ///////////////////////////////////////////////////////////////////////////////
66 // typedefs
69 // list of enum-identifiers and their associated values
70 typedef struct { char *id; int value; } enumList_t;
74 ///////////////////////////////////////////////////////////////////////////////
75 // local prototypes
78 bool ReadString( const char *from, unsigned int line, char **to );
79 bool ReadEnum( const char *from, unsigned int line, int *to, const enumList_t *enumList );
80 bool ReadBool( const char *from, unsigned int line, bool *to );
81 bool ReadInt( const char *from, unsigned int line, int *to );
82 bool IsWhiteSpace( const char *chr );
83 const char *SkipWhiteSpaces( const char *str );
85 // current configuration
86 config_t config = {
87 NULL,
88 NULL, // use $MAIL environment-variable
89 NULL,
90 NULL,
91 NULL,
92 NULL,
93 NULL,
94 NULL,
95 NULL,
96 WMAIL_CHECK_INTERVAL,
97 WMAIL_FPS,
98 false,
99 false,
100 TICKER_ADDRESS,
101 NULL,
102 NULL,
103 false,
104 NULL,
109 // enumeration names for ticker mode
110 enumList_t tickerEnum[] =
112 { "address", TICKER_ADDRESS },
113 { "familyname", TICKER_FAMILYNAME },
114 { "nickname", TICKER_NICKNAME },
115 { NULL, 0 }
118 bool Tokenize( const char *line, const char **id, const char **value )
120 int len;
121 const char *token1, *token2;
123 if( line != NULL )
125 token1 = SkipWhiteSpaces( line );
127 if(( len = strlen( token1 )) != 0 && token1[0] != '#' )
129 token2 = strchr( token1, '=' );
130 if( token2 != NULL )
131 token2 = SkipWhiteSpaces( token2+1 );
133 if( !IsWhiteSpace( token2 ))
135 *id = token1;
136 *value = token2;
138 return true;
143 return false;
146 void AddSenderToSkipList( char *sender )
148 int numNames, i;
149 char **skipName, **newList;
151 for( skipName = config.skipNames, numNames = 0;
152 skipName != NULL && *skipName != NULL; skipName++ )
154 if( !strcmp( *skipName, sender ))
155 return;
157 numNames++;
160 TRACE( "adding \"%s\" to skip-list of currently %d names\n", sender, numNames );
161 newList = malloc( sizeof(char *) * (numNames + 2) );
163 for( i = 0; i < numNames; ++i )
164 newList[i] = config.skipNames[i];
166 newList[i] = strdup( sender );
167 newList[i+1] = NULL;
168 free( config.skipNames );
169 config.skipNames = newList;
172 void ResetConfigStrings()
174 if( !( config.givenOptions & CL_MAILBOX )) {
175 free( config.mailBox );
176 config.mailBox = NULL;
179 if( !( config.givenOptions & CL_RUNCMD )) {
180 free( config.runCmd );
181 config.runCmd = NULL;
184 if( !( config.givenOptions & CL_SYMBOLCOLOR )) {
185 free( config.symbolColor );
186 config.symbolColor = NULL;
189 if( !( config.givenOptions & CL_FONTCOLOR )) {
190 free( config.fontColor );
191 config.fontColor = NULL;
194 if( !( config.givenOptions & CL_BACKCOLOR )) {
195 free( config.backColor );
196 config.backColor = NULL;
199 if( !( config.givenOptions & CL_OFFLIGHTCOLOR )) {
200 free( config.offlightColor );
201 config.offlightColor = NULL;
204 if( !( config.givenOptions & CL_BACKGROUNDCOLOR )) {
205 free( config.backgroundColor );
206 config.backgroundColor = NULL;
209 if( !( config.givenOptions & CL_CHECKSUMFILENAME )) {
210 free( config.checksumFileName );
211 config.checksumFileName = NULL;
214 if( !( config.givenOptions & CL_CMDONMAIL )) {
215 free( config.cmdOnMail );
216 config.cmdOnMail = NULL;
219 if( !( config.givenOptions & CL_USEX11FONT )) {
220 free( config.useX11Font );
221 config.useX11Font = NULL;
225 void PostProcessConfiguration()
227 if( config.display == NULL )
228 config.display = strdup( WMAIL_DISPLAY );
230 if( config.runCmd == NULL )
231 config.runCmd = strdup( WMAIL_CLIENT_CMD );
233 if( config.mailBox == NULL )
235 char *envMBox = getenv( "MAIL" );
236 if( envMBox != NULL )
237 config.mailBox = strdup( envMBox );
241 void ReadConfigFile( bool resetConfigStrings )
243 char *usersHome;
245 // free all config strings and reset their pointers if required
246 if( resetConfigStrings )
247 ResetConfigStrings();
249 if(( usersHome = getenv( "HOME" )) != NULL )
251 char *fileName = MakePathName( usersHome, WMAIL_RC_FILE );
252 FILE *f = fopen( fileName, "rt" );
254 if( f != NULL )
256 char buf[1024];
257 int line = 1;
259 for( ; !feof( f ); ++line )
261 const char *id, *value;
262 unsigned int len;
264 if( fgets( buf, 1024, f ) == NULL )
265 break;
267 // first eliminate the trailing whitespaces
268 for( len = strlen( buf );
269 len > 0 && IsWhiteSpace(buf+(--len)); )
270 *(buf+len) = '\0';
272 if( !Tokenize( buf, &id, &value ))
273 continue;
275 if( !strncasecmp( id, "Window.Display", 14 )) {
276 if( !( config.givenOptions & CL_DISPLAY ))
277 ReadString( value, line, &config.display );
278 } else if( !strncasecmp( id, "Window.NonShaped", 16 )) {
279 if( !( config.givenOptions & CL_NOSHAPE ))
280 ReadBool( value, line, &config.noshape );
281 } else if( !strncasecmp( id, "Window.Button.Command", 21 )) {
282 if( !( config.givenOptions & CL_RUNCMD ))
283 ReadString( value, line, &config.runCmd );
284 } else if( !strncasecmp( id, "Mail.MailBox", 12 )) {
285 if( !( config.givenOptions & CL_MAILBOX ))
286 ReadString( value, line, &config.mailBox );
287 } else if( !strncasecmp( id, "Mail.ChecksumFile", 17 )) // no corresponding cmdline option
288 ReadString( value, line, &config.checksumFileName );
289 else if( !strncasecmp( id, "Mail.CheckIntervall", 19 )) {
290 if( !( config.givenOptions & CL_CHECKINTERVAL ))
291 ReadInt( value, line, &config.checkInterval );
292 } else if( !strncasecmp( id, "Mail.ShowOnlyNew", 16 )) {
293 if( !( config.givenOptions & CL_NEWMAILONLY ))
294 ReadBool( value, line, &config.newMailsOnly );
295 } else if( !strncasecmp( id, "Ticker.Mode", 11 )) {
296 if( !( config.givenOptions & CL_TICKERMODE ))
297 ReadEnum( value, line, (int *)&config.tickerMode, tickerEnum );
298 } else if( !strncasecmp( id, "Ticker.Frames", 13 )) {
299 if( !( config.givenOptions & CL_FPS ))
300 ReadInt( value, line, &config.fps );
301 } else if( !strncasecmp( id, "Colors.Symbols", 14 )) {
302 if( !( config.givenOptions & CL_SYMBOLCOLOR ))
303 ReadString( value, line, &config.symbolColor );
304 } else if( !strncasecmp( id, "Colors.Font", 11 )) {
305 if( !( config.givenOptions & CL_FONTCOLOR ))
306 ReadString( value, line, &config.fontColor );
307 } else if( !strncasecmp( id, "Colors.Backlight", 16 )) {
308 if( !( config.givenOptions & CL_BACKCOLOR ))
309 ReadString( value, line, &config.backColor );
310 } else if( !strncasecmp( id, "Colors.OffLight", 15 )) {
311 if( !( config.givenOptions & CL_OFFLIGHTCOLOR ))
312 ReadString( value, line, &config.offlightColor );
313 } else if( !strncasecmp( id, "Colors.NonShapedFrame", 21 )) {
314 if( !( config.givenOptions & CL_NOSHAPE ))
315 ReadString( value, line, &config.backgroundColor );
316 } else if( !strncasecmp( id, "Ticker.X11Font", 14 )) {
317 if( !( config.givenOptions & CL_USEX11FONT ))
318 ReadString( value, line, &config.useX11Font );
319 } else if( !strncasecmp( id, "Mail.SkipSender", 15 )) { // no corresponding cmdline options
320 char *skip;
321 if( ReadString( value, line, &skip ))
322 AddSenderToSkipList( skip );
323 } else if( !strncasecmp( id, "Mail.OnNew.Command", 18 )) {
324 if( !( config.givenOptions & CL_CMDONMAIL ))
325 ReadString( value, line, &config.cmdOnMail );
326 } else if( !strncasecmp( id, "Mail.UseStatusField", 19 )) {
327 if( !( config.givenOptions & CL_CONSIDERSTATUSFIELD ))
328 ReadBool( value, line, &config.considerStatusField );
329 } else if( !strncasecmp( id, "Mail.ReadStatus", 15 )) {
330 if( !( config.givenOptions & CL_READSTATUS ))
331 ReadString( value, line, &config.readStatus );
332 } else
333 WARNING( "cfg-file(%i): unrecognized: \"%s\"\n", line, buf );
336 fclose( f );
337 } else {
338 TRACE( "unable to open config-file \"%s\"\n", fileName );
340 } else {
341 TRACE( "no $HOME defined - config-file not read\n" );
344 PostProcessConfiguration();
347 bool ReadString( const char *from, unsigned int line, char **to )
349 if( *from++ == '"' ) {
350 const char *trailingQuote;
352 for( trailingQuote = strchr( from, '"' );
353 trailingQuote != NULL;
354 trailingQuote = strchr( trailingQuote, '"' ))
356 if( *(trailingQuote-1) != '\\' )
357 break;
359 ++trailingQuote;
362 if( trailingQuote != NULL ) {
363 // valid string found, copy and translate escape sequences
364 const char *c;
365 char *to_c;
367 // disposing of "to" is up to the caller...
368 *to = malloc( trailingQuote - from + 1 );
369 to_c = *to;
371 for( c = from; c != trailingQuote; ++c ) {
372 if( *c == '\\' ) {
373 switch( *(++c) ) {
374 case 'n': *to_c = '\n'; break;
375 case 'b': *to_c = '\b'; break;
376 case '\\': *to_c = '\\'; break;
377 case 'r': *to_c = '\r'; break;
378 case 't': *to_c = '\t'; break;
379 case '"': *to_c = '"'; break;
380 default: {
381 int value, i;
382 for( i = 0, value = 0; i < 3; ++i ) {
383 if( c+i == NULL || *(c+i) < '0' || *(c+i) > '9' )
384 break;
385 value = value * 10 + *(c+i) - '0';
387 if( value == 0 )
388 WARNING( "cfg-file(%i): '\\0' in string or unknown escape sequence found\n", line );
389 else {
390 *to_c = (char)value;
391 c += i-1;
395 } else
396 *to_c = *c;
398 ++to_c;
401 *to_c = '\0';
402 TRACE( "ReadString read \"%s\"\n", *to );
404 return true;
408 WARNING( "cfg-file(%i): invalid string\n" );
409 return false;
412 bool ReadBool( const char *from, unsigned int line, bool *to )
414 if( !strcasecmp( from, "on" ) || !strcasecmp( from, "yes" ) || !strcasecmp( from, "true" ))
415 *to = true;
416 else if( !strcasecmp( from, "off" ) || !strcasecmp( from, "no" ) || !strcasecmp( from, "false" ))
417 *to = false;
418 else {
419 WARNING( "cfg-file(%i): invalid boolean value: \"%s\"\n", line, from );
420 return false;
423 TRACE( "ReadBool read \"%s\"\n", *to ? "True" : "False" );
425 return true;
428 bool ReadInt( const char *from, unsigned int line, int *to )
430 int value = 0;
432 if( *from == '0' && (*(from+1) == 'x' || *(from+1) == 'X') ) {
433 for( from += 2; *from != '\0' && !IsWhiteSpace( from ); ++from )
435 if( value > (INT_MAX - 0xf) / 0x10 ) {
436 WARNING( "cfg-file(%i): hexadecimal-number too large: \">%x\"\n", line, INT_MAX );
437 return false;
440 if( *from >= '0' && *from <= '9')
441 value = value * 16 + *from - '0';
442 else if( *from >= 'a' && *from >= 'f' )
443 value = value * 16 + *from - 'a' + 10;
444 else if( *from >= 'A' && *from >= 'F' )
445 value = value * 16 + *from - 'A' + 10;
446 else {
447 WARNING( "cfg-file(%i): invalid hex-digit: \"%c\"\n", line, *from );
448 return false;
451 } else for( ; *from != '\0' && !IsWhiteSpace( from ); ++from ) {
452 if( value > (INT_MAX - 9) / 10 ) {
453 WARNING( "cfg-file(%i): decimal-number too large: \">%i\"\n", line, INT_MAX );
454 return false;
456 if( *from >= '0' && *from <= '9' )
457 value = value * 10 + *from - '0';
458 else {
459 WARNING( "cfg-file(%i): invalid decimal-digit: \"%c\"\n", line, *from );
460 return false;
464 *to = value;
466 TRACE( "ReadInt read \"%i\"\n", *to );
468 return true;
471 bool ReadEnum( const char *from, unsigned int line, int *to, const enumList_t *enumList )
473 int index;
475 for( index = 0; enumList[index].id != NULL; ++index )
476 if( !strcasecmp( enumList[index].id, from )) {
477 *to = enumList[index].value;
479 TRACE( "ReadEnum read \"%i\"\n", *to );
481 return true;
484 WARNING( "cfg-file(%i): unknown modifier: \"%s\"\n", line, from );
486 return false;
489 bool IsWhiteSpace( const char *chr )
491 return ( chr != NULL && ( *chr == ' ' || *chr == '\t' || *chr == '\n' )) ? true : false;
494 const char *SkipWhiteSpaces( const char *str )
496 const char *c;
498 for( c = str; IsWhiteSpace( c ); ++c )
501 return c;