/* * POP3Lite - 3lite POP3 Daemon * Copyright (C) 2000, 2001 Gergely Nagy <8@free.bsd.hu> * * This file is part of POP3Lite. * * POP3Lite is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * POP3Lite is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "main.h" #include #include #include #include #include #include #include #include #include #include #include "cfg.h" static const char rcsid[]="$Id: cfg.c,v 1.8 2001/01/24 14:01:47 algernon Exp $"; static char *cfg_strip_str ( char *str ); static char *cfg_strip_quote ( char *str ); /** * cfg_strip_quote: strip leading & trailing quotes * @str: the victim * * Strips the leading & trailing quotes if BOTH are * present. * * Returns: the bare truth ;) **/ static char * cfg_strip_quote ( char *str ) { char *mystr; if ( str[0] == '"' && str[strlen(str)-1] == '"' ) { mystr = &str[1]; mystr[strlen(mystr)-1] = '\0'; } else mystr = str; return mystr; } /** * cfg_strip_str: strips leading & trailing whitespace * @str: the victim * * Strips spaces and tabs from the beggining and from * the end of a string. * * Returns: a stripped string **/ static char * cfg_strip_str ( char *str ) { int i = 0; char *mystr; while ( str[i] == ' ' || str[i] == '\t' ) i++; mystr = &str[i]; i = strlen(mystr)-1; while ( mystr[i] == ' ' || mystr[i] == '\t' ) i--; mystr[i+1] = '\0'; return mystr; } /** * cfg_parse_line: parse a configuration file line * @control: the control to insert result into * @line: the line itself * @linecnt: the line number, for error message generation * @prev: previous key * * Parses a configuration file line. * * Returns: the config key **/ char * cfg_parse_line ( P3LControl *control, const char *ln, unsigned long linecnt, const char *prev, GHashTable *table ) { long j, i = 0; int seen_eq = -1; char *args, *carg, *lv1, *cmd = NULL, *line; gboolean ows = TRUE; gboolean is_cont = FALSE, to_cont = FALSE; GList *arglist = NULL; GList *arglist_c = NULL; GList *arglist_a = NULL; if ( ln == NULL ) return g_strdup ( prev ); if ( ! strcmp ( ln, "" ) ) return g_strdup ( prev ); line = g_strdup ( ln ); /* * Strip \r, \n and comments */ while ( 1 ) { switch ( line[i] ) { case '\r': case '\n': line[i]='\0'; break; case '#': /* Escaped # is not a comment */ if ( line [i-1] != '\\' ) line[i] = '\0'; break; case '=': if ( seen_eq < 0 ) seen_eq = i; break; default: break; } if ( line[i] == '\0' ) break; i++; } /* * Detect if we must do anything about this... * RATIONALE: a line that only contains whitespace, * is ignored. a line with no euqation signs, is * considered an error. */ i=0; while ( i < strlen ( line ) ) { if ( line[i] != ' ' ) { if ( line [i] == '=' ) seen_eq = -2; ows = FALSE; break; } i++; } /* * Whitespace only lines are ignored. * FIXME: if it is continued... */ if ( ows ) { g_free ( line ); return g_strdup ( prev ); } /* * If it is non-whitespace, and has no =, it is * an error. However, if it is a continued line... */ if ( seen_eq == -1 && prev == NULL ) { control->system->log ( control, LOG_CRIT, "CFG: Syntax error on line %lu (no equation sign).", linecnt ); exit ( 1 ); } /* * A line that begins with [\t ]*= is illegal */ if ( seen_eq == -2 ) { control->system->log ( control, LOG_CRIT, "CFG: Syntax error on line %lu (no field name).", linecnt ); exit ( 1 ); } /* * Now we split it into two... * The first equation sign is marked by seen_eq, so use it! */ if ( prev == NULL ) { cmd = g_strndup ( line, seen_eq ); args = g_strdup ( &line[seen_eq+1] ); } else { is_cont = TRUE; cmd = g_strdup ( prev ); args = g_strdup ( line ); } /* * Strip whitespace... */ args = cfg_strip_str ( args ); cmd = cfg_strip_str ( cmd ); g_strup ( cmd ); /* * Here comes a hard part... * We have to split up the args by [\t ,], but only if * they aren't inside quotes */ i = 0; carg = g_strdup(""); while ( i < strlen ( args ) ) { switch ( args[i] ) { case '"': /* Escaped quotes are treated specially. */ if ( args[i-1] == '\\' ) { carg = g_strdup_printf ( "%s%c", carg, args[i] ); break; } /* Find matching quote ... */ j=i; i++; while ( args[i] != '"' && args[i-1] != '\\' && i < strlen(args) ) i++; if ( i == strlen (args) && args[i] != '"' ) { control->system->log ( control, LOG_CRIT, "CFG: Syntax error on line %lu (unmatched quotes)", linecnt ); exit ( 1 ); } /* ... and copy it */ carg = g_strdup_printf ( "%s%s", carg, g_strndup ( &args[j], i-j+1 ) ); break; case ' ': case ',': case '\t': /* * These are separators, they cannot be escaped, * only quoted, and that is captured above. */ if ( strlen ( carg ) > 0 ) arglist = g_list_append ( arglist, cfg_strip_quote ( g_strdup ( carg ) ) ); g_free ( carg ); carg = g_strdup ( "" ); break; default: /* Everything else gets appended */ carg = g_strdup_printf ( "%s%c", carg, args[i] ); break; } i++; } /* * If there are pending things, append them too */ if ( strlen ( carg ) > 0 ) arglist = g_list_append ( arglist, cfg_strip_quote ( g_strdup ( carg ) ) ); g_free ( carg ); /* * If the value ends with \, it will be * continued */ lv1 = (char *) g_list_nth_data ( arglist, g_list_length ( arglist ) - 1 ); if ( lv1 && ! strcmp ( lv1, "\\" ) ) { to_cont = 1; g_list_remove_link ( arglist, g_list_last ( arglist ) ); } if ( is_cont == TRUE ) { arglist_c = g_hash_table_lookup ( table, cmd ); for ( i = 0; i < g_list_length ( arglist_c ); i++ ) arglist_a = g_list_append ( arglist_a, g_list_nth_data ( arglist_c, i ) ); arglist = g_list_concat ( arglist_a, arglist ); } /* * And finally add it to the hash. */ g_hash_table_insert ( table, cmd, arglist ); g_free ( line ); return ( to_cont ) ? cmd : NULL; } /** * p3l_load_config: load configuration * @control: the usual control struct * @filename: where to load configuration from * @table: destination * * This loads the a configuration file. * * Returns: nothing **/ void p3l_load_config ( P3LControl *control, const char *filename, GHashTable *table ) { char *fn, *line, *buffer, *prev=NULL; P3LString **lines; int i = 0; size_t size, linenum; fn = ( filename != NULL ) ? g_strdup ( filename ) : g_strdup ( SYSCONFDIR "/pop3lite.conf" ); buffer = (char *) p3l_read_file ( fn, &size ); if ( !buffer ) { g_free ( fn ); return; } lines = p3l_split_lines ( buffer, size, &linenum ); for ( i = 0; i < linenum; i++ ) { line = (char *) g_malloc ( lines[i]->length + 1 ); memcpy ( line, lines[i]->str, lines[i]->length ); line[lines[i]->length]=0; prev = cfg_parse_line ( control, line, i, prev, table ); g_free ( line ); } g_free ( buffer ); g_free ( fn ); }