/* -*- c -*- elmo - ELectronic Mail Operator Copyright (C) 2003, 2004 rzyjontko This program 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; version 2. This program 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. ------------------------------------------------------------ */ %{ #define _GNU_SOURCE 1 /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #ifdef HAVE_WORDEXP_H # include #endif #include "error.h" #include "xmalloc.h" #include "confhold.h" #include "misc.h" #include "ecurses.h" #include "keymap.h" #include "cmd.h" #include "gettext.h" #include "abook.h" #include "procmail.h" #include "mime.h" #include "property.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define FIRST_ALLOC 128 #define MAX_DEPTH 10 #define YY_DECL static token_t next_token YY_PROTO ((void)) typedef enum token {KW_SET = 300, KW_HOOK = 301, KW_KEY = 302, KW_ADDR = 303, KW_INCLUDE = 304, KW_RULE = 305, KW_TRANSLATE = 306, KW_MIME = 307, KW_HANDLER = 308, KW_PROPERTY = 309, KW_MACRO = 310, IDENTIFIER = 401, EOL = 402, SQUOTED = 403, DQUOTED = 404, TEXT = 405, ARROW = 406, KEY_SPECIAL = 501, KEY_CONTROL = 502, KEY_SIMPLE = 503, KEY_VALUE = 504, } token_t; /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ typedef struct finfo { YY_BUFFER_STATE state; char *fname; int lineno; } finfo_t; /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ static token_t current_token = 0; /** * this is used for recursive file inclusion * * fstack and ftop are to be used only by frame_pop, and frame_push * others should use these functions and get data from fframe * * ftop points to the top used frame, so that fstack[ftop + 1] is the * first available */ static finfo_t fstack[MAX_DEPTH]; static int ftop = -1; static finfo_t *fframe; /** * these are copies of yytext and yyleng */ static char *token_txt = NULL; static int token_len = 0; /** * key-value, and a flag if a key is a meta-key */ static int token_key = 0; static int key_meta = 0; /** * this is set to 0 if confhold does not have appropriate structures * to hold new variable, it may happen if someone defines not registered * variable */ static int prepared_to_insert = 1; /** * this holds field's name that is passed to confhold_insert_value */ static char *field_name = NULL; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ static int parse (void); static int frame_push (char *file, int silent); static int frame_pop (void); /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ %} %option noyywrap ID [a-zA-Z_][a-zA-Z0-9_\-]+ SQT \'[^\']*\' DQT \"[^\"]*\" SKEY1 ()|()|()|()|()|()|() SKEY2 ()|()|()|()|()|() SKEY3 ()|()|() KEY ([^<>\n \t:\\\{\}\'\"\(\)])|(\\[^CM0-9\n]) VKEY \\[0-9]{3} %% ^[ \t]*#.* /* ignore */ [ \t] /* ignore */ ^set return KW_SET; ^key return KW_KEY; ^hook return KW_HOOK; ^addr return KW_ADDR; ^include return KW_INCLUDE; ^rule return KW_RULE; ^translate return KW_TRANSLATE; ^mime return KW_MIME; ^handler return KW_HANDLER; ^property return KW_PROPERTY; ^macro return KW_MACRO; {ID} return IDENTIFIER; {SQT} return SQUOTED; {DQT} return DQUOTED; "=>" return ARROW; (\\M)?({KEY}) { char *seek; if (strstr (yytext, "\\M") == yytext){ key_meta = 1; seek = yytext + 2; } else { key_meta = 0; seek = yytext; } if (*seek == '\\') token_key = seek[1]; else token_key = seek[0]; return KEY_SIMPLE; } (\\M)?({SKEY1}|{SKEY2}|{SKEY3}) { int f; char *seek; if (strstr (yytext, "\\M") == yytext){ key_meta = 1; seek = yytext + 2; } else { key_meta = 0; seek = yytext; } if (! strcmp (seek, "")) token_key = 9; else if (! strcmp (seek, "")) token_key = 27; else if (! strcmp (seek, "")) token_key = KEY_UP; else if (! strcmp (seek, "")) token_key = KEY_DOWN; else if (! strcmp (seek, "")) token_key = KEY_LEFT; else if (! strcmp (seek, "")) token_key = KEY_RIGHT; else if (! strcmp (seek, "")) token_key = KEY_PPAGE; else if (! strcmp (seek, "")) token_key = KEY_NPAGE; else if (! strcmp (seek, "")) token_key = KEY_BACKSPACE; else if (! strcmp (seek, "")) token_key = KEY_DC; else if (! strcmp (seek, "")) token_key = KEY_IC; else if (! strcmp (seek, "")) token_key = '\r'; else if (! strcmp (seek, "")) token_key = KEY_HOME; else if (! strcmp (seek, "")) token_key = KEY_END; else if (! strcmp (seek, "")) token_key = ' '; else { f = atoi (seek + 2); token_key = KEY_F (f); } return KEY_SPECIAL; } (\\M)?({VKEY}) { char *seek; if (strstr (yytext, "\\M") == yytext){ key_meta = 1; seek = yytext + 2; } else { key_meta = 0; seek = yytext; } token_key = strtol (seek + 1, NULL, 8); return KEY_VALUE; } (\\M)?(\\C[a-z]) { char *seek; if (strstr (yytext, "\\M") == yytext){ key_meta = 1; seek = yytext + 2; } else { key_meta = 0; seek = yytext; } token_key = seek[2] - 'a' + 1; return KEY_CONTROL; } \\\n fframe->lineno++; \n fframe->lineno++; return EOL; [^ \t\n:\\<>\{\}\(\),]+ return TEXT; . return *yytext; <> { if (ftop > 0) frame_pop (); else { return EOF; yyunput (0, NULL); } } %% int confread_read_file (const char *file, int silent) { int ret; ftop = -1; if (frame_push (xstrdup (file), silent)) return -1; current_token = next_token (); ret = parse (); frame_pop (); if (token_txt) xfree (token_txt); token_txt = NULL; return ret; } /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static char * expand (char *str) { #ifdef HAVE_WORDEXP_H wordexp_t word_vector; int len = 0; int i; char *result; char *end; if (wordexp (str, &word_vector, 0)){ return NULL; } for (i = 0; i < word_vector.we_wordc; i++){ len += strlen (word_vector.we_wordv[i]); } result = xmalloc (len + 1); end = result; for (i = 0; i < word_vector.we_wordc; i++){ end = stpcpy (end, word_vector.we_wordv[i]); } *end = '\0'; wordfree (&word_vector); return result; #else int len = strlen (str); char *result; if ((*str == '"' && str[len - 1] == '"') || (*str == '\'' && str[len - 1] == '\'')){ result = xmalloc (len - 2 + 1); memcpy (result, str + 1, len - 2); result[len - 2] = '\0'; } return xstrdup (str); #endif } static cmd_state_t name_to_state (char *name) { if (! strcmp (name, "folder")) return CMD_LIST; if (! strcmp (name, "mail")) return CMD_READ_MAIL; if (! strcmp (name, "select")) return CMD_SELECT_BOX; if (! strcmp (name, "fetch")) return CMD_FETCH; if (! strcmp (name, "send")) return CMD_SENDER; if (! strcmp (name, "attach")) return CMD_ATTACH; if (! strcmp (name, "abook")) return CMD_ABOOK; if (! strcmp (name, "abook_add")) return CMD_ABOOK_ADD; if (! strcmp (name, "ask")) return CMD_ASK; if (! strcmp (name, "help")) return CMD_HELP; if (! strcmp (name, "read")) return CMD_READ; if (! strcmp (name, "debug")) return CMD_DEBUG; if (! strcmp (name, "search")) return CMD_SEARCH; return CMD_INVALID; } static int frame_push (char *fname, int silent) { FILE *fp; struct stat st; if (ftop >= MAX_DEPTH){ error_ (0, _("includes nested to deep")); xfree (fname); return 1; } if (stat (fname, & st)){ if (! silent) error_ (errno, "%s", fname); xfree (fname); return 1; } if (! S_ISREG (st.st_mode)){ error_ (0, _("%s is not a file"), fname); xfree (fname); return 1; } fp = fopen (fname, "r"); if (fp == NULL){ error_ (errno, "%s", fname); xfree (fname); return 1; } ftop++; yyin = fp; fframe = fstack + ftop; fframe->fname = fname; fframe->lineno = 1; fframe->state = yy_create_buffer (yyin, YY_BUF_SIZE); yy_switch_to_buffer (fframe->state); return 0; } static int frame_pop (void) { xfree (fframe->fname); yy_delete_buffer (fframe->state); fclose (yyin); if (ftop > 0){ ftop--; fframe = fstack + ftop; yy_switch_to_buffer (fframe->state); } else { fframe = NULL; } return 0; } /**************************************************************************** * PARSER FUNCTIONS ****************************************************************************/ /** * Please note, that there is no error-recovery. First error generates * parse error message and stops file analysis. */ /* GRAMMAR ::= | ::= | | | | | | | | | ::= "hook" ID ID EOL ::= "key" ID ID EOL ::= "set" ID EOL | "set" ID "{" EOL "}" ::= "addr" "{" EOL "}" ::= "rule" ID "{" "}" ::= "translate" ::= "mime" EOL ::= "handler" EOL ::= "property" "(" ")" "{" EOL "}" ::= "include" EOL ::= KEY | CONTROL-KEY | SPECIAL | VALUE ::= | ::= ::= | "" ::= | ::= | ::= | ::= ID | EOL | TEXT | ... ::= "TO" | "SUBJECT" | "FROM" | "CC" | "TOCC" ::= SQUOTED | DQUOTED ::= TEXT"." ::= | ::= TEXT | IDENTIFIER | EOL ::= | ::= "=>" ::= | ::= SQUOTED | DQUOTED | EOL ::= ID ":" EOL | EOL ::= ID | SQUOTED | DQUOTED | TEXT */ static char * token_2_string (int token) { static char token_txt[2] = {'\0', '\0'}; if (token > 0 && token < 255){ *token_txt = token; return token_txt; } switch (token){ case KW_SET: return "set"; case KW_KEY: return "key"; case KW_HOOK: return "hook"; case KW_ADDR: return "addr"; case KW_INCLUDE: return "include"; case KW_RULE: return "rule"; case KW_TRANSLATE: return "translate"; case KW_MIME: return "mime"; case KW_HANDLER: return "handler"; case KW_PROPERTY: return "property"; case KW_MACRO: return "macro"; case IDENTIFIER: return _("identifier"); case EOL: /* it's tricky but this is about end of PREVIOUS line */ fframe->lineno--; return _("end of line"); case TEXT: return _("text"); case SQUOTED: case DQUOTED: return _("quoted text"); case ARROW: return "=>"; case KEY_SPECIAL: case KEY_CONTROL: case KEY_SIMPLE: case KEY_VALUE: return _("key specifier"); case EOF: return _("end of file"); } return ""; } static void parse_error (void) { error_ (0, _("%s:%d: parse error near %s"), fframe->fname, fframe->lineno, token_2_string (current_token)); } static int match (token_t token) { token_t old_token = current_token; if (token_txt == NULL){ token_len = FIRST_ALLOC + yyleng; token_txt = xmalloc (token_len + 1); } else if (token_len < yyleng + 2){ token_len += yyleng + 2; xfree (token_txt); token_txt = xmalloc (token_len); } memcpy (token_txt, yytext, yyleng + 1); current_token = next_token (); return old_token != token; } static int set_field_value (void) { char *txt; switch (current_token){ case IDENTIFIER: case KEY_SIMPLE: case TEXT: case SQUOTED: case DQUOTED: match (current_token); if (! prepared_to_insert) return 0; txt = expand (token_txt); if (txt){ if (confhold_insert_value (field_name, txt)) error_ (0, _("%s:%d: invalid field: %s"), fframe->fname, fframe->lineno, field_name); xfree (txt); } return 0; default: return 1; } } static int set_field (void) { if (current_token == EOL) return match (EOL); if (match (IDENTIFIER)) return 1; field_name = xstrdup (token_txt); if (match (':')){ if (field_name) xfree (field_name); field_name = NULL; return 1; } if (set_field_value ()){ if (field_name) xfree (field_name); field_name = NULL; return 1; } xfree (field_name); field_name = NULL; return match (EOL); } static int set_struct_value (void) { do { if (set_field ()) return 1; } while (current_token == IDENTIFIER || current_token == EOL); return 0; } static int set_value (void) { char *txt; switch (current_token){ case '{': match ('{'); if (match (EOL)) return 1; if (set_struct_value ()) return 1; return match ('}'); case IDENTIFIER: case SQUOTED: case DQUOTED: case TEXT: case KEY_SIMPLE: match (current_token); if (! prepared_to_insert) return match (EOL); txt = expand (token_txt); if (txt){ confhold_insert_value (NULL, txt); xfree (txt); } return match (EOL); default: return 1; } } static int set_command (void) { prepared_to_insert = 1; if (match (KW_SET)) return 1; if (match (IDENTIFIER)) return 1; if (confhold_prepare_to_insert (token_txt)){ prepared_to_insert = 0; error_ (0, _("%s:%d: no such variable: %s"), fframe->fname, fframe->lineno, token_txt); } return set_value (); } static int hook_command (void) { exec_t *trigger; exec_t *action; if (match (KW_HOOK)) return 1; if (match (IDENTIFIER)) return 1; trigger = exec_lookup (token_txt); if (trigger == NULL){ error_ (0, _("%s:%d: no such function: %s"), fframe->fname, fframe->lineno, token_txt); } if (match (IDENTIFIER)) return 1; action = exec_lookup (token_txt); if (action == NULL){ error_ (0, _("%s:%d: no such function: %s"), fframe->fname, fframe->lineno - 1, token_txt); } if (action->fun){ if (trigger && action){ if (trigger->hook == NULL) trigger->hook = hook_create (action->fun); else hook_add (trigger->hook, action->fun); } } else { error_ (0, _("%s:%d: macros (%s) may not be used as actions " "in hook command"), fframe->fname, fframe->lineno - 1, token_txt); } return match (EOL); } static int key_specifier (void) { switch (current_token){ case KEY_SIMPLE: case KEY_VALUE: case KEY_SPECIAL: case KEY_CONTROL: match (current_token); return 0; default: return 1; } } static int key_command (void) { cmd_state_t state; exec_t *exec; if (match (KW_KEY)) return 1; if (match (IDENTIFIER)) return 1; state = name_to_state (token_txt); if (state == CMD_INVALID) error_ (0, _("%s:%d: no such state: %s"), fframe->fname, fframe->lineno, token_txt); if (key_specifier ()) return 1; if (match (IDENTIFIER)) return 1; exec = exec_lookup (token_txt); if (exec == NULL) error_ (0, _("%s:%d: no such function: %s"), fframe->fname, fframe->lineno - 1, token_txt); if (state != CMD_INVALID && exec){ keymap_add (keymaps + state, token_key, key_meta, exec->fun); } return match (EOL); } static int addr_field_value (void) { char *txt; switch (current_token){ case IDENTIFIER: case TEXT: case SQUOTED: case DQUOTED: case KEY_SIMPLE: match (current_token); txt = expand (token_txt); if (txt){ abook_new_value (txt); xfree (txt); } return 0; default: return 1; } } static int addr_field (void) { switch (current_token){ case EOL: match (EOL); return 0; case IDENTIFIER: match (IDENTIFIER); if (abook_new_field (token_txt)) error_ (0, _("%s:%d: invalid field: %s"), fframe->fname, fframe->lineno, token_txt); if (match (':')) return 1; return addr_field_value (); default: return 1; } } static int addr_value (void) { do { if (addr_field ()) return 1; } while (current_token == IDENTIFIER || current_token == EOL); return 0; } static int addr_command (void) { if (match (KW_ADDR)) return 1; if (match ('{')) return 1; if (match (EOL)) return 1; abook_new_prepare (); if (addr_value ()){ abook_new_drop (); return 1; } abook_new_store (); return match ('}'); } static int include_command (void) { char *fname; if (match (KW_INCLUDE)) return 1; switch (current_token){ case DQUOTED: case SQUOTED: case IDENTIFIER: case TEXT: match (current_token); fname = expand (token_txt); frame_push (fname, 0); return match (EOL); default: return 1; } } static int rule_header (void) { while (1){ switch (current_token){ case IDENTIFIER: match (IDENTIFIER); if (strcmp (token_txt, "SUBJECT") == 0 || strcmp (token_txt, "TO") == 0 || strcmp (token_txt, "FROM") == 0 || strcmp (token_txt, "CC") == 0 || strcmp (token_txt, "TOCC") == 0){ procmail_setup_header (token_txt); return 0; } break; case EOL: case TEXT: case KEY_SPECIAL: case KEY_CONTROL: case KEY_SIMPLE: case KEY_VALUE: match (current_token); break; default: return 1; } } } static int rule_constraint (void) { char *txt; while (1){ switch (current_token){ case SQUOTED: match (SQUOTED); txt = expand (token_txt); procmail_setup_str (txt); xfree (txt); return 0; case DQUOTED: match (DQUOTED); txt = expand (token_txt); procmail_setup_re (txt); xfree (txt); return 0; case IDENTIFIER: case EOL: case TEXT: case KEY_SPECIAL: case KEY_CONTROL: case KEY_SIMPLE: case KEY_VALUE: match (current_token); break; default: return 1; } } } static int rule_action (void) { int len; while (1){ switch (current_token){ case SQUOTED: case DQUOTED: return 0; case IDENTIFIER: if (strcmp (yytext, "SUBJECT") == 0 || strcmp (yytext, "TO") == 0 || strcmp (yytext, "FROM") == 0 || strcmp (yytext, "CC") == 0 || strcmp (yytext, "TOCC") == 0) return 0; match (IDENTIFIER); break; case KEY_CONTROL: case KEY_SIMPLE: case KEY_VALUE: case EOL: match (current_token); break; case TEXT: match (TEXT); len = strlen (token_txt); if (len < 2) break; if (token_txt[len - 1] == '.'){ token_txt[len - 1] = '\0'; procmail_setup_action (token_txt, 1); goto eat_empty_tokens; } else if (token_txt[len - 1] == ';'){ token_txt[len - 1] = '\0'; procmail_setup_action (token_txt, 0); goto eat_empty_tokens; } break; default: return 1; } } eat_empty_tokens: while (current_token == EOL) match (EOL); return 0; } static int rule_content (void) { do { if (rule_header ()) return 1; if (rule_constraint ()) return 1; if (rule_action ()) return 1; } while (current_token != EOF && current_token != '}'); return 0; } static int rule_command (void) { if (match (KW_RULE)) return 1; if (match (IDENTIFIER)) return 1; procmail_setup_name (token_txt); if (match ('{')){ return 1; } if (rule_content ()){ return 1; } return match ('}'); } static int translate_charset (void) { char *txt; switch (current_token){ case IDENTIFIER: case SQUOTED: case DQUOTED: case TEXT: case KEY_SIMPLE: match (current_token); txt = expand (token_txt); mime_add_charset (txt); return 0; default: return 1; } } static int translate_command (void) { if (match (KW_TRANSLATE)) return 1; if (translate_charset ()) return 1; if (translate_charset ()) return 1; return match (EOL); } static char * mime_value (void) { char *txt; switch (current_token){ case IDENTIFIER: case SQUOTED: case DQUOTED: case TEXT: case KEY_SIMPLE: match (current_token); txt = expand (token_txt); return txt; default: return NULL; } } static int mime_command (void) { char *type; char *re; if (match (KW_MIME)) return 1; type = mime_value (); if (type == NULL) return 1; re = mime_value (); if (re == NULL) return 1; mime_register_type (type, re); return match (EOL); } static int handler_command (void) { char *type; char *handler; if (match (KW_HANDLER)) return 1; type = mime_value (); if (type == NULL) return 1; handler = mime_value (); if (handler == NULL) return 1; mime_register_handler (type, handler); return match (EOL); } static int property_arg (void) { switch (current_token){ case TEXT: case IDENTIFIER: match (current_token); property_add_arg (token_txt); return 0; case EOL: match (EOL); return 0; default: return 1; } } static int property_args (void) { do { if (property_arg ()) return 1; } while (current_token == TEXT || current_token == IDENTIFIER || current_token == EOL); return 0; } static int property_rule_cond (void) { char *txt; switch (current_token){ case SQUOTED: match (SQUOTED); txt = expand (token_txt); property_add_cond_str (txt); xfree (txt); return 0; case DQUOTED: match (DQUOTED); txt = expand (token_txt); property_add_cond_re (txt); xfree (txt); return 0; case EOL: match (EOL); return 0; default: return 1; } } static int property_rule_conds (void) { do { if (property_rule_cond ()) return 1; } while (current_token == SQUOTED || current_token == DQUOTED || current_token == EOL); return 0; } static int property_rule_val (void) { char *txt; switch (current_token){ case IDENTIFIER: case TEXT: case SQUOTED: case DQUOTED: case KEY_SIMPLE: match (current_token); txt = expand (token_txt); property_add_value (txt); return 0; default: return 1; } } static int property_rule (void) { if (property_rule_conds ()) return 1; if (match (ARROW)) return 1; if (property_rule_val ()) return 1; return match (EOL); } static int property_body (void) { do { if (property_rule ()) return 1; } while (current_token == SQUOTED || current_token == DQUOTED || current_token == EOL); return 0; } static int property_name (void) { switch (current_token){ case IDENTIFIER: case TEXT: match (current_token); property_new (token_txt); return 0; default: return 1; } } static int property_command (void) { if (match (KW_PROPERTY)) return 1; if (property_name ()) return 1; if (match ('(')) return 1; if (property_args ()) return 1; if (match (')')) return 1; if (match ('{')) return 1; if (match (EOL)) return 1; if (property_body ()) return 1; if (match ('}')) return 1; return 0; } static int macro_comment (void) { char *txt; switch (current_token){ case SQUOTED: case DQUOTED: match (current_token); txt = expand (token_txt); exec_macro_comment (txt); return match (EOL); default: return 0; } } static int macro_instruction (void) { switch (current_token){ case IDENTIFIER: match (IDENTIFIER); exec_macro_add_hook (token_txt); return match (EOL); case EOL: match (EOL); return 0; default: return 1; } } static int macro_body (void) { do { if (macro_instruction ()) return 1; } while (current_token == IDENTIFIER || current_token == EOL); return 0; } static int macro_command (void) { if (match (KW_MACRO)) return 1; if (match (IDENTIFIER)) return 1; exec_new_macro (token_txt); if (match ('{')) return 1; if (match (EOL)) return 1; if (macro_comment ()) return 1; if (macro_body ()) return 1; exec_macro_commit (); if (match ('}')) return 1; return 0; } static int command (void) { switch (current_token){ case KW_SET: return set_command (); case KW_HOOK: return hook_command (); case KW_KEY: return key_command (); case KW_ADDR: return addr_command (); case KW_INCLUDE: return include_command (); case KW_RULE: return rule_command (); case KW_TRANSLATE: return translate_command (); case KW_MIME: return mime_command (); case KW_HANDLER: return handler_command (); case KW_PROPERTY: return property_command (); case KW_MACRO: return macro_command (); case EOL: return match (EOL); default: return 1; } } static int command_list (void) { while (current_token != EOF){ if (command ()){ return 1; } } return 0; } static int parse (void) { if (command_list ()){ parse_error (); return 1; } return match (EOF); } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE confread.l * ****************************************************************************/