/********************************************************************** ... ***********************************************************************/ const char *inf_token(struct inputfile *inf, enum inf_token_type type) { const char *c; const char *name; get_token_fn_t func; assert_sanity(inf); assert(type>=INF_TOK_FIRST && type<INF_TOK_LAST); name = tok_tab[type].name ? tok_tab[type].name : "(unnamed)"; func = tok_tab[type].func; if (!func) { freelog(LOG_ERROR, "token type %d (%s) not supported yet", type, name); c = NULL; } else { if (!have_line(inf)) (void) read_a_line(inf); if (!have_line(inf)) { c = NULL; } else { c = func(inf); } } if (c && INF_DEBUG_FOUND) { freelog(LOG_DEBUG, "inputfile: found %s '%s'", name, inf->token.str); } return c; }
/********************************************************************** Close the file and free associated memory, but don't recurse included_from files, and don't free the actual memory where the inf record is stored (ie, the memory where the users pointer points to). This is used when closing an included file. ***********************************************************************/ static void inf_close_partial(struct inputfile *inf) { assert_sanity(inf); freelog(LOG_DEBUG, "inputfile: sub-closing \"%s\"", inf_filename(inf)); if (fz_ferror(inf->fp) != 0) { freelog(LOG_ERROR, "Error before closing %s: %s", inf_filename(inf), fz_strerror(inf->fp)); fz_fclose(inf->fp); inf->fp = NULL; } else if (fz_fclose(inf->fp) != 0) { freelog(LOG_ERROR, "Error closing %s", inf_filename(inf)); } if (inf->filename) { free(inf->filename); } inf->filename = NULL; astr_free(&inf->cur_line); astr_free(&inf->copy_line); astr_free(&inf->token); astr_free(&inf->partial); /* assign zeros for safety if accidently re-use etc: */ init_zeros(inf); inf->magic = ~INF_MAGIC; freelog(LOG_DEBUG, "inputfile: sub-closed ok"); }
/********************************************************************** Give a detailed log message, including information on current line number etc. Message can be NULL: then just logs information on where we are in the file. ***********************************************************************/ void inf_log(struct inputfile *inf, int loglevel, const char *message) { assert_sanity(inf); if (message) { freelog(loglevel, "%s", message); } freelog(loglevel, " file \"%s\", line %d, pos %d%s", inf_filename(inf), inf->line_num, inf->cur_line_pos, (inf->at_eof ? ", EOF" : "")); if (inf->cur_line.str && inf->cur_line.n > 0) { freelog(loglevel, " looking at: '%s'", inf->cur_line.str+inf->cur_line_pos); } if (inf->copy_line.str && inf->copy_line.n > 0) { freelog(loglevel, " original line: '%s'", inf->copy_line.str); } if (inf->in_string) { freelog(loglevel, " processing string starting at line %d", inf->string_start_line); } while ((inf=inf->included_from)) { /* local pointer assignment */ freelog(loglevel, " included from file \"%s\", line %d", inf_filename(inf), inf->line_num); } }
/********************************************************************** Close the file and free associated memory, included any partially recursed included files, and the memory allocated for 'inf' itself. Should only be used on an actually open inputfile. After this, the pointer should not be used. ***********************************************************************/ void inf_close(struct inputfile *inf) { assert_sanity(inf); freelog(LOG_DEBUG, "inputfile: closing \"%s\"", inf_filename(inf)); if (inf->included_from) { inf_close(inf->included_from); } inf_close_partial(inf); free(inf); freelog(LOG_DEBUG, "inputfile: closed ok"); }
/********************************************************************** Check sensible values for an opened inputfile. ***********************************************************************/ static void assert_sanity(struct inputfile *inf) { assert(inf != NULL); assert(inf->magic==INF_MAGIC); assert(inf->fp != NULL); assert(inf->line_num >= 0); assert(inf->cur_line_pos >= 0); assert(inf->at_eof == FALSE || inf->at_eof == TRUE); assert(inf->in_string == FALSE || inf->in_string == TRUE); #ifdef DEBUG assert(inf->string_start_line >= 0); assert(inf->cur_line.n >= 0); assert(inf->copy_line.n >= 0); assert(inf->token.n >= 0); assert(inf->partial.n >= 0); assert(inf->cur_line.n_alloc >= 0); assert(inf->copy_line.n_alloc >= 0); assert(inf->token.n_alloc >= 0); assert(inf->partial.n_alloc >= 0); if(inf->included_from) { assert_sanity(inf->included_from); } #endif }
void datum_sequence::block_keyword_values(bool z) { keyword_values_are_blocked_ = z; assert_sanity(); }
datum_sequence::datum_sequence(std::string const& s) :datum_string(s) ,keyword_values_are_blocked_(false) { assert_sanity(); }
datum_sequence::datum_sequence() :keyword_values_are_blocked_(false) { assert_sanity(); }
/********************************************************************** Read a new line into cur_line; also copy to copy_line. Increments line_num and cur_line_pos. Returns 0 if didn't read or other problem: treat as EOF. Strips newline from input. ***********************************************************************/ static bool read_a_line(struct inputfile *inf) { struct astring *line; char *ret; int pos; assert_sanity(inf); if (inf->at_eof) return FALSE; /* abbreviation: */ line = &inf->cur_line; /* minimum initial line length: */ astr_minsize(line, 80); pos = 0; /* don't print "orig line" in warnings until we have it: */ inf->copy_line.n = 0; /* Read until we get a full line: * At start of this loop, pos is index to trailing null * (or first position) in line. */ for(;;) { ret = fz_fgets(line->str + pos, line->n_alloc - pos, inf->fp); if (!ret) { /* fgets failed */ inf->at_eof = TRUE; if (inf->in_string) { /* Note: Don't allow multi-line strings to cross "include" * boundaries */ inf_log(inf, LOG_ERROR, "Multi-line string went to end-of-file"); return FALSE; } break; } pos += strlen(line->str + pos); line->n = pos + 1; if (line->str[pos-1] == '\n') { line->str[pos-1] = '\0'; line->n--; break; } if (line->n != line->n_alloc) { freelog(LOG_VERBOSE, "inputfile: expect missing newline at EOF"); } astr_minsize(line, line->n*2); } inf->line_num++; inf->cur_line_pos = 0; astr_minsize(&inf->copy_line, inf->cur_line.n + ((inf->cur_line.n == 0) ? 1 : 0)); strcpy(inf->copy_line.str, inf->cur_line.str); if (check_include(inf)) { return read_a_line(inf); } if (inf->at_eof) { line->str[0] = '\0'; line->n = 0; if (inf->included_from) { /* Pop the include, and get next line from file above instead. */ struct inputfile *inc = inf->included_from; inf_close_partial(inf); *inf = *inc; /* so the user pointer in still valid * (and inf pointers in calling functions) */ free(inc); return read_a_line(inf); } return FALSE; } else { return TRUE; } }
/********************************************************************** Check for an include command, which is an isolated line with: *include "filename" If a file is included via this mechanism, returns 1, and sets up data appropriately: (*inf) will now correspond to the new file, which is opened but no data read, and inf->included_from is set to newly malloced memory which corresponds to the old file. ***********************************************************************/ static bool check_include(struct inputfile *inf) { const char *include_prefix = "*include"; static size_t len = 0; char *bare_name, *full_name, *c; struct inputfile *new_inf, temp; if (len==0) { len = strlen(include_prefix); } assert_sanity(inf); if (inf->in_string || inf->cur_line.n <= len || inf->cur_line_pos > 0) { return FALSE; } if (strncmp(inf->cur_line.str, include_prefix, len)!=0) { return FALSE; } /* from here, the include-line must be well formed or we die */ /* keep inf->cur_line_pos accurate just so error messages are useful */ /* skip any whitespace: */ inf->cur_line_pos = len; c = inf->cur_line.str + len; while (*c != '\0' && my_isspace(*c)) c++; if (*c != '\"') { inf_log(inf, LOG_ERROR, "Did not find opening doublequote for '*include' line"); return FALSE; } c++; inf->cur_line_pos = c - inf->cur_line.str; bare_name = c; while (*c != '\0' && *c != '\"') c++; if (*c != '\"') { inf_log(inf, LOG_ERROR, "Did not find closing doublequote for '*include' line"); return FALSE; } *c++ = '\0'; inf->cur_line_pos = c - inf->cur_line.str; /* check rest of line is well-formed: */ while (*c != '\0' && my_isspace(*c) && !is_comment(*c)) c++; if (!(*c=='\0' || is_comment(*c))) { inf_log(inf, LOG_ERROR, "Junk after filename for '*include' line"); return FALSE; } inf->cur_line_pos = inf->cur_line.n-1; full_name = inf->datafn(bare_name); if (!full_name) { freelog(LOG_ERROR, "Could not find included file \"%s\"", bare_name); return FALSE; } /* avoid recursion: (first filename may not have the same path, but will at least stop infinite recursion) */ { struct inputfile *inc = inf; do { if (inc->filename && strcmp(full_name, inc->filename)==0) { freelog(LOG_ERROR, "Recursion trap on '*include' for \"%s\"", full_name); return FALSE; } } while((inc=inc->included_from)); } new_inf = inf_from_file(full_name, inf->datafn); /* Swap things around so that memory pointed to by inf (user pointer, and pointer in calling functions) contains the new inputfile, and newly allocated memory for new_inf contains the old inputfile. This is pretty scary, lets hope it works... */ temp = *new_inf; *new_inf = *inf; *inf = temp; inf->included_from = new_inf; return TRUE; }
/********************************************************************** ... ***********************************************************************/ bool inf_at_eof(struct inputfile *inf) { assert_sanity(inf); return inf->at_eof; }
/********************************************************************** Return 1 if current pos is at end of current line. ***********************************************************************/ static bool at_eol(struct inputfile *inf) { assert_sanity(inf); assert(inf->cur_line_pos <= inf->cur_line.n); return (inf->cur_line_pos >= inf->cur_line.n - 1); }
/********************************************************************** Return 1 if have data for current line. ***********************************************************************/ static bool have_line(struct inputfile *inf) { assert_sanity(inf); return (inf->cur_line.n > 0); }