1 ///////////////////////////////////////////////////////////////////////////////
3 // configuration file parser, part of wmail
5 // Copyright 2000~2002, Sven Geisenhainer <sveng@informatik.uni-jena.de>.
6 // All rights reserved.
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions
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.
32 #ifndef CONFIG_H_INCLUDED
33 #include "../config.h"
34 #define CONFIG_H_INCLUDED
47 ///////////////////////////////////////////////////////////////////////////////
48 // wmailrc file format
51 // Window.Display = "string"
52 // Window.NonShaped = On|Off
53 // Window.Button.Command = "string"
54 // Mail.MailBox = "string"
55 // Mail.ChecksumFile = "string"
56 // Mail.CheckIntervall = number
57 // Mail.ShowOnlyNew = On|Off
58 // Mail.SkipSender = "string"
59 // Mail.OnNew.Command = "string"
60 // Mail.UseStatusField = On|Off
61 // Ticker.Mode = Address|NickName|FamilyName
62 // Ticker.Frames = number
63 // Ticker.BoldFont = On|Off
64 // Ticker.X11Font = "string"
65 // Colors.Symbols = "string"
66 // Colors.Font = "string"
67 // Colors.Backlight = "string"
68 // Colors.OffLight = "string"
69 // Colors.NonShapedFrame = "string"
73 ///////////////////////////////////////////////////////////////////////////////
77 // list of enum-identifiers and their associated values
78 typedef struct { char *id
; int value
; } enumList_t
;
82 ///////////////////////////////////////////////////////////////////////////////
86 static bool ReadString( const char *from
, unsigned int line
, char **to
);
87 static bool ReadEnum( const char *from
, unsigned int line
, int *to
,
88 const enumList_t
*enumList
);
89 static bool ReadBool( const char *from
, unsigned int line
, bool *to
);
90 static bool ReadInt( const char *from
, unsigned int line
, int *to
);
91 static bool IsWhiteSpace( const char *chr
);
92 static const char *SkipWhiteSpaces( const char *str
);
94 // current configuration
97 NULL
, // use $MAIL environment-variable
105 WMAIL_CHECK_INTERVAL
,
118 // enumeration names for ticker mode
119 static enumList_t tickerEnum
[] =
121 { "address", TICKER_ADDRESS
},
122 { "familyname", TICKER_FAMILYNAME
},
123 { "nickname", TICKER_NICKNAME
},
127 static bool Tokenize( const char *line
, const char **id
, const char **value
)
130 const char *token1
, *token2
;
134 token1
= SkipWhiteSpaces( line
);
136 if(( len
= strlen( token1
)) != 0 && token1
[0] != '#' )
138 token2
= strchr( token1
, '=' );
140 token2
= SkipWhiteSpaces( token2
+1 );
142 if( !IsWhiteSpace( token2
))
155 static void AddSenderToSkipList( char *sender
)
158 char **skipName
, **newList
;
160 for( skipName
= config
.skipNames
, numNames
= 0;
161 skipName
!= NULL
&& *skipName
!= NULL
; skipName
++ )
163 if( !strcmp( *skipName
, sender
))
169 TRACE( "adding \"%s\" to skip-list of currently %d names\n", sender
, numNames
);
170 newList
= malloc( sizeof(char *) * (numNames
+ 2) );
172 for( i
= 0; i
< numNames
; ++i
)
173 newList
[i
] = config
.skipNames
[i
];
175 newList
[i
] = strdup( sender
);
177 free( config
.skipNames
);
178 config
.skipNames
= newList
;
181 void ResetConfigStrings( void )
183 if( !( config
.givenOptions
& CL_MAILBOX
)) {
184 free( config
.mailBox
);
185 config
.mailBox
= NULL
;
188 if( !( config
.givenOptions
& CL_RUNCMD
)) {
189 free( config
.runCmd
);
190 config
.runCmd
= NULL
;
193 if( !( config
.givenOptions
& CL_SYMBOLCOLOR
)) {
194 free( config
.symbolColor
);
195 config
.symbolColor
= NULL
;
198 if( !( config
.givenOptions
& CL_FONTCOLOR
)) {
199 free( config
.fontColor
);
200 config
.fontColor
= NULL
;
203 if( !( config
.givenOptions
& CL_BACKCOLOR
)) {
204 free( config
.backColor
);
205 config
.backColor
= NULL
;
208 if( !( config
.givenOptions
& CL_OFFLIGHTCOLOR
)) {
209 free( config
.offlightColor
);
210 config
.offlightColor
= NULL
;
213 if( !( config
.givenOptions
& CL_BACKGROUNDCOLOR
)) {
214 free( config
.backgroundColor
);
215 config
.backgroundColor
= NULL
;
218 if( !( config
.givenOptions
& CL_CHECKSUMFILENAME
)) {
219 free( config
.checksumFileName
);
220 config
.checksumFileName
= NULL
;
223 if( !( config
.givenOptions
& CL_CMDONMAIL
)) {
224 free( config
.cmdOnMail
);
225 config
.cmdOnMail
= NULL
;
228 if( !( config
.givenOptions
& CL_USEX11FONT
)) {
229 free( config
.useX11Font
);
230 config
.useX11Font
= NULL
;
234 static void PostProcessConfiguration( void )
236 if( config
.display
== NULL
)
237 config
.display
= strdup( WMAIL_DISPLAY
);
239 if( config
.runCmd
== NULL
)
240 config
.runCmd
= strdup( WMAIL_CLIENT_CMD
);
242 if( config
.mailBox
== NULL
)
244 char *envMBox
= getenv( "MAIL" );
245 if( envMBox
!= NULL
)
246 config
.mailBox
= strdup( envMBox
);
250 void ReadConfigFile( bool resetConfigStrings
)
254 // free all config strings and reset their pointers if required
255 if( resetConfigStrings
)
256 ResetConfigStrings();
258 if(( usersHome
= getenv( "HOME" )) != NULL
)
261 if(( fileName
= MakePathName( usersHome
, WMAIL_RC_FILE
)) != NULL
)
263 FILE *f
= fopen( fileName
, "rt" );
270 for( ; !feof( f
); ++line
)
272 const char *id
, *value
;
275 if( fgets( buf
, 1024, f
) == NULL
)
278 // first eliminate the trailing whitespaces
279 for( len
= strlen( buf
);
280 len
> 0 && IsWhiteSpace(buf
+(--len
)); )
283 if( !Tokenize( buf
, &id
, &value
))
286 if( !strncasecmp( id
, "Window.Display", 14 )) {
287 if( !( config
.givenOptions
& CL_DISPLAY
))
288 ReadString( value
, line
, &config
.display
);
289 } else if( !strncasecmp( id
, "Window.NonShaped", 16 )) {
290 if( !( config
.givenOptions
& CL_NOSHAPE
))
291 ReadBool( value
, line
, &config
.noshape
);
292 } else if( !strncasecmp( id
, "Window.Button.Command", 21 )) {
293 if( !( config
.givenOptions
& CL_RUNCMD
))
294 ReadString( value
, line
, &config
.runCmd
);
295 } else if( !strncasecmp( id
, "Mail.MailBox", 12 )) {
296 if( !( config
.givenOptions
& CL_MAILBOX
))
297 ReadString( value
, line
, &config
.mailBox
);
298 } else if( !strncasecmp( id
, "Mail.ChecksumFile", 17 )) // no corresponding cmdline option
299 ReadString( value
, line
, &config
.checksumFileName
);
300 else if( !strncasecmp( id
, "Mail.CheckIntervall", 19 )) {
301 if( !( config
.givenOptions
& CL_CHECKINTERVAL
))
302 ReadInt( value
, line
, &config
.checkInterval
);
303 } else if( !strncasecmp( id
, "Mail.ShowOnlyNew", 16 )) {
304 if( !( config
.givenOptions
& CL_NEWMAILONLY
))
305 ReadBool( value
, line
, &config
.newMailsOnly
);
306 } else if( !strncasecmp( id
, "Ticker.Mode", 11 )) {
307 if( !( config
.givenOptions
& CL_TICKERMODE
))
308 ReadEnum( value
, line
, (int *)&config
.tickerMode
, tickerEnum
);
309 } else if( !strncasecmp( id
, "Ticker.Frames", 13 )) {
310 if( !( config
.givenOptions
& CL_FPS
))
311 ReadInt( value
, line
, &config
.fps
);
312 } else if( !strncasecmp( id
, "Colors.Symbols", 14 )) {
313 if( !( config
.givenOptions
& CL_SYMBOLCOLOR
))
314 ReadString( value
, line
, &config
.symbolColor
);
315 } else if( !strncasecmp( id
, "Colors.Font", 11 )) {
316 if( !( config
.givenOptions
& CL_FONTCOLOR
))
317 ReadString( value
, line
, &config
.fontColor
);
318 } else if( !strncasecmp( id
, "Colors.Backlight", 16 )) {
319 if( !( config
.givenOptions
& CL_BACKCOLOR
))
320 ReadString( value
, line
, &config
.backColor
);
321 } else if( !strncasecmp( id
, "Colors.OffLight", 15 )) {
322 if( !( config
.givenOptions
& CL_OFFLIGHTCOLOR
))
323 ReadString( value
, line
, &config
.offlightColor
);
324 } else if( !strncasecmp( id
, "Colors.NonShapedFrame", 21 )) {
325 if( !( config
.givenOptions
& CL_NOSHAPE
))
326 ReadString( value
, line
, &config
.backgroundColor
);
327 } else if( !strncasecmp( id
, "Ticker.X11Font", 14 )) {
328 if( !( config
.givenOptions
& CL_USEX11FONT
))
329 ReadString( value
, line
, &config
.useX11Font
);
330 } else if( !strncasecmp( id
, "Mail.SkipSender", 15 )) { // no corresponding cmdline options
332 if( ReadString( value
, line
, &skip
))
333 AddSenderToSkipList( skip
);
334 } else if( !strncasecmp( id
, "Mail.OnNew.Command", 18 )) {
335 if( !( config
.givenOptions
& CL_CMDONMAIL
))
336 ReadString( value
, line
, &config
.cmdOnMail
);
337 } else if( !strncasecmp( id
, "Mail.UseStatusField", 19 )) {
338 if( !( config
.givenOptions
& CL_CONSIDERSTATUSFIELD
))
339 ReadBool( value
, line
, &config
.considerStatusField
);
340 } else if( !strncasecmp( id
, "Mail.ReadStatus", 15 )) {
341 if( !( config
.givenOptions
& CL_READSTATUS
))
342 ReadString( value
, line
, &config
.readStatus
);
344 WARNING( "cfg-file(%i): unrecognized: \"%s\"\n", line
, buf
);
349 TRACE( "unable to open config-file \"%s\"\n", fileName
);
354 TRACE( "unable to allocate config-file\n" );
357 TRACE( "no $HOME defined - config-file not read\n" );
360 PostProcessConfiguration();
363 static bool ReadString( const char *from
, unsigned int line
, char **to
)
365 if( *from
++ == '"' ) {
366 const char *trailingQuote
;
368 for( trailingQuote
= strchr( from
, '"' );
369 trailingQuote
!= NULL
;
370 trailingQuote
= strchr( trailingQuote
, '"' ))
372 if( *(trailingQuote
-1) != '\\' )
378 if( trailingQuote
!= NULL
) {
379 // valid string found, copy and translate escape sequences
383 // disposing of "to" is up to the caller...
384 *to
= malloc( trailingQuote
- from
+ 1 );
387 for( c
= from
; c
!= trailingQuote
; ++c
) {
390 case 'n': *to_c
= '\n'; break;
391 case 'b': *to_c
= '\b'; break;
392 case '\\': *to_c
= '\\'; break;
393 case 'r': *to_c
= '\r'; break;
394 case 't': *to_c
= '\t'; break;
395 case '"': *to_c
= '"'; break;
398 for( i
= 0, value
= 0; i
< 3; ++i
) {
399 if( c
+i
== NULL
|| *(c
+i
) < '0' || *(c
+i
) > '9' )
401 value
= value
* 10 + *(c
+i
) - '0';
404 WARNING( "cfg-file(%i): '\\0' in string or unknown escape sequence found\n", line
);
418 TRACE( "ReadString read \"%s\"\n", *to
);
424 WARNING( "cfg-file(%i): invalid string\n" );
428 static bool ReadBool( const char *from
, unsigned int line
, bool *to
)
430 if( !strcasecmp( from
, "on" ) || !strcasecmp( from
, "yes" ) || !strcasecmp( from
, "true" ))
432 else if( !strcasecmp( from
, "off" ) || !strcasecmp( from
, "no" ) || !strcasecmp( from
, "false" ))
435 WARNING( "cfg-file(%i): invalid boolean value: \"%s\"\n", line
, from
);
439 TRACE( "ReadBool read \"%s\"\n", *to
? "True" : "False" );
444 static bool ReadInt( const char *from
, unsigned int line
, int *to
)
448 if( *from
== '0' && (*(from
+1) == 'x' || *(from
+1) == 'X') ) {
449 for( from
+= 2; *from
!= '\0' && !IsWhiteSpace( from
); ++from
)
451 if( value
> (INT_MAX
- 0xf) / 0x10 ) {
452 WARNING( "cfg-file(%i): hexadecimal-number too large: \">%x\"\n", line
, INT_MAX
);
456 if( *from
>= '0' && *from
<= '9')
457 value
= value
* 16 + *from
- '0';
458 else if( *from
>= 'a' && *from
>= 'f' )
459 value
= value
* 16 + *from
- 'a' + 10;
460 else if( *from
>= 'A' && *from
>= 'F' )
461 value
= value
* 16 + *from
- 'A' + 10;
463 WARNING( "cfg-file(%i): invalid hex-digit: \"%c\"\n", line
, *from
);
467 } else for( ; *from
!= '\0' && !IsWhiteSpace( from
); ++from
) {
468 if( value
> (INT_MAX
- 9) / 10 ) {
469 WARNING( "cfg-file(%i): decimal-number too large: \">%i\"\n", line
, INT_MAX
);
472 if( *from
>= '0' && *from
<= '9' )
473 value
= value
* 10 + *from
- '0';
475 WARNING( "cfg-file(%i): invalid decimal-digit: \"%c\"\n", line
, *from
);
482 TRACE( "ReadInt read \"%i\"\n", *to
);
487 static bool ReadEnum( const char *from
, unsigned int line
, int *to
,
488 const enumList_t
*enumList
)
492 for( index
= 0; enumList
[index
].id
!= NULL
; ++index
)
493 if( !strcasecmp( enumList
[index
].id
, from
)) {
494 *to
= enumList
[index
].value
;
496 TRACE( "ReadEnum read \"%i\"\n", *to
);
501 WARNING( "cfg-file(%i): unknown modifier: \"%s\"\n", line
, from
);
506 static bool IsWhiteSpace( const char *chr
)
508 return ( chr
!= NULL
&& ( *chr
== ' ' || *chr
== '\t' || *chr
== '\n' )) ? true : false;
511 static const char *SkipWhiteSpaces( const char *str
)
515 for( c
= str
; IsWhiteSpace( c
); ++c
)