/* * ffe - flat file extractor * * Copyright (C) 2006 Timo Savinen * This file is part of ffe. * * ffe 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. * * ffe 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 ffe; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /* $Id: parserc.c,v 1.41 2007-05-30 07:32:48 timo Exp $ */ /* parsing the rc-file */ #include "ffe.h" #include #include #include #ifdef PACKAGE static char *program = PACKAGE; #else static char *program = "ffe"; #endif /* pointer to rc-file */ static FILE *fp = NULL; /* current line number */ static int lineno = 0; /* block chars */ #define BLOCK_START '{' #define BLOCK_END '}' #define COMMENT '#' /* reading logical line states */ #define LL_OPTION 1 #define LL_BLOCK_START 2 #define LL_BLOCK_END 3 #define LL_EOF 4 /* structure containing one rc-file option definition */ struct rc_option { char *name; /* the name */ char *parameters; /* paramters option must/may have */ }; char *values[100]; /* poister to option name and parameters */ /* rc-file option parameter pictures M_ = mandatory O_ = optional */ #define M_STRING 'S' #define O_STRING 's' #define M_NUMBER 'N' #define O_NUMBER 'n' #define M_CHAR 'C' #define O_CHAR 'c' /* option names */ #define N_STRUCTURE "structure" #define N_TYPE "type" #define N_QUOTE "quoted" #define N_HEADER "header" #define N_OUTPUT "output" #define N_RECORD "record" #define N_ID "id" #define N_FIELD "field" #define N_FIELDSFROM "fields-from" #define N_FILE_HEADER "file-header" #define N_FILE_TRAILER "file-trailer" #define N_DATA "data" #define N_SEPARATOR "separator" #define N_RECORD_HEADER "record-header" #define N_RECORD_TRAILER "record-trailer" #define N_JUSTIFY "justify" #define N_INDENT "indent" #define N_FIELDLIST "field-list" #define N_PRINT_NO_DATA "no-data-print" #define N_FIELD_EMPTY_PRINT "field-empty-print" #define N_EMPTY_CHARS "empty-chars" #define N_LOOKUP "lookup" #define N_PAIR "pair" #define N_FILE "file" #define N_DEFAULT "default-value" #define N_SEARCH "search" #define N_CONST "const" static struct rc_option rc_opts[] = { {N_STRUCTURE,"S"}, {N_TYPE,"Scc"}, {N_QUOTE,"c"}, {N_HEADER,"S"}, {N_RECORD,"S"}, {N_OUTPUT,"S"}, {N_ID,"NS"}, {N_FIELD,"Sns"}, {N_FIELDSFROM,"S"}, {N_FILE_HEADER,"S"}, {N_FILE_TRAILER,"S"}, {N_DATA,"S"}, {N_SEPARATOR,"S"}, {N_RECORD_HEADER,"S"}, {N_RECORD_TRAILER,"S"}, {N_JUSTIFY,"S"}, {N_INDENT,"S"}, {N_FIELDLIST,"S"}, {N_PRINT_NO_DATA,"S"}, {N_FIELD_EMPTY_PRINT,"S"}, {N_EMPTY_CHARS,"S"}, {N_LOOKUP,"S"}, {N_PAIR,"SS"}, {N_FILE,"Sc"}, {N_DEFAULT,"S"}, {N_SEARCH,"S"}, {N_CONST,"SS"}, {NULL,NULL} }; void open_rc_file(char *file) { fp = fopen(file,"r"); if (fp == NULL) panic("Error in opening file",file,strerror(errno)); } /* remove leading and trailing whitespace */ void trim(char *buf) { register char *wpos=buf,*rpos=buf; while(isspace(*rpos)) rpos++; if(rpos != buf) { while(*rpos) { *wpos = *rpos; wpos++; rpos++; } *wpos = 0; } if(*buf) { rpos = buf; while(*rpos) rpos++; rpos--; while(isspace(*rpos)) rpos--; rpos++; *rpos = 0; } } /* parses a field list from include command or from -f option */ /* returns NULL if not fields */ /* comma is assumed to be escaped as \, */ struct include_field * parse_include_list(char *list) { struct include_field *ret = NULL,*c = NULL; char *p = list; char *w = list; char *s = list; if(p == NULL) return NULL; while(*p) { if(*p == ',' && p[1] == ',') { p++; *w = *p; w++; p++; } *w = *p; if(*p == ',' || !p[1]) { if(p[1]) *w = 0; if(strlen(s)) { if(ret == NULL) { ret = xmalloc(sizeof(struct include_field)); c = ret; } else { c->next = xmalloc(sizeof(struct include_field)); c = c->next; } c->next = NULL; c->name = xstrdup(s); c->found = 0; c->reported = 0; } p++; while(*p == ',') p++; s = p; w = s; p = s; } else { w++; p++; } } return ret; } /* reading one logical line, returns the status of the line read */ int read_logical_line(char *buffer, int bufsize) { static char last_eol_char = 0; /* for what did previous read end */ register int prev_char = 0,c; register int i=0,retval = 0; buffer[0] = 0; switch(last_eol_char) { case BLOCK_START: last_eol_char = 0; return LL_BLOCK_START; break; case BLOCK_END: last_eol_char = 0; return LL_BLOCK_END; break; } do { c = getc(fp); if(prev_char == '\\') { switch(c) { case 'a': buffer[i] = '\a'; c = 0; i++; break; case 'b': buffer[i] = '\b'; c = 0; i++; break; case 't': buffer[i] = '\t'; c = 0; i++; break; case 'n': buffer[i] = '\n'; c = 0; i++; break; case 'v': buffer[i] = '\v'; c = 0; i++; break; case 'f': buffer[i] = '\f'; c = 0; i++; break; case 'r': buffer[i] = '\r'; c = 0; i++; break; case '\\': buffer[i] = '\\'; c = 0; i++; break; case COMMENT: buffer[i] = COMMENT; c = 0; i++; break; case '\n': /* newline escaped */ lineno++; buffer[i] = getc(fp); c = 0; i++; break; default: buffer[i] = '\\'; i++; break; } } prev_char = c; switch(c) { case COMMENT: do { c = getc(fp); } while (c != '\n' && c != EOF); /* no break !*/ case '\n': if(c == '\n') lineno++; buffer[i] = 0; retval = LL_OPTION; break; case BLOCK_START: buffer[i] = 0; retval = LL_OPTION; break; case BLOCK_END: buffer[i] = 0; retval = LL_OPTION; break; case EOF: buffer[i] = 0; retval = LL_OPTION; break; case '\\': case '\r': /* skip win32 CR */ case 0: break; default: if(i >= bufsize) panic("rc-file line too long",NULL,NULL); buffer[i] = (char) c; i++; break; } if(retval) { trim(buffer); switch(c) { case '\n': if(buffer[0] == 0) { i = 0; /* empty line */ retval = 0; } else { return retval; } break; case EOF: if(buffer[0] == 0) { retval = LL_EOF; } return retval; case BLOCK_START: if(buffer[0] == 0) { retval = LL_BLOCK_START; } else { last_eol_char = c; } return retval; case BLOCK_END: if(buffer[0] == 0) { retval = LL_BLOCK_END; } else { last_eol_char = c; } return retval; break; } } } while (1); } void error_in_line() { fprintf(stderr,"%s: Error in rcfile, line %d\n",program,lineno); } #define READ_BUF_SIZE 1024 /* reads logical lines and parses a one option */ /* option name and parameters are in values-array */ /* return the number of elements in array */ int parse_option(char *buf) { register char *rpos = buf; char *end = buf; char *param; register char *p; int i = 0,j; int valc = 0; int quoted; values[0] = buf; while(*end) end++; while(!isspace(*rpos) && *rpos) rpos++; *rpos = 0; p = buf; while(*p) // convert _ to - { if(*p == '_') *p = '-'; p++; } while(rc_opts[i].name != NULL && strcmp(rc_opts[i].name,values[0]) != 0) i++; if(rc_opts[i].name != NULL) /* found */ { param = rc_opts[i].parameters; while(rpos < end) { if(*param) { rpos++; while(isspace(*rpos)) rpos++; /* next non space */ quoted = 0; switch(*param) { case 'S': case 's': case 'C': case 'c': if(*rpos) { if(*rpos == '"') { rpos++; quoted = 1; } valc++; values[valc] = rpos; if(*(param + 1)) /* not the last possible paramter */ { if(quoted) { j = 0; while(*rpos != '"' && *rpos) { rpos++; if(*rpos == '"' && *(rpos - 1) == '\\') { j++; *(rpos - j) = *rpos; rpos++; } if(j) *(rpos - j) = *rpos; } if(*rpos != '"') { error_in_line(); panic("Quotation not ended",NULL,NULL); } *(rpos - j) = 0; *rpos = 0; } else { while(!isspace(*rpos) && *rpos) rpos++; *rpos=0; } } else /* last parameter, get the rest of the line */ { j = 0; while(*rpos) { rpos++; if(*rpos == '"') { if(*(rpos - 1) == '\\') { j++; *(rpos - j) = *rpos; rpos++; if(!*rpos) { error_in_line(); panic("Quotation not ended",NULL,NULL); } } else if(*(rpos + 1) && quoted) { error_in_line(); panic("Too many parameters",values[0],NULL); } } if(j && *rpos) *(rpos - j) = *rpos; } if(quoted) { if(*(rpos - 1) != '"') { error_in_line(); panic("Quotation not ended",NULL,NULL); } *(rpos - j - 1) = 0; } else { *(rpos - j) = 0; } *rpos = 0; } if((*param == 'C' || *param == 'c') && values[valc][1]) { error_in_line(); panic("Single character parameter expected",values[0],NULL); } } else { if(*param == 'S' || *param == 'C') { error_in_line(); panic("Mandatory parameter missing",values[0],NULL); } } break; case 'N': case 'n': if(*rpos) { valc++; values[valc] = rpos; if(*rpos == '*' && (isspace(rpos[1]) || !rpos[1])) { rpos++; *rpos=0; } else { while(isdigit(*rpos)) rpos++; if(!isspace(*rpos) && *rpos) { error_in_line(); panic("A number expected",values[0],NULL); } *rpos=0; } } else { if(*param == 'N') { error_in_line(); panic("Mandatory parameter missing",values[0],NULL); } } break; } if(valc > 3) { error_in_line(); panic("Too many parameters",values[0],NULL); } } else { error_in_line(); panic("Too many parameters",values[0],NULL); } param++; } if(isupper(*param)) { error_in_line(); panic("Mandatory parameter missing",values[0],NULL); } } else { error_in_line(); panic("Unknown option",values[0],NULL); } return valc; } /* expand tilde in path */ char * expand_home(char *path) { char *r; char *home = getenv("HOME"); if(*path == '~' && strncmp(&path[1],PATH_SEPARATOR_STRING,strlen(PATH_SEPARATOR_STRING)) == 0 && home != NULL) { r = xmalloc(strlen(home) + strlen(path)); strcpy(r,home); strcat(r,&path[1]); } else { r = xstrdup(path); } return r; } /* read key value pairs from file */ /* to struct lookup data chain */ /* returns pointer to last element */ struct lookup_data * read_lookup_from_file(struct lookup_data **data,char *file,char separator,int *max_key_len) { FILE *fp; register int line_len; size_t max_line_size = 1024; char *efile; char *line; register char *p; struct lookup_data *c_data = *data; efile = expand_home(file); fp = fopen(efile,"r"); if(fp == NULL) { error_in_line(); panic("Cannot open file",efile,strerror(errno)); } line = xmalloc(max_line_size); if(c_data != NULL) while(c_data->next != NULL) c_data = c_data->next; do { #ifdef HAVE_GETLINE line_len = getline(&line,&max_line_size,fp); #else if(fgets(line,max_line_size,fp) == NULL) { line_len = -1; } else { line_len = strlen(line); } #endif if(line_len > 0) { switch(line[line_len - 1]) // remove newline { case '\n': case '\r': line[line_len - 1] = 0; break; } p = line; while(*p && *p != separator) p++; if(*p) { *p = 0; p++; if(c_data == NULL) { *data = xmalloc(sizeof(struct lookup_data)); c_data = *data; } else { c_data->next = xmalloc(sizeof(struct lookup_data)); c_data = c_data->next; } c_data->next = NULL; c_data->key = xstrdup(line); c_data->value = xstrdup(p); c_data->key_len = strlen(c_data->key); if(*max_key_len < c_data->key_len) *max_key_len = c_data->key_len; } } } while(line_len != -1); fclose(fp); free(line); free(efile); return c_data; } /* parse status values */ #define PS_MAIN 1 #define PS_STRUCT 2 #define PS_RECORD 3 #define PS_OUTPUT 4 #define PS_W_STRUCT 5 #define PS_W_RECORD 6 #define PS_W_OUTPUT 7 #define PS_LOOKUP 8 #define PS_W_LOOKUP 9 void print_s() { struct structure *s = structure; struct record *r; struct field *f; while(s != NULL) { printf("%s\n",s->name); r = s->r; while(r != NULL) { f = r->f; printf(" %s\n",r->name); while(f != NULL) { printf(" %s %d\n",f->name,f->length); f = f->next; } r = r->next; } s = s->next; } } void parserc(char *rcfile,char *include_field_list) { struct structure *c_structure = structure; struct field *c_field = NULL; struct id *c_id = NULL; struct record *c_record = NULL; struct output *c_output = output; struct lookup *c_lookup = NULL; struct lookup_data *c_lookup_data = NULL; struct include_field *fl = parse_include_list(include_field_list); char *read_buffer; int line_status; int status = PS_MAIN; int opt_count; open_rc_file(rcfile); read_buffer = xmalloc(READ_BUF_SIZE); if(c_structure != NULL) while(c_structure->next != NULL) c_structure = c_structure->next; if(c_output != NULL) while(c_output->next != NULL) c_output = c_output->next; while((line_status = read_logical_line(read_buffer,READ_BUF_SIZE)) != LL_EOF) { switch(line_status) { case LL_OPTION: opt_count = parse_option(read_buffer); switch(status) { case PS_MAIN: if(strcmp(values[0],N_STRUCTURE) == 0) { if(structure == NULL) { c_structure = xmalloc(sizeof(struct structure)); structure = c_structure; } else { c_structure->next = xmalloc(sizeof(struct structure)); c_structure = c_structure->next; } c_structure->next = NULL; c_structure->name = xstrdup(values[1]); c_structure->type[0] = FIXED_LENGTH; c_structure->quote = 0; c_structure->header = 0; c_structure->output_name = DEFALT_OUTPUT; c_structure->vote = 0; c_structure->o = NULL; c_structure->r = NULL; status = PS_W_STRUCT; } else if(strcmp(values[0],N_OUTPUT) == 0) { if(c_output == NULL) { c_output = xmalloc(sizeof(struct output)); output = c_output; } else { c_output->next = xmalloc(sizeof(struct output)); c_output = c_output->next; } c_output->next = NULL; c_output->name = xstrdup(values[1]); c_output->file_header = NULL; c_output->file_trailer = NULL; c_output->header = NULL; c_output->data = "%d"; c_output->lookup = NULL; c_output->separator = NULL; c_output->record_header = NULL; c_output->record_trailer = "\n"; c_output->justify = LEFT_JUSTIFY; c_output->indent = NULL; c_output->no_data = 1; c_output->empty_chars = " \f\n\r\t\v"; c_output->print_empty = 1; if(fl != NULL) { c_output->fl = fl; } else { c_output->fl = NULL; } status = PS_W_OUTPUT; } else if(strcmp(values[0],N_LOOKUP) == 0) { if(c_lookup == NULL) { c_lookup = xmalloc(sizeof(struct lookup)); lookup = c_lookup; } else { c_lookup->next = xmalloc(sizeof(struct lookup)); c_lookup = c_lookup->next; } c_lookup->next = NULL; c_lookup->name = xstrdup(values[1]); c_lookup->type = EXACT; c_lookup->default_value = ""; c_lookup->max_key_len = 0; c_lookup->data = NULL; status = PS_W_LOOKUP; } else if(strcmp(values[0],N_CONST) == 0) { if(const_field == NULL) { c_field = xmalloc(sizeof(struct field)); const_field = c_field; } else { c_field = const_field; while(c_field->next != NULL) c_field = c_field->next; c_field->next = xmalloc(sizeof(struct field)); c_field = c_field->next; } c_field->lookup_table_name = NULL; c_field->lookup = NULL; c_field->rep = NULL; c_field->next = NULL; c_field->name = xstrdup(values[1]); c_field->const_data = xstrdup(values[2]); c_field->position = 0; c_field->length = strlen(c_field->const_data); } else { error_in_line(); panic("Option not inside structure, output or lookup",values[0],NULL); } break; case PS_STRUCT: if(strcmp(values[0],N_TYPE) == 0) { if(strcmp(values[1],"fixed") == 0) { c_structure->type[0] = FIXED_LENGTH; if(opt_count > 1) { error_in_line(); panic("too many parameters for",values[0],NULL); } } else if(strcmp(values[1],"separated") == 0) { c_structure->type[0] = SEPARATED; c_structure->type[1] = ','; c_structure->type[2] = 0; if(opt_count > 1) { c_structure->type[1] = values[2][0]; } if(opt_count > 2) { if(values[3][0] == '*') { c_structure->type[2] = values[3][0]; } else { error_in_line(); panic("An \'*\' is expected",values[0],NULL); } } } else { error_in_line(); panic("Unknown type",NULL,NULL); } } else if(strcmp(values[0],N_HEADER) == 0) { if(strcmp(values[1],"first") == 0) { c_structure->header = HEADER_FIRST; } else if(strcmp(values[1],"all") == 0) { c_structure->header = HEADER_ALL; } else if(strcmp(values[1],"no") == 0) { c_structure->header = 0; } else { error_in_line(); panic("first, all or no expected",NULL,NULL); } } else if(strcmp(values[0],N_OUTPUT) == 0) { c_structure->output_name = xstrdup(values[1]); } else if(strcmp(values[0],N_RECORD) == 0) { if(c_structure->r == NULL) { c_record = xmalloc(sizeof(struct record)); c_structure->r = c_record; } else { c_record->next = xmalloc(sizeof(struct record)); c_record = c_record->next; } c_record->next = NULL; c_record->name = xstrdup(values[1]); c_record->i = NULL; c_record->f = NULL; c_record->fields_from = NULL; c_record->o = NULL; c_record->output_name = NULL; c_record->vote = 0; status = PS_W_RECORD; } else if(strcmp(values[0],N_QUOTE) == 0) { if(opt_count > 0) { c_structure->quote = values[1][0]; } else { c_structure->quote = '"'; } } else { error_in_line(); panic("Unknown option in structure",NULL,NULL); } break; case PS_RECORD: if(strcmp(values[0],N_ID) == 0) { if(c_record->i == NULL) { c_id = xmalloc(sizeof(struct id)); c_record->i = c_id; } else { c_id->next = xmalloc(sizeof(struct id)); c_id = c_id->next; } c_id->next = NULL; if(sscanf(values[1],"%d",&c_id->position) != 1) { error_in_line(); panic("Error in number",NULL,NULL); } if(c_id->position < 1) { error_in_line(); panic("Position must be greater than zero",NULL,NULL); } c_id->key = xstrdup(values[2]); } else if(strcmp(values[0],N_FIELD) == 0) { if(c_record->f == NULL) { c_field = xmalloc(sizeof(struct field)); c_record->f = c_field; } else { c_field->next = xmalloc(sizeof(struct field)); c_field = c_field->next; } c_field->lookup_table_name = NULL; c_field->lookup = NULL; c_field->rep = NULL; c_field->next = NULL; c_field->const_data = NULL; c_field->length = 0; if(values[1][0] == '*' && !values[1][1]) { c_field->name = NULL; } else { c_field->name = xstrdup(values[1]); } if(opt_count > 1) { if(!(values[2][0] == '*' && !values[2][1])) { if(sscanf(values[2],"%d",&c_field->length) != 1) { error_in_line(); panic("Error in number",NULL,NULL); } } if(opt_count > 2) { c_field->lookup_table_name = xstrdup(values[3]); } } } else if(strcmp(values[0],N_FIELDSFROM) == 0) { c_record->fields_from = xstrdup(values[1]); } else if(strcmp(values[0],N_OUTPUT) == 0) { c_record->output_name = xstrdup(values[1]); } else { error_in_line(); panic("Unknown option in record",NULL,NULL); } break; case PS_OUTPUT: if(strcmp(values[0],N_FILE_HEADER) == 0) { c_output->file_header = xstrdup(values[1]); } else if(strcmp(values[0],N_FILE_TRAILER) == 0) { c_output->file_trailer = xstrdup(values[1]); } else if(strcmp(values[0],N_HEADER) == 0) { c_output->header = xstrdup(values[1]); } else if(strcmp(values[0],N_DATA) == 0) { c_output->data = xstrdup(values[1]); } else if (strcmp(values[0],N_LOOKUP) == 0) { c_output->lookup = xstrdup(values[1]); } else if(strcmp(values[0],N_SEPARATOR) == 0) { c_output->separator = xstrdup(values[1]); } else if(strcmp(values[0],N_RECORD_HEADER) == 0) { c_output->record_header = xstrdup(values[1]); } else if(strcmp(values[0],N_RECORD_TRAILER) == 0) { c_output->record_trailer = xstrdup(values[1]); } else if(strcmp(values[0],N_JUSTIFY) == 0) { if(!values[1][1]) { c_output->justify = values[1][0]; } else if(strcmp(values[1],"right") == 0) { c_output->justify = RIGHT_JUSTIFY; } else if(strcmp(values[1],"left") == 0) { c_output->justify = LEFT_JUSTIFY; } else { error_in_line(); panic("Unknown values in justify",NULL,NULL); } } else if(strcmp(values[0],N_PRINT_NO_DATA) == 0) { if(strcmp(values[1],"yes") == 0) { c_output->no_data = 1; } else if(strcmp(values[1],"no") == 0) { c_output->no_data = 0; } else { error_in_line(); panic("Unknown values in print-no-data",NULL,NULL); } } else if(strcmp(values[0],N_INDENT) == 0) { c_output->indent = xstrdup(values[1]); } else if(strcmp(values[0],N_FIELDLIST) == 0) { if(c_output->fl == NULL) c_output->fl = parse_include_list(values[1]); } else if(strcmp(values[0],N_FIELD_EMPTY_PRINT) == 0) { if(strcmp(values[1],"yes") == 0) { c_output->print_empty = 1; } else if(strcmp(values[1],"no") == 0) { c_output->print_empty = 0; } else { error_in_line(); panic("Unknown values in field-empty-print",NULL,NULL); } } else if(strcmp(values[0],N_EMPTY_CHARS) == 0) { c_output->empty_chars = xstrdup(values[1]); } else { error_in_line(); panic("Unknown option in output definition",NULL,NULL); } break; case PS_LOOKUP: if(strcmp(values[0],N_SEARCH) == 0) { if(strcmp(values[1],"exact") == 0) { c_lookup->type = EXACT; } else if(strcmp(values[1],"longest") == 0) { c_lookup->type = LONGEST; } else { error_in_line(); panic("Unknown value for lookup tables search option",values[1],NULL); } } else if(strcmp(values[0],N_PAIR) == 0) { if(c_lookup->data == NULL) { c_lookup->data = xmalloc(sizeof(struct lookup_data)); c_lookup_data = c_lookup->data; } else { c_lookup_data->next = xmalloc(sizeof(struct lookup_data)); c_lookup_data = c_lookup_data->next; } c_lookup_data->next = NULL; c_lookup_data->key = xstrdup(values[1]); c_lookup_data->value = xstrdup(values[2]); c_lookup_data->key_len = strlen(c_lookup_data->key); if(c_lookup->max_key_len < c_lookup_data->key_len) c_lookup->max_key_len = c_lookup_data->key_len; } else if(strcmp(values[0],N_FILE) == 0) { if(opt_count == 1) { c_lookup_data = read_lookup_from_file(&(c_lookup->data),values[1],';',&c_lookup->max_key_len); } else { c_lookup_data = read_lookup_from_file(&(c_lookup->data),values[1],values[2][0],&c_lookup->max_key_len); } } else if(strcmp(values[0],N_DEFAULT) == 0) { c_lookup->default_value = xstrdup(values[1]); } else { error_in_line(); panic("Unknown option for lookup",values[0],NULL); } break; case PS_W_RECORD: case PS_W_OUTPUT: case PS_W_STRUCT: case PS_W_LOOKUP: error_in_line(); panic("{ expected, found",values[0],NULL); break; } break; case LL_BLOCK_START: switch(status) { case PS_W_STRUCT: status = PS_STRUCT; break; case PS_W_OUTPUT: status = PS_OUTPUT; break; case PS_W_RECORD: status = PS_RECORD; break; case PS_W_LOOKUP: status = PS_LOOKUP; break; default: error_in_line(); panic("{ not expected",NULL,NULL); break; } break; case LL_BLOCK_END: switch(status) { case PS_STRUCT: case PS_OUTPUT: case PS_LOOKUP: status = PS_MAIN; break; case PS_RECORD: status = PS_STRUCT; break; default: error_in_line(); panic("} not expected",NULL,NULL); } break; } } if(status != PS_MAIN) { panic("End of file reached before closing }",NULL,NULL); } free(read_buffer); fclose(fp); }