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
, '=' );
127 token2
= SkipWhiteSpaces( token2
+1 );
129 if( !IsWhiteSpace( token2
))
142 static void AddSenderToSkipList( char *sender
)
145 char **skipName
, **newList
;
147 for( skipName
= config
.skipNames
, numNames
= 0;
148 skipName
!= NULL
&& *skipName
!= NULL
; skipName
++ )
150 if( !strcmp( *skipName
, sender
))
156 TRACE( "adding \"%s\" to skip-list of currently %d names\n", sender
, numNames
);
157 newList
= realloc( config
.skipNames
, sizeof *config
.skipNames
* (numNames
+ 2) );
159 if( newList
== NULL
)
161 WARNING( "Cannot allocate memory for skip list.\n");
165 config
.skipNames
= newList
;
166 config
.skipNames
[numNames
++] = sender
;
167 config
.skipNames
[numNames
++] = NULL
;
170 void ResetConfigStrings( void )
172 if( !( config
.givenOptions
& CL_MAILBOX
)) {
173 free( config
.mailBox
);
174 config
.mailBox
= NULL
;
177 if( !( config
.givenOptions
& CL_RUNCMD
)) {
178 free( config
.runCmd
);
179 config
.runCmd
= NULL
;
182 if( !( config
.givenOptions
& CL_SYMBOLCOLOR
)) {
183 free( config
.symbolColor
);
184 config
.symbolColor
= NULL
;
187 if( !( config
.givenOptions
& CL_FONTCOLOR
)) {
188 free( config
.fontColor
);
189 config
.fontColor
= NULL
;
192 if( !( config
.givenOptions
& CL_BACKCOLOR
)) {
193 free( config
.backColor
);
194 config
.backColor
= NULL
;
197 if( !( config
.givenOptions
& CL_OFFLIGHTCOLOR
)) {
198 free( config
.offlightColor
);
199 config
.offlightColor
= NULL
;
202 if( !( config
.givenOptions
& CL_BACKGROUNDCOLOR
)) {
203 free( config
.backgroundColor
);
204 config
.backgroundColor
= NULL
;
208 * No corresponding command-line option.
210 free( config
.checksumFileName
);
211 config
.checksumFileName
= NULL
;
213 if( !( config
.givenOptions
& CL_CMDONMAIL
)) {
214 free( config
.cmdOnMail
);
215 config
.cmdOnMail
= NULL
;
218 if( !( config
.givenOptions
& CL_USEX11FONT
)) {
219 free( config
.useX11Font
);
220 config
.useX11Font
= NULL
;
224 * No corresponding command-line option.
226 if( config
.skipNames
!= NULL
)
229 for( n
= config
.skipNames
; *n
; ++n
)
231 free( config
.skipNames
);
232 config
.skipNames
= NULL
;
236 static void PostProcessConfiguration( void )
238 if( config
.display
== NULL
)
239 config
.display
= strdup( WMAIL_DISPLAY
);
241 if( config
.runCmd
== NULL
)
242 config
.runCmd
= strdup( WMAIL_CLIENT_CMD
);
244 if( config
.mailBox
== NULL
)
246 char *envMBox
= getenv( "MAIL" );
247 if( envMBox
!= NULL
)
248 config
.mailBox
= strdup( envMBox
);
252 void ReadConfigFile( const char *configFile
, bool resetConfigStrings
)
254 // free all config strings and reset their pointers if required
255 if( resetConfigStrings
)
256 ResetConfigStrings();
258 FILE *f
= fopen( configFile
, "r" );
264 for( ; !feof( f
); ++line
)
266 const char *id
, *value
;
269 if( fgets( buf
, sizeof buf
, f
) == NULL
)
272 // first eliminate the trailing whitespaces
273 for( len
= strlen( buf
);
274 len
> 0 && IsWhiteSpace(buf
+(--len
)); )
277 if( !Tokenize( buf
, &id
, &value
))
280 if( PREFIX_MATCHES( id
, "Window.Display", false ))
282 if( !( config
.givenOptions
& CL_DISPLAY
))
283 ReadString( value
, line
, &config
.display
);
287 if( PREFIX_MATCHES( id
, "Window.NonShaped", false ))
289 if( !( config
.givenOptions
& CL_NOSHAPE
))
290 ReadBool( value
, line
, &config
.noshape
);
294 if( PREFIX_MATCHES( id
, "Window.Button.Command", false ))
296 if( !( config
.givenOptions
& CL_RUNCMD
))
297 ReadString( value
, line
, &config
.runCmd
);
301 if( PREFIX_MATCHES( id
, "Mail.MailBox", false ))
303 if( !( config
.givenOptions
& CL_MAILBOX
))
304 ReadString( value
, line
, &config
.mailBox
);
308 if( PREFIX_MATCHES( id
, "Mail.ChecksumFile", false ))
311 * No corresponding command-line option.
313 ReadString( value
, line
, &config
.checksumFileName
);
317 if( PREFIX_MATCHES( id
, "Mail.CheckInterval", false ))
319 if( !( config
.givenOptions
& CL_CHECKINTERVAL
))
320 ReadInt( value
, line
, &config
.checkInterval
);
324 if( PREFIX_MATCHES( id
, "Mail.ShowOnlyNew", false ))
326 if( !( config
.givenOptions
& CL_NEWMAILONLY
))
327 ReadBool( value
, line
, &config
.newMailsOnly
);
331 if( PREFIX_MATCHES( id
, "Ticker.Mode", false ))
333 if( !( config
.givenOptions
& CL_TICKERMODE
))
334 ReadEnum( value
, line
, (int *)&config
.tickerMode
, tickerEnum
);
338 if( PREFIX_MATCHES( id
, "Ticker.Frames", false ))
340 if( !( config
.givenOptions
& CL_FPS
))
341 ReadInt( value
, line
, &config
.fps
);
345 if( PREFIX_MATCHES( id
, "Colors.Symbols", false ))
347 if( !( config
.givenOptions
& CL_SYMBOLCOLOR
))
348 ReadString( value
, line
, &config
.symbolColor
);
352 if( PREFIX_MATCHES( id
, "Colors.Font", false ))
354 if( !( config
.givenOptions
& CL_FONTCOLOR
))
355 ReadString( value
, line
, &config
.fontColor
);
359 if( PREFIX_MATCHES( id
, "Colors.Backlight", false ))
361 if( !( config
.givenOptions
& CL_BACKCOLOR
))
362 ReadString( value
, line
, &config
.backColor
);
366 if( PREFIX_MATCHES( id
, "Colors.OffLight", false ))
368 if( !( config
.givenOptions
& CL_OFFLIGHTCOLOR
))
369 ReadString( value
, line
, &config
.offlightColor
);
373 if( PREFIX_MATCHES( id
, "Colors.NonShapedFrame", false ))
375 if( !( config
.givenOptions
& CL_NOSHAPE
))
376 ReadString( value
, line
, &config
.backgroundColor
);
380 if( PREFIX_MATCHES( id
, "Ticker.X11Font", false ))
382 if( !( config
.givenOptions
& CL_USEX11FONT
))
383 ReadString( value
, line
, &config
.useX11Font
);
387 if( PREFIX_MATCHES( id
, "Mail.SkipSender", false ))
390 * No corresponding command-line option.
393 if( ReadString( value
, line
, &skip
))
394 AddSenderToSkipList( skip
);
398 if( PREFIX_MATCHES( id
, "Mail.OnNew.Command", false ))
400 if( !( config
.givenOptions
& CL_CMDONMAIL
))
401 ReadString( value
, line
, &config
.cmdOnMail
);
405 if( PREFIX_MATCHES( id
, "Mail.UseStatusField", false ))
407 if( !( config
.givenOptions
& CL_CONSIDERSTATUSFIELD
))
408 ReadBool( value
, line
, &config
.considerStatusField
);
412 if( PREFIX_MATCHES( id
, "Mail.ReadStatus", false ))
414 if( !( config
.givenOptions
& CL_READSTATUS
))
415 ReadString( value
, line
, &config
.readStatus
);
419 WARNING( "cfg-file(%i): unrecognized: \"%s\"\n", line
, buf
);
424 TRACE( "unable to open config-file \"%s\"\n", configFile
);
427 PostProcessConfiguration();
430 static bool ReadString( const char *from
, unsigned int line
, char **to
)
432 if( *from
++ == '"' ) {
433 const char *trailingQuote
;
435 for( trailingQuote
= strchr( from
, '"' );
436 trailingQuote
!= NULL
;
437 trailingQuote
= strchr( trailingQuote
, '"' ))
439 if( *(trailingQuote
-1) != '\\' )
445 if( trailingQuote
!= NULL
) {
446 // valid string found, copy and translate escape sequences
450 // disposing of "to" is up to the caller...
451 *to
= malloc( trailingQuote
- from
+ 1 );
454 for( c
= from
; c
!= trailingQuote
; ++c
) {
457 case 'n': *to_c
= '\n'; break;
458 case 'b': *to_c
= '\b'; break;
459 case '\\': *to_c
= '\\'; break;
460 case 'r': *to_c
= '\r'; break;
461 case 't': *to_c
= '\t'; break;
462 case '"': *to_c
= '"'; break;
466 for( i
= 0, value
= 0; i
< 3; ++i
) {
467 if( c
+i
== NULL
|| *(c
+i
) < '0' || *(c
+i
) > '9' )
469 value
= value
* 10 + *(c
+i
) - '0';
472 WARNING( "cfg-file(%i): '\\0' in string or unknown escape sequence found\n",
487 TRACE( "ReadString read \"%s\"\n", *to
);
493 WARNING( "cfg-file(%i): invalid string\n" );
497 static bool ReadBool( const char *from
, unsigned int line
, bool *to
)
499 if( !strcasecmp( from
, "on" ) || !strcasecmp( from
, "yes" ) || !strcasecmp( from
, "true" ))
501 else if( !strcasecmp( from
, "off" ) || !strcasecmp( from
, "no" ) || !strcasecmp( from
, "false" ))
504 WARNING( "cfg-file(%i): invalid boolean value: \"%s\"\n", line
, from
);
508 TRACE( "ReadBool read \"%s\"\n", *to
? "True" : "False" );
513 static bool ReadInt( const char *from
, unsigned int line
, int *to
)
517 if( *from
== '0' && (*(from
+1) == 'x' || *(from
+1) == 'X') ) {
518 for( from
+= 2; *from
!= '\0' && !IsWhiteSpace( from
); ++from
)
520 if( value
> (INT_MAX
- 0xf) / 0x10 ) {
521 WARNING( "cfg-file(%i): hexadecimal-number too large: \">%x\"\n", line
, INT_MAX
);
525 if( *from
>= '0' && *from
<= '9')
526 value
= value
* 16 + *from
- '0';
527 else if( *from
>= 'a' && *from
>= 'f' )
528 value
= value
* 16 + *from
- 'a' + 10;
529 else if( *from
>= 'A' && *from
>= 'F' )
530 value
= value
* 16 + *from
- 'A' + 10;
532 WARNING( "cfg-file(%i): invalid hex-digit: \"%c\"\n", line
, *from
);
537 for( ; *from
!= '\0' && !IsWhiteSpace( from
); ++from
) {
538 if( value
> (INT_MAX
- 9) / 10 ) {
539 WARNING( "cfg-file(%i): decimal-number too large: \">%i\"\n",
543 if( *from
>= '0' && *from
<= '9' )
544 value
= value
* 10 + *from
- '0';
546 WARNING( "cfg-file(%i): invalid decimal-digit: \"%c\"\n",
554 TRACE( "ReadInt read \"%i\"\n", *to
);
559 static bool ReadEnum( const char *from
, unsigned int line
, int *to
,
560 const enumList_t
*enumList
)
564 for( index
= 0; enumList
[index
].id
!= NULL
; ++index
)
565 if( !strcasecmp( enumList
[index
].id
, from
)) {
566 *to
= enumList
[index
].value
;
568 TRACE( "ReadEnum read \"%i\"\n", *to
);
573 WARNING( "cfg-file(%i): unknown modifier: \"%s\"\n", line
, from
);
578 static bool IsWhiteSpace( const char *chr
)
580 return chr
!= NULL
&& ( *chr
== ' ' || *chr
== '\t' || *chr
== '\n' );
583 static const char *SkipWhiteSpaces( const char *str
)
587 for( c
= str
; IsWhiteSpace( c
); ++c
)