wmacpi: move variable and remove header.
[dockapps.git] / wmail / src / config.c
blob56578e66481419af7f51cd4fc05049d870310cec
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 )
128 token2 = SkipWhiteSpaces( token2 + 1 );
130 if( !IsWhiteSpace( token2 ))
132 *id = token1;
133 *value = token2;
135 return true;
141 return false;
144 static void AddSenderToSkipList( char *sender )
146 size_t numNames;
147 char **skipName, **newList;
149 for( skipName = config.skipNames, numNames = 0;
150 skipName != NULL && *skipName != NULL; skipName++ )
152 if( !strcmp( *skipName, sender ))
153 return;
155 numNames++;
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");
164 return;
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 )
239 char **n;
240 for( n = config.skipNames; *n; ++n )
241 free( *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" );
270 if( f != NULL )
272 char buf[1024];
273 int line = 1;
275 for( ; !feof( f ); ++line )
277 const char *id, *value;
278 size_t len;
280 if( fgets( buf, sizeof buf, f ) == NULL )
281 break;
283 // first eliminate the trailing whitespaces
284 for( len = strlen( buf ); len > 0 && IsWhiteSpace(buf + (--len)); )
285 *(buf + len) = '\0';
287 if( !Tokenize( buf, &id, &value ))
288 continue;
290 if( PREFIX_MATCHES( id, "Window.Display", false ))
292 if( !( config.givenOptions & CL_DISPLAY ))
293 ReadString( value, line, &config.display );
294 continue;
297 if( PREFIX_MATCHES( id, "Window.NonShaped", false ))
299 if( !( config.givenOptions & CL_NOSHAPE ))
300 ReadBool( value, line, &config.noshape );
301 continue;
304 if( PREFIX_MATCHES( id, "Window.Button.Command", false ))
306 if( !( config.givenOptions & CL_RUNCMD ))
307 ReadString( value, line, &config.runCmd );
308 continue;
311 if( PREFIX_MATCHES( id, "Mail.MailBox", false ))
313 if( !( config.givenOptions & CL_MAILBOX ))
314 ReadString( value, line, &config.mailBox );
315 continue;
318 if( PREFIX_MATCHES( id, "Mail.ChecksumFile", false ))
321 * No corresponding command-line option.
323 ReadString( value, line, &config.checksumFileName );
324 continue;
327 if( PREFIX_MATCHES( id, "Mail.CheckInterval", false ))
329 if( !( config.givenOptions & CL_CHECKINTERVAL ))
330 ReadInt( value, line, &config.checkInterval );
331 continue;
334 if( PREFIX_MATCHES( id, "Mail.ShowOnlyNew", false ))
336 if( !( config.givenOptions & CL_NEWMAILONLY ))
337 ReadBool( value, line, &config.newMailsOnly );
338 continue;
341 if( PREFIX_MATCHES( id, "Ticker.Mode", false ))
343 if( !( config.givenOptions & CL_TICKERMODE ))
344 ReadEnum( value, line, (int *)&config.tickerMode, tickerEnum );
345 continue;
348 if( PREFIX_MATCHES( id, "Ticker.Frames", false ))
350 if( !( config.givenOptions & CL_FPS ))
351 ReadInt( value, line, &config.fps );
352 continue;
355 if( PREFIX_MATCHES( id, "Colors.Symbols", false ))
357 if( !( config.givenOptions & CL_SYMBOLCOLOR ))
358 ReadString( value, line, &config.symbolColor );
359 continue;
362 if( PREFIX_MATCHES( id, "Colors.Font", false ))
364 if( !( config.givenOptions & CL_FONTCOLOR ))
365 ReadString( value, line, &config.fontColor );
366 continue;
369 if( PREFIX_MATCHES( id, "Colors.Backlight", false ))
371 if( !( config.givenOptions & CL_BACKCOLOR ))
372 ReadString( value, line, &config.backColor );
373 continue;
376 if( PREFIX_MATCHES( id, "Colors.OffLight", false ))
378 if( !( config.givenOptions & CL_OFFLIGHTCOLOR ))
379 ReadString( value, line, &config.offlightColor );
380 continue;
383 if( PREFIX_MATCHES( id, "Colors.NonShapedFrame", false ))
385 if( !( config.givenOptions & CL_NOSHAPE ))
386 ReadString( value, line, &config.backgroundColor );
387 continue;
390 if( PREFIX_MATCHES( id, "Ticker.X11Font", false ))
392 if( !( config.givenOptions & CL_USEX11FONT ))
393 ReadString( value, line, &config.useX11Font );
394 continue;
397 if( PREFIX_MATCHES( id, "Mail.SkipSender", false ))
400 * No corresponding command-line option.
402 char *skip;
403 if( ReadString( value, line, &skip ))
404 AddSenderToSkipList( skip );
405 continue;
408 if( PREFIX_MATCHES( id, "Mail.OnNew.Command", false ))
410 if( !( config.givenOptions & CL_CMDONMAIL ))
411 ReadString( value, line, &config.cmdOnMail );
412 continue;
415 if( PREFIX_MATCHES( id, "Mail.UseStatusField", false ))
417 if( !( config.givenOptions & CL_CONSIDERSTATUSFIELD ))
418 ReadBool( value, line, &config.considerStatusField );
419 continue;
422 if( PREFIX_MATCHES( id, "Mail.ReadStatus", false ))
424 if( !( config.givenOptions & CL_READSTATUS ))
425 ReadString( value, line, &config.readStatus );
426 continue;
429 WARNING( "cfg-file(%i): unrecognized: \"%s\"\n", line, buf );
432 fclose( f );
434 else
435 TRACE( "unable to open config-file \"%s\"\n", configFile );
437 PostProcessConfiguration();
440 static bool ReadString( const char *from, unsigned int line, char **to )
442 if( *from++ == '"' )
444 const char *trailingQuote;
446 for( trailingQuote = strchr( from, '"' );
447 trailingQuote != NULL;
448 trailingQuote = strchr( trailingQuote, '"' ))
450 if( *(trailingQuote-1) != '\\' )
451 break;
453 ++trailingQuote;
456 if( trailingQuote != NULL )
458 // valid string found, copy and translate escape sequences
459 const char *c;
460 char *to_c;
462 // disposing of "to" is up to the caller...
463 to_c = malloc( trailingQuote - from + 1 );
464 if( to_c == NULL )
465 return false;
467 *to = to_c;
469 for( c = from; c != trailingQuote; ++c )
471 if( *c == '\\' )
473 switch( *(++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;
481 default:
483 int value, i;
484 for( i = 0, value = 0; i < 3; ++i )
486 if( c + i == NULL || *(c + i) < '0' || *(c + i) > '9' )
487 break;
488 value = value * 10 + *(c + i) - '0';
490 if( value == 0 )
491 WARNING( "cfg-file(%i): '\\0' in string or unknown escape sequence found\n",
492 line );
493 else
495 *to_c = (char)value;
496 c += i - 1;
501 else
502 *to_c = *c;
504 ++to_c;
507 *to_c = '\0';
509 TRACE( "ReadString read \"%s\"\n", *to );
510 return true;
514 WARNING( "cfg-file(%i): invalid string\n" );
515 return false;
518 static bool ReadBool( const char *from, unsigned int line, bool *to )
520 if( !strcasecmp( from, "on" ) || !strcasecmp( from, "yes" ) || !strcasecmp( from, "true" ))
521 *to = true;
522 else if( !strcasecmp( from, "off" ) || !strcasecmp( from, "no" ) || !strcasecmp( from, "false" ))
523 *to = false;
524 else
526 WARNING( "cfg-file(%i): invalid boolean value: \"%s\"\n", line, from );
527 return false;
530 TRACE( "ReadBool read \"%s\"\n", *to ? "True" : "False" );
532 return true;
535 static bool ReadInt( const char *from, unsigned int line, int *to )
537 int value = 0;
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 );
545 return false;
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;
554 else
556 WARNING( "cfg-file(%i): invalid hex-digit: \"%c\"\n", line, *from );
557 return false;
560 else
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",
566 line, INT_MAX );
567 return false;
569 if( *from >= '0' && *from <= '9' )
570 value = value * 10 + *from - '0';
571 else
573 WARNING( "cfg-file(%i): invalid decimal-digit: \"%c\"\n",
574 line, *from );
575 return false;
579 *to = value;
581 TRACE( "ReadInt read \"%i\"\n", *to );
583 return true;
586 static bool ReadEnum( const char *from, unsigned int line, int *to,
587 const enumList_t *enumList )
589 int index;
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 );
598 return true;
601 WARNING( "cfg-file(%i): unknown modifier: \"%s\"\n", line, from );
603 return false;
606 static bool IsWhiteSpace( const char *chr )
608 return chr != NULL && ( *chr == ' ' || *chr == '\t' || *chr == '\n' );
611 static const char *SkipWhiteSpaces( const char *str )
613 const char *c;
615 for( c = str; IsWhiteSpace( c ); ++c )
618 return c;