wmail: fixed config typo.
[dockapps.git] / wmail / src / config.c
blobadf96dee3856590d40a0b67745543982e3190de7
1 ///////////////////////////////////////////////////////////////////////////////
2 // config.c
3 // configuration file parser, part of wmail
4 //
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>.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions
11 // are met:
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.
32 #ifdef HAVE_CONFIG_H
33 #ifndef CONFIG_H_INCLUDED
34 #include "../config.h"
35 #define CONFIG_H_INCLUDED
36 #endif
37 #endif
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #ifdef HAVE_STRINGS_H
43 #include <strings.h>
44 #endif
45 #include <limits.h>
47 #include "common.h"
48 #include "config.h"
51 ///////////////////////////////////////////////////////////////////////////////
52 // wmailrc file format
54 // # - comment-lines
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 ///////////////////////////////////////////////////////////////////////////////
78 // typedefs
81 // list of enum-identifiers and their associated values
82 typedef struct { char *id; int value; } enumList_t;
86 ///////////////////////////////////////////////////////////////////////////////
87 // local prototypes
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
99 config_t config = {
100 .checkInterval = WMAIL_CHECK_INTERVAL,
101 .fps = WMAIL_FPS,
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 },
111 { NULL, 0 }
114 static bool Tokenize( const char *line, const char **id, const char **value )
116 size_t len;
117 const char *token1, *token2;
119 if( line != NULL )
121 token1 = SkipWhiteSpaces( line );
123 if(( len = strlen( token1 )) != 0 && token1[0] != '#' )
125 token2 = strchr( token1, '=' );
126 if( token2 != NULL )
127 token2 = SkipWhiteSpaces( token2+1 );
129 if( !IsWhiteSpace( token2 ))
131 *id = token1;
132 *value = token2;
134 return true;
139 return false;
142 static void AddSenderToSkipList( char *sender )
144 size_t numNames;
145 char **skipName, **newList;
147 for( skipName = config.skipNames, numNames = 0;
148 skipName != NULL && *skipName != NULL; skipName++ )
150 if( !strcmp( *skipName, sender ))
151 return;
153 numNames++;
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");
162 return;
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 )
228 char **n;
229 for( n = config.skipNames; *n; ++n )
230 free( *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" );
259 if( f != NULL )
261 char buf[1024];
262 int line = 1;
264 for( ; !feof( f ); ++line )
266 const char *id, *value;
267 size_t len;
269 if( fgets( buf, sizeof buf, f ) == NULL )
270 break;
272 // first eliminate the trailing whitespaces
273 for( len = strlen( buf );
274 len > 0 && IsWhiteSpace(buf+(--len)); )
275 *(buf+len) = '\0';
277 if( !Tokenize( buf, &id, &value ))
278 continue;
280 if( PREFIX_MATCHES( id, "Window.Display", false ))
282 if( !( config.givenOptions & CL_DISPLAY ))
283 ReadString( value, line, &config.display );
284 continue;
287 if( PREFIX_MATCHES( id, "Window.NonShaped", false ))
289 if( !( config.givenOptions & CL_NOSHAPE ))
290 ReadBool( value, line, &config.noshape );
291 continue;
294 if( PREFIX_MATCHES( id, "Window.Button.Command", false ))
296 if( !( config.givenOptions & CL_RUNCMD ))
297 ReadString( value, line, &config.runCmd );
298 continue;
301 if( PREFIX_MATCHES( id, "Mail.MailBox", false ))
303 if( !( config.givenOptions & CL_MAILBOX ))
304 ReadString( value, line, &config.mailBox );
305 continue;
308 if( PREFIX_MATCHES( id, "Mail.ChecksumFile", false ))
311 * No corresponding command-line option.
313 ReadString( value, line, &config.checksumFileName );
314 continue;
317 if( PREFIX_MATCHES( id, "Mail.CheckInterval", false ))
319 if( !( config.givenOptions & CL_CHECKINTERVAL ))
320 ReadInt( value, line, &config.checkInterval );
321 continue;
324 if( PREFIX_MATCHES( id, "Mail.ShowOnlyNew", false ))
326 if( !( config.givenOptions & CL_NEWMAILONLY ))
327 ReadBool( value, line, &config.newMailsOnly );
328 continue;
331 if( PREFIX_MATCHES( id, "Ticker.Mode", false ))
333 if( !( config.givenOptions & CL_TICKERMODE ))
334 ReadEnum( value, line, (int *)&config.tickerMode, tickerEnum );
335 continue;
338 if( PREFIX_MATCHES( id, "Ticker.Frames", false ))
340 if( !( config.givenOptions & CL_FPS ))
341 ReadInt( value, line, &config.fps );
342 continue;
345 if( PREFIX_MATCHES( id, "Colors.Symbols", false ))
347 if( !( config.givenOptions & CL_SYMBOLCOLOR ))
348 ReadString( value, line, &config.symbolColor );
349 continue;
352 if( PREFIX_MATCHES( id, "Colors.Font", false ))
354 if( !( config.givenOptions & CL_FONTCOLOR ))
355 ReadString( value, line, &config.fontColor );
356 continue;
359 if( PREFIX_MATCHES( id, "Colors.Backlight", false ))
361 if( !( config.givenOptions & CL_BACKCOLOR ))
362 ReadString( value, line, &config.backColor );
363 continue;
366 if( PREFIX_MATCHES( id, "Colors.OffLight", false ))
368 if( !( config.givenOptions & CL_OFFLIGHTCOLOR ))
369 ReadString( value, line, &config.offlightColor );
370 continue;
373 if( PREFIX_MATCHES( id, "Colors.NonShapedFrame", false ))
375 if( !( config.givenOptions & CL_NOSHAPE ))
376 ReadString( value, line, &config.backgroundColor );
377 continue;
380 if( PREFIX_MATCHES( id, "Ticker.X11Font", false ))
382 if( !( config.givenOptions & CL_USEX11FONT ))
383 ReadString( value, line, &config.useX11Font );
384 continue;
387 if( PREFIX_MATCHES( id, "Mail.SkipSender", false ))
390 * No corresponding command-line option.
392 char *skip;
393 if( ReadString( value, line, &skip ))
394 AddSenderToSkipList( skip );
395 continue;
398 if( PREFIX_MATCHES( id, "Mail.OnNew.Command", false ))
400 if( !( config.givenOptions & CL_CMDONMAIL ))
401 ReadString( value, line, &config.cmdOnMail );
402 continue;
405 if( PREFIX_MATCHES( id, "Mail.UseStatusField", false ))
407 if( !( config.givenOptions & CL_CONSIDERSTATUSFIELD ))
408 ReadBool( value, line, &config.considerStatusField );
409 continue;
412 if( PREFIX_MATCHES( id, "Mail.ReadStatus", false ))
414 if( !( config.givenOptions & CL_READSTATUS ))
415 ReadString( value, line, &config.readStatus );
416 continue;
419 WARNING( "cfg-file(%i): unrecognized: \"%s\"\n", line, buf );
422 fclose( f );
423 } else {
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) != '\\' )
440 break;
442 ++trailingQuote;
445 if( trailingQuote != NULL ) {
446 // valid string found, copy and translate escape sequences
447 const char *c;
448 char *to_c;
450 // disposing of "to" is up to the caller...
451 *to = malloc( trailingQuote - from + 1 );
452 to_c = *to;
454 for( c = from; c != trailingQuote; ++c ) {
455 if( *c == '\\' ) {
456 switch( *(++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;
463 default:
465 int value, i;
466 for( i = 0, value = 0; i < 3; ++i ) {
467 if( c+i == NULL || *(c+i) < '0' || *(c+i) > '9' )
468 break;
469 value = value * 10 + *(c+i) - '0';
471 if( value == 0 )
472 WARNING( "cfg-file(%i): '\\0' in string or unknown escape sequence found\n",
473 line );
474 else {
475 *to_c = (char)value;
476 c += i-1;
480 } else
481 *to_c = *c;
483 ++to_c;
486 *to_c = '\0';
487 TRACE( "ReadString read \"%s\"\n", *to );
489 return true;
493 WARNING( "cfg-file(%i): invalid string\n" );
494 return false;
497 static bool ReadBool( const char *from, unsigned int line, bool *to )
499 if( !strcasecmp( from, "on" ) || !strcasecmp( from, "yes" ) || !strcasecmp( from, "true" ))
500 *to = true;
501 else if( !strcasecmp( from, "off" ) || !strcasecmp( from, "no" ) || !strcasecmp( from, "false" ))
502 *to = false;
503 else {
504 WARNING( "cfg-file(%i): invalid boolean value: \"%s\"\n", line, from );
505 return false;
508 TRACE( "ReadBool read \"%s\"\n", *to ? "True" : "False" );
510 return true;
513 static bool ReadInt( const char *from, unsigned int line, int *to )
515 int value = 0;
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 );
522 return false;
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;
531 else {
532 WARNING( "cfg-file(%i): invalid hex-digit: \"%c\"\n", line, *from );
533 return false;
536 } else
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",
540 line, INT_MAX );
541 return false;
543 if( *from >= '0' && *from <= '9' )
544 value = value * 10 + *from - '0';
545 else {
546 WARNING( "cfg-file(%i): invalid decimal-digit: \"%c\"\n",
547 line, *from );
548 return false;
552 *to = value;
554 TRACE( "ReadInt read \"%i\"\n", *to );
556 return true;
559 static bool ReadEnum( const char *from, unsigned int line, int *to,
560 const enumList_t *enumList )
562 int index;
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 );
570 return true;
573 WARNING( "cfg-file(%i): unknown modifier: \"%s\"\n", line, from );
575 return false;
578 static bool IsWhiteSpace( const char *chr )
580 return chr != NULL && ( *chr == ' ' || *chr == '\t' || *chr == '\n' );
583 static const char *SkipWhiteSpaces( const char *str )
585 const char *c;
587 for( c = str; IsWhiteSpace( c ); ++c )
590 return c;