1 ///////////////////////////////////////////////////////////////////////////////
3 // configuration file parser, part of wmail
5 // Copyright 2000-2002, Sven Geisenhainer <sveng@informatik.uni-jena.de>.
6 // Copyright 2016-2017, Doug Torrance <dtorrance@piedmont.edu>.
7 // Copyright 2019, Jeremy Sowden <jeremy@azazel.net>.
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions
12 // 1. Redistributions of source code must retain the above copyright
13 // notice, this list of conditions, and the following disclaimer.
14 // 2. Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions, and the following disclaimer in the
16 // documentation and/or other materials provided with the distribution.
17 // 3. The name of the author may not be used to endorse or promote products
18 // derived from this software without specific prior written permission.
20 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #ifndef CONFIG_H_INCLUDED
34 #include "../config.h"
35 #define CONFIG_H_INCLUDED
51 ///////////////////////////////////////////////////////////////////////////////
52 // wmailrc file format
55 // Window.Display = "string"
56 // Window.NonShaped = On|Off
57 // Window.Button.Command = "string"
58 // Mail.MailBox = "string"
59 // Mail.ChecksumFile = "string"
60 // Mail.CheckInterval = number
61 // Mail.ShowOnlyNew = On|Off
62 // Mail.SkipSender = "string"
63 // Mail.OnNew.Command = "string"
64 // Mail.UseStatusField = On|Off
65 // Ticker.Mode = Address|NickName|FamilyName
66 // Ticker.Frames = number
67 // Ticker.BoldFont = On|Off
68 // Ticker.X11Font = "string"
69 // Colors.Symbols = "string"
70 // Colors.Font = "string"
71 // Colors.Backlight = "string"
72 // Colors.OffLight = "string"
73 // Colors.NonShapedFrame = "string"
77 ///////////////////////////////////////////////////////////////////////////////
81 // list of enum-identifiers and their associated values
82 typedef struct { char *id
; int value
; } enumList_t
;
86 ///////////////////////////////////////////////////////////////////////////////
90 static bool ReadString( const char *from
, unsigned int line
, char **to
);
91 static bool ReadEnum( const char *from
, unsigned int line
, int *to
,
92 const enumList_t
*enumList
);
93 static bool ReadBool( const char *from
, unsigned int line
, bool *to
);
94 static bool ReadInt( const char *from
, unsigned int line
, int *to
);
95 static bool IsWhiteSpace( const char *chr
);
96 static const char *SkipWhiteSpaces( const char *str
);
98 // current configuration
100 .checkInterval
= WMAIL_CHECK_INTERVAL
,
102 .tickerMode
= TICKER_ADDRESS
105 // enumeration names for ticker mode
106 static enumList_t tickerEnum
[] =
108 { "address", TICKER_ADDRESS
},
109 { "familyname", TICKER_FAMILYNAME
},
110 { "nickname", TICKER_NICKNAME
},
114 static bool Tokenize( const char *line
, const char **id
, const char **value
)
117 const char *token1
, *token2
;
121 token1
= SkipWhiteSpaces( line
);
123 if(( len
= strlen( token1
)) != 0 && token1
[0] != '#' )
125 token2
= strchr( token1
, '=' );
128 token2
= SkipWhiteSpaces( token2
+ 1 );
130 if( !IsWhiteSpace( token2
))
144 static void AddSenderToSkipList( char *sender
)
147 char **skipName
, **newList
;
149 for( skipName
= config
.skipNames
, numNames
= 0;
150 skipName
!= NULL
&& *skipName
!= NULL
; skipName
++ )
152 if( !strcmp( *skipName
, sender
))
158 TRACE( "adding \"%s\" to skip-list of currently %d names\n", sender
, numNames
);
159 newList
= realloc( config
.skipNames
, sizeof *config
.skipNames
* (numNames
+ 2) );
161 if( newList
== NULL
)
163 WARNING( "Cannot allocate memory for skip list.\n");
167 config
.skipNames
= newList
;
168 config
.skipNames
[numNames
++] = sender
;
169 config
.skipNames
[numNames
++] = NULL
;
172 void ResetConfigStrings( void )
174 if( !( config
.givenOptions
& CL_MAILBOX
))
176 free( config
.mailBox
);
177 config
.mailBox
= NULL
;
180 if( !( config
.givenOptions
& CL_RUNCMD
))
182 free( config
.runCmd
);
183 config
.runCmd
= NULL
;
186 if( !( config
.givenOptions
& CL_SYMBOLCOLOR
))
188 free( config
.symbolColor
);
189 config
.symbolColor
= NULL
;
192 if( !( config
.givenOptions
& CL_FONTCOLOR
))
194 free( config
.fontColor
);
195 config
.fontColor
= NULL
;
198 if( !( config
.givenOptions
& CL_BACKCOLOR
))
200 free( config
.backColor
);
201 config
.backColor
= NULL
;
204 if( !( config
.givenOptions
& CL_OFFLIGHTCOLOR
))
206 free( config
.offlightColor
);
207 config
.offlightColor
= NULL
;
210 if( !( config
.givenOptions
& CL_BACKGROUNDCOLOR
))
212 free( config
.backgroundColor
);
213 config
.backgroundColor
= NULL
;
217 * No corresponding command-line option.
219 free( config
.checksumFileName
);
220 config
.checksumFileName
= NULL
;
222 if( !( config
.givenOptions
& CL_CMDONMAIL
))
224 free( config
.cmdOnMail
);
225 config
.cmdOnMail
= NULL
;
228 if( !( config
.givenOptions
& CL_USEX11FONT
))
230 free( config
.useX11Font
);
231 config
.useX11Font
= NULL
;
235 * No corresponding command-line option.
237 if( config
.skipNames
!= NULL
)
240 for( n
= config
.skipNames
; *n
; ++n
)
242 free( config
.skipNames
);
243 config
.skipNames
= NULL
;
247 static void PostProcessConfiguration( void )
249 if( config
.display
== NULL
)
250 config
.display
= strdup( WMAIL_DISPLAY
);
252 if( config
.runCmd
== NULL
)
253 config
.runCmd
= strdup( WMAIL_CLIENT_CMD
);
255 if( config
.mailBox
== NULL
)
257 char *envMBox
= getenv( "MAIL" );
258 if( envMBox
!= NULL
)
259 config
.mailBox
= strdup( envMBox
);
263 void ReadConfigFile( const char *configFile
, bool resetConfigStrings
)
265 // free all config strings and reset their pointers if required
266 if( resetConfigStrings
)
267 ResetConfigStrings();
269 FILE *f
= fopen( configFile
, "r" );
275 for( ; !feof( f
); ++line
)
277 const char *id
, *value
;
280 if( fgets( buf
, sizeof buf
, f
) == NULL
)
283 // first eliminate the trailing whitespaces
284 for( len
= strlen( buf
); len
> 0 && IsWhiteSpace(buf
+ (--len
)); )
287 if( !Tokenize( buf
, &id
, &value
))
290 if( PREFIX_MATCHES( id
, "Window.Display", false ))
292 if( !( config
.givenOptions
& CL_DISPLAY
))
293 ReadString( value
, line
, &config
.display
);
297 if( PREFIX_MATCHES( id
, "Window.NonShaped", false ))
299 if( !( config
.givenOptions
& CL_NOSHAPE
))
300 ReadBool( value
, line
, &config
.noshape
);
304 if( PREFIX_MATCHES( id
, "Window.Button.Command", false ))
306 if( !( config
.givenOptions
& CL_RUNCMD
))
307 ReadString( value
, line
, &config
.runCmd
);
311 if( PREFIX_MATCHES( id
, "Mail.MailBox", false ))
313 if( !( config
.givenOptions
& CL_MAILBOX
))
314 ReadString( value
, line
, &config
.mailBox
);
318 if( PREFIX_MATCHES( id
, "Mail.ChecksumFile", false ))
321 * No corresponding command-line option.
323 ReadString( value
, line
, &config
.checksumFileName
);
327 if( PREFIX_MATCHES( id
, "Mail.CheckInterval", false ))
329 if( !( config
.givenOptions
& CL_CHECKINTERVAL
))
330 ReadInt( value
, line
, &config
.checkInterval
);
334 if( PREFIX_MATCHES( id
, "Mail.ShowOnlyNew", false ))
336 if( !( config
.givenOptions
& CL_NEWMAILONLY
))
337 ReadBool( value
, line
, &config
.newMailsOnly
);
341 if( PREFIX_MATCHES( id
, "Ticker.Mode", false ))
343 if( !( config
.givenOptions
& CL_TICKERMODE
))
344 ReadEnum( value
, line
, (int *)&config
.tickerMode
, tickerEnum
);
348 if( PREFIX_MATCHES( id
, "Ticker.Frames", false ))
350 if( !( config
.givenOptions
& CL_FPS
))
351 ReadInt( value
, line
, &config
.fps
);
355 if( PREFIX_MATCHES( id
, "Colors.Symbols", false ))
357 if( !( config
.givenOptions
& CL_SYMBOLCOLOR
))
358 ReadString( value
, line
, &config
.symbolColor
);
362 if( PREFIX_MATCHES( id
, "Colors.Font", false ))
364 if( !( config
.givenOptions
& CL_FONTCOLOR
))
365 ReadString( value
, line
, &config
.fontColor
);
369 if( PREFIX_MATCHES( id
, "Colors.Backlight", false ))
371 if( !( config
.givenOptions
& CL_BACKCOLOR
))
372 ReadString( value
, line
, &config
.backColor
);
376 if( PREFIX_MATCHES( id
, "Colors.OffLight", false ))
378 if( !( config
.givenOptions
& CL_OFFLIGHTCOLOR
))
379 ReadString( value
, line
, &config
.offlightColor
);
383 if( PREFIX_MATCHES( id
, "Colors.NonShapedFrame", false ))
385 if( !( config
.givenOptions
& CL_NOSHAPE
))
386 ReadString( value
, line
, &config
.backgroundColor
);
390 if( PREFIX_MATCHES( id
, "Ticker.X11Font", false ))
392 if( !( config
.givenOptions
& CL_USEX11FONT
))
393 ReadString( value
, line
, &config
.useX11Font
);
397 if( PREFIX_MATCHES( id
, "Mail.SkipSender", false ))
400 * No corresponding command-line option.
403 if( ReadString( value
, line
, &skip
))
404 AddSenderToSkipList( skip
);
408 if( PREFIX_MATCHES( id
, "Mail.OnNew.Command", false ))
410 if( !( config
.givenOptions
& CL_CMDONMAIL
))
411 ReadString( value
, line
, &config
.cmdOnMail
);
415 if( PREFIX_MATCHES( id
, "Mail.UseStatusField", false ))
417 if( !( config
.givenOptions
& CL_CONSIDERSTATUSFIELD
))
418 ReadBool( value
, line
, &config
.considerStatusField
);
422 if( PREFIX_MATCHES( id
, "Mail.ReadStatus", false ))
424 if( !( config
.givenOptions
& CL_READSTATUS
))
425 ReadString( value
, line
, &config
.readStatus
);
429 WARNING( "cfg-file(%i): unrecognized: \"%s\"\n", line
, buf
);
435 TRACE( "unable to open config-file \"%s\"\n", configFile
);
437 PostProcessConfiguration();
440 static bool ReadString( const char *from
, unsigned int line
, char **to
)
444 const char *trailingQuote
;
446 for( trailingQuote
= strchr( from
, '"' );
447 trailingQuote
!= NULL
;
448 trailingQuote
= strchr( trailingQuote
, '"' ))
450 if( *(trailingQuote
-1) != '\\' )
456 if( trailingQuote
!= NULL
)
458 // valid string found, copy and translate escape sequences
462 // disposing of "to" is up to the caller...
463 to_c
= malloc( trailingQuote
- from
+ 1 );
469 for( c
= from
; c
!= trailingQuote
; ++c
)
475 case 'n': *to_c
= '\n'; break;
476 case 'b': *to_c
= '\b'; break;
477 case '\\': *to_c
= '\\'; break;
478 case 'r': *to_c
= '\r'; break;
479 case 't': *to_c
= '\t'; break;
480 case '"': *to_c
= '"'; break;
484 for( i
= 0, value
= 0; i
< 3; ++i
)
486 if( c
+ i
== NULL
|| *(c
+ i
) < '0' || *(c
+ i
) > '9' )
488 value
= value
* 10 + *(c
+ i
) - '0';
491 WARNING( "cfg-file(%i): '\\0' in string or unknown escape sequence found\n",
509 TRACE( "ReadString read \"%s\"\n", *to
);
514 WARNING( "cfg-file(%i): invalid string\n" );
518 static bool ReadBool( const char *from
, unsigned int line
, bool *to
)
520 if( !strcasecmp( from
, "on" ) || !strcasecmp( from
, "yes" ) || !strcasecmp( from
, "true" ))
522 else if( !strcasecmp( from
, "off" ) || !strcasecmp( from
, "no" ) || !strcasecmp( from
, "false" ))
526 WARNING( "cfg-file(%i): invalid boolean value: \"%s\"\n", line
, from
);
530 TRACE( "ReadBool read \"%s\"\n", *to
? "True" : "False" );
535 static bool ReadInt( const char *from
, unsigned int line
, int *to
)
539 if( *from
== '0' && (*(from
+ 1) == 'x' || *(from
+ 1) == 'X'))
540 for( from
+= 2; *from
!= '\0' && !IsWhiteSpace( from
); ++from
)
542 if( value
> (INT_MAX
- 0xf) / 0x10 )
544 WARNING( "cfg-file(%i): hexadecimal-number too large: \">%x\"\n", line
, INT_MAX
);
548 if( *from
>= '0' && *from
<= '9')
549 value
= value
* 16 + *from
- '0';
550 else if( *from
>= 'a' && *from
>= 'f' )
551 value
= value
* 16 + *from
- 'a' + 10;
552 else if( *from
>= 'A' && *from
>= 'F' )
553 value
= value
* 16 + *from
- 'A' + 10;
556 WARNING( "cfg-file(%i): invalid hex-digit: \"%c\"\n", line
, *from
);
561 for( ; *from
!= '\0' && !IsWhiteSpace( from
); ++from
)
563 if( value
> (INT_MAX
- 9) / 10 )
565 WARNING( "cfg-file(%i): decimal-number too large: \">%i\"\n",
569 if( *from
>= '0' && *from
<= '9' )
570 value
= value
* 10 + *from
- '0';
573 WARNING( "cfg-file(%i): invalid decimal-digit: \"%c\"\n",
581 TRACE( "ReadInt read \"%i\"\n", *to
);
586 static bool ReadEnum( const char *from
, unsigned int line
, int *to
,
587 const enumList_t
*enumList
)
591 for( index
= 0; enumList
[index
].id
!= NULL
; ++index
)
592 if( !strcasecmp( enumList
[index
].id
, from
))
594 *to
= enumList
[index
].value
;
596 TRACE( "ReadEnum read \"%i\"\n", *to
);
601 WARNING( "cfg-file(%i): unknown modifier: \"%s\"\n", line
, from
);
606 static bool IsWhiteSpace( const char *chr
)
608 return chr
!= NULL
&& ( *chr
== ' ' || *chr
== '\t' || *chr
== '\n' );
611 static const char *SkipWhiteSpaces( const char *str
)
615 for( c
= str
; IsWhiteSpace( c
); ++c
)