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.
39 ///////////////////////////////////////////////////////////////////////////////
40 // wmailrc file format
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 ///////////////////////////////////////////////////////////////////////////////
69 // list of enum-identifiers and their associated values
70 typedef struct { char *id
; int value
; } enumList_t
;
74 ///////////////////////////////////////////////////////////////////////////////
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
88 NULL
, // use $MAIL environment-variable
109 // enumeration names for ticker mode
110 enumList_t tickerEnum
[] =
112 { "address", TICKER_ADDRESS
},
113 { "familyname", TICKER_FAMILYNAME
},
114 { "nickname", TICKER_NICKNAME
},
118 bool Tokenize( const char *line
, const char **id
, const char **value
)
121 const char *token1
, *token2
;
125 token1
= SkipWhiteSpaces( line
);
127 if(( len
= strlen( token1
)) != 0 && token1
[0] != '#' )
129 token2
= strchr( token1
, '=' );
131 token2
= SkipWhiteSpaces( token2
+1 );
133 if( !IsWhiteSpace( token2
))
146 void AddSenderToSkipList( char *sender
)
149 char **skipName
, **newList
;
151 for( skipName
= config
.skipNames
, numNames
= 0;
152 skipName
!= NULL
&& *skipName
!= NULL
; skipName
++ )
154 if( !strcmp( *skipName
, sender
))
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
);
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
)
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" );
259 for( ; !feof( f
); ++line
)
261 const char *id
, *value
;
264 if( fgets( buf
, 1024, f
) == NULL
)
267 // first eliminate the trailing whitespaces
268 for( len
= strlen( buf
);
269 len
> 0 && IsWhiteSpace(buf
+(--len
)); )
272 if( !Tokenize( buf
, &id
, &value
))
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
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
);
333 WARNING( "cfg-file(%i): unrecognized: \"%s\"\n", line
, buf
);
338 TRACE( "unable to open config-file \"%s\"\n", fileName
);
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) != '\\' )
362 if( trailingQuote
!= NULL
) {
363 // valid string found, copy and translate escape sequences
367 // disposing of "to" is up to the caller...
368 *to
= malloc( trailingQuote
- from
+ 1 );
371 for( c
= from
; c
!= trailingQuote
; ++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;
382 for( i
= 0, value
= 0; i
< 3; ++i
) {
383 if( c
+i
== NULL
|| *(c
+i
) < '0' || *(c
+i
) > '9' )
385 value
= value
* 10 + *(c
+i
) - '0';
388 WARNING( "cfg-file(%i): '\\0' in string or unknown escape sequence found\n", line
);
402 TRACE( "ReadString read \"%s\"\n", *to
);
408 WARNING( "cfg-file(%i): invalid string\n" );
412 bool ReadBool( const char *from
, unsigned int line
, bool *to
)
414 if( !strcasecmp( from
, "on" ) || !strcasecmp( from
, "yes" ) || !strcasecmp( from
, "true" ))
416 else if( !strcasecmp( from
, "off" ) || !strcasecmp( from
, "no" ) || !strcasecmp( from
, "false" ))
419 WARNING( "cfg-file(%i): invalid boolean value: \"%s\"\n", line
, from
);
423 TRACE( "ReadBool read \"%s\"\n", *to
? "True" : "False" );
428 bool ReadInt( const char *from
, unsigned int line
, int *to
)
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
);
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;
447 WARNING( "cfg-file(%i): invalid hex-digit: \"%c\"\n", line
, *from
);
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
);
456 if( *from
>= '0' && *from
<= '9' )
457 value
= value
* 10 + *from
- '0';
459 WARNING( "cfg-file(%i): invalid decimal-digit: \"%c\"\n", line
, *from
);
466 TRACE( "ReadInt read \"%i\"\n", *to
);
471 bool ReadEnum( const char *from
, unsigned int line
, int *to
, const enumList_t
*enumList
)
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
);
484 WARNING( "cfg-file(%i): unknown modifier: \"%s\"\n", line
, from
);
489 bool IsWhiteSpace( const char *chr
)
491 return ( chr
!= NULL
&& ( *chr
== ' ' || *chr
== '\t' || *chr
== '\n' )) ? true : false;
494 const char *SkipWhiteSpaces( const char *str
)
498 for( c
= str
; IsWhiteSpace( c
); ++c
)