/*
* freescope - Free source browser
* Copyright (C) 2001 Olivier Deme
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*
*/
// FILE: dbinf.cpp
/************/
/* INCLUDES */
/************/
#include <algorithm>
#include "dbinf.h"
#include "trace.h"
#include "defs.h"
/********************/
/* GLOBAL VARIABLES */
/********************/
Add2Db add2db;
// Watch out, there shouldn't be any symbol equals to 0!
symbol_type_t DEFINITION = 1;
symbol_type_t FUNC_CALLED = 2;
symbol_type_t FUNC_CALL = 3;
symbol_type_t ASSIGNMENT = 4;
symbol_type_t INCLUDE = 5;
symbol_type_t ANY = 6;
char* GLOBAL_SCOPE = "`GLOBAL";
size_t GLOBAL_SCOPE_LENGTH = 7;
/**************************/
/* STATIC INITIALIZATIONS */
/**************************/
DbInf* DbInf::m_instance = 0;
const size_t DbInf::RECORD_LENGTH = sizeof(symbol_code_t) +
sizeof(symbol_type_t) +
sizeof(symbol_code_t) +
sizeof(line_nbr_t);
const size_t DbInf::BUFFER_SIZE = 500 * DbInf::RECORD_LENGTH;
const DbInf::symbol_code_t DbInf::SYMBOL_NEW_FILE = 0;
const DbInf::symbol_code_t DbInf::SYMBOL_FREE_RECORD = 1;
/***********************/
/* FUNCTION DEFINTIONS */
/***********************/
/*
* FUNCTION: DbInf::DbInf
*
* DESCRIPTION: Constructor
*
* IN:
* IN-OUT:
* RETURN CODE:
*/
DbInf::DbInf ()
: m_db_modified(0),
m_do_existing_file(false)
{
add2db = DbInf::add_entry;
m_buffer.reserve(BUFFER_SIZE);
}
/*
* FUNCTION: DbInf::~DbInf
*
* DESCRIPTION: Destructor
*
* IN:
* IN-OUT:
* RETURN CODE:
*/
DbInf::~DbInf ()
{
// Close the file
m_db_file.close();
}
/*
* FUNCTION: DbInf::instance
*
* DESCRIPTION: Return a pointer to the unique instance of this class.
*
* IN:
* IN-OUT:
* RETURN CODE: A pointer to the instance
*/
DbInf* DbInf::instance()
{
if (m_instance == NULL)
m_instance = new DbInf;
return m_instance;
}
/*
* FUNCTION: DbInf::open
*
* DESCRIPTION: This function opens a database file. If the file does not exist,
* it is created, otherwise the symbol table and file table are
* loaded into the multimap attributes.
*
* IN: filename The name of the database file to open
* IN-OUT:
* RETURN CODE:
*/
void DbInf::open (const string& filename)
{
// Open the file
m_db_file.open(filename.c_str(), ios::in | ios::out | ios::binary);
if (m_db_file == 0)
{
LOG(ALL, ERROR) << "Failed to open database file" << endl;
return;
}
m_symbol_code[GLOBAL_SCOPE] = SYMBOL_FREE_RECORD + 1;
m_code_symbol[SYMBOL_FREE_RECORD + 1] = GLOBAL_SCOPE;
// Check if file is empty
m_db_file.seekg(0, ios::end);
if (m_db_file.tellg() == 0)
{
m_db_file.seekg(0, ios::beg);
m_db_modified = true;
return; // File newly created
}
m_do_existing_file = true;
// Load symbol table
if (load_symbols() != 0)
{
LOG(ALL, ERROR) << "Failed to load file table" << endl;
return;
}
// Load the file table
if (load_files() != 0)
{
LOG(ALL, ERROR) << "Failed to load file table" << endl;
return;
}
m_db_file.seekg(m_st_lower, ios::beg);
}
/*
* FUNCTION: DbInf::load_symbols
*
* DESCRIPTION: This function reads the symbol table from the database file.
* IN:
* IN-OUT:
* RETURN CODE: 0 on success. 1 otherwise
*/
int DbInf::load_symbols ()
{
file_ptr_t cur;
vector<Uint8> buffer(BUFFER_SIZE);
vector<Uint8>::iterator it;
// Get symbol table boundaries
m_db_file.seekg((-4) * (int)sizeof(file_ptr_t), ios::end);
m_db_file.read(&m_st_lower, sizeof(file_ptr_t));
m_db_file.read(&m_st_upper, sizeof(file_ptr_t));
// Go to start of symbol table
m_db_file.seekg(m_st_lower, ios::beg);
cur = m_st_lower;
// Populate symbol table
while (cur < m_st_upper)
{
file_ptr_t old_cur;
old_cur = cur;
// Fill the buffer
if (m_st_upper - cur <= BUFFER_SIZE)
{
m_db_file.read(&buffer[0], m_st_upper - cur);
buffer.resize(m_st_upper - cur);
cur += m_st_upper - cur;
}
else
{
m_db_file.read(&buffer[0], BUFFER_SIZE);
buffer.resize(BUFFER_SIZE);
cur += BUFFER_SIZE;
}
it = buffer.begin();
while (it != buffer.end())
{
size_t i;
vector<char>::iterator it2;
string symbol_name;
symbol_code_t symbol_code;
// Find end of symbol
it2 = (char*)(::find(it, buffer.end(), '\0'));
if (it2 == (char*)buffer.end())
{
cur = cur - (buffer.end() - it);
m_db_file.seekg(cur, ios::beg);
break;
}
symbol_name.assign((char*)it, it2 - (char*)it);
// Do we have enough characters in the buffer to process one code?
if (buffer.end() - (Uint8*)it2 < (int)sizeof(symbol_code_t))
{
cur = cur - (buffer.end() - it);
m_db_file.seekg(cur, ios::beg);
break;
}
it = (Uint8*)it2 + 1;
symbol_code = 0;
// Get symbol code
for (i = 0; i < sizeof(symbol_code); ++i)
{
symbol_code |= ((symbol_code_t)*it) << (8 * i);
++it;
}
m_symbol_code[symbol_name] = symbol_code;
m_code_symbol[symbol_code] = symbol_name;
}
}
return 0;
}
/*
* FUNCTION: DbInf::load_files
*
* DESCRIPTION: This function reads the file table from the database file.
* IN:
* IN-OUT:
* RETURN CODE: 0 on success. 1 otherwise
*/
int DbInf::load_files ()
{
string filename;
time_t time_stamp;
// Get symbol table boundaries
m_db_file.seekg((-2) * (int)sizeof(file_ptr_t), ios::end);
m_db_file.read(&m_ft_lower , sizeof(file_ptr_t));
m_db_file.read(&m_ft_upper, sizeof(file_ptr_t));
// Go to start of symbol table
m_db_file.seekg(m_ft_lower, ios::beg);
// Populate symbol table
while (m_db_file.tellg() <= (streampos)m_ft_upper)
{
getline(m_db_file, filename, '\0');
m_files.push_front(filename);
m_db_file.read(&time_stamp, sizeof(time_t));
m_time_stamps[filename] = time_stamp;
}
m_files_copy = m_files;
return 0;
}
/*
* FUNCTION: DbInf::add_entry
*
* DESCRIPTION: This function adds an entry in the database. It should be
* called directly from a parser module.
*
* IN: type The symbol type
* symbol The symbol name
* symbol_length The length of the symbol
* scope The scope in which the symbol appears
* scope_length The length of the scope
* file The file containing the symbol
* lineno The line nbr where is the symbol
*
* IN-OUT:
* RETURN CODE: 0 on success. 1 on error
*/
int DbInf::add_entry (const symbol_type_t type,
const char* symbol,
const size_t symbol_length,
const char* scope,
const size_t scope_length,
const line_nbr_t lineno)
{
static string tmp_symbol;
static string tmp_scope;
symbol_code_t symbol_code;
symbol_code_t scope_code;
symbol_code_t symbol_code_mask;
symbol_type_t type_mask;
line_nbr_t line_mask;
size_t i;
Uint8 my_byte;
if (m_instance->m_db_modified == false)
m_instance->m_db_modified = true;
// Get the symbol code from the symbol table
tmp_symbol.erase();
tmp_symbol.append(symbol, symbol_length);
if ((symbol_code = m_instance->m_symbol_code[tmp_symbol]) == 0)
{
// Symbol is not in symbol table. Add it.
symbol_code = (symbol_code_t)m_instance->m_symbol_code.size() + 2;
m_instance->m_symbol_code[tmp_symbol] = symbol_code;
m_instance->m_code_symbol[symbol_code] = tmp_symbol;
}
// Get the scope code from the symbol table
tmp_scope.erase();
tmp_scope.append(scope, scope_length);
scope_code = m_instance->m_symbol_code[tmp_scope];
if (m_instance->m_do_existing_file == true)
{
// This is a file being updated.
if (m_instance->m_old_file != m_instance->m_file ||
m_instance->m_buffer.size() >= BUFFER_SIZE)
{
// Flush the buffer if needed
if (m_instance->m_buffer.size() != 0)
{
if (m_instance->m_file != m_instance->m_old_file)
{
string tmp;
tmp = m_instance->m_file;
m_instance->m_file = m_instance->m_old_file;
m_instance->update_records();
m_instance->m_file = tmp;
}
else
m_instance->update_records();
}
m_instance->m_old_file = m_instance->m_file;
}
// Add the record in the buffer
for (i = 0; i < sizeof(symbol_code); ++i)
{
symbol_code_mask = 0xFF << (8*i);
my_byte = (Uint8)((symbol_code & symbol_code_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
for (i = 0; i < sizeof(type); ++i)
{
type_mask = 0xFF << (8*i);
my_byte = (Uint8)((type & type_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
for (i = 0; i < sizeof(scope_code); ++i)
{
symbol_code_mask = 0xFF << (8*i);
my_byte = (Uint8)((scope_code & symbol_code_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
for (i = 0; i < sizeof(lineno); ++i)
{
line_mask = 0xFF << (8*i);
my_byte = (Uint8)((lineno & line_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
}
else
{
if (m_instance->m_old_file != m_instance->m_file)
{
symbol_code_t null_code = 0;
// Flush the buffer
if (m_instance->m_db_file.write(&m_instance->m_buffer[0],
m_instance->m_buffer.size()) == 0)
{
abort();
}
m_instance->m_buffer.clear();
m_instance->m_st_lower = m_instance->m_db_file.tellg();
// Write file name.
if (m_instance->m_db_file.write(&null_code, sizeof(symbol_code_t))
== 0)
{
abort();
}
m_instance->m_db_file << m_instance->m_file.c_str() << '\0';
m_instance->m_old_file = m_instance->m_file;
}
// Add a record in the buffer
for (i = 0; i < sizeof(symbol_code); ++i)
{
symbol_code_mask = 0xFF << (8*i);
my_byte = (Uint8)((symbol_code & symbol_code_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
for (i = 0; i < sizeof(type); ++i)
{
type_mask = 0xFF << (8*i);
my_byte = (Uint8)((type & type_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
for (i = 0; i < sizeof(scope_code); ++i)
{
symbol_code_mask = 0xFF << (8*i);
my_byte = (Uint8)((scope_code & symbol_code_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
for (i = 0; i < sizeof(lineno); ++i)
{
line_mask = 0xFF << (8*i);
my_byte = (Uint8)((lineno & line_mask) >> (8*i));
m_instance->m_buffer.push_back(my_byte);
}
// Do we need to flush the buffer?
if (m_instance->m_buffer.size() >= BUFFER_SIZE)
{
if (m_instance->m_db_file.write(&m_instance->m_buffer[0],
m_instance->m_buffer.size()) == 0)
{
abort();
}
m_instance->m_buffer.clear();
m_instance->m_st_lower = m_instance->m_db_file.tellg();
}
}
return 0;
}
/*
* FUNCTION: DbInf::find
*
* DESCRIPTION: Find a series of matches for a given symbol and a given type
*
* IN: symbol The symbol
* type The type of the symbol
* IN-OUT: result A queueue object of Entry objects that will
* contain the matches
* RETURN CODE:
*/
void DbInf::find (const string& symbol,
const symbol_type_t type,
list<match_t>& result)
{
match_t match;
static string match_file;
symbol_code_t symbol_code;
vector<Uint8> buffer(BUFFER_SIZE);
file_ptr_t cur = 0;
if (result.empty() == false)
result.erase(result.begin(), result.end());
// Get the code associated with the symbol.
if (m_symbol_code.find(symbol) == m_symbol_code.end())
return;
symbol_code = m_symbol_code[symbol];
// Iterate the database records.
m_db_file.seekg(0, ios::beg);
while(cur < m_st_lower)
{
symbol_type_t type2;
symbol_code_t symbol_code2;
symbol_code_t scope_code;
vector<Uint8>::iterator it;
// Fill the buffer
if (m_st_lower - cur <= BUFFER_SIZE)
{
m_db_file.read(&buffer[0], m_st_lower - cur);
buffer.resize(m_st_lower - cur);
cur = m_st_lower;
}
else
{
m_db_file.read(&buffer[0], BUFFER_SIZE);
buffer.resize(BUFFER_SIZE);
cur += BUFFER_SIZE;
}
it = buffer.begin();
while (it != buffer.end())
{
size_t i;
// Do we have enough characters in the buffer to process one record?
if (buffer.end() - it < (int)RECORD_LENGTH)
{
cur = cur - (buffer.end() - it);
m_db_file.seekg(cur, ios::beg);
break;
}
symbol_code2 = 0;
for (i = 0; i < sizeof(symbol_code2); ++i)
{
symbol_code2 |= ((symbol_code_t)*it) << (8 * i);
++it;
}
if (symbol_code2 == SYMBOL_NEW_FILE)
{
// This is a new file
vector<char>::iterator it2;
it2 = (char*)(::find(it, buffer.end(), '\0'));
if (it2 == (char*)buffer.end())
{
cur = cur - (buffer.end() - it) - sizeof(symbol_code_t);
m_db_file.seekg(cur, ios::beg);
break;
}
match_file.assign((char*)it, it2 - (char*)it);
it = (Uint8*)it2 + 1;
continue;
}
if ((symbol_code2 != symbol_code && type != FUNC_CALLED) ||
(symbol_code2 == SYMBOL_FREE_RECORD))
{
it += RECORD_LENGTH - sizeof(symbol_code2);
continue;
}
type2 = (symbol_type_t)*it;
++it;
if ((type != type2 && type != ANY && type != FUNC_CALLED) ||
(type == FUNC_CALLED && type2 != FUNC_CALL))
{
it += RECORD_LENGTH - sizeof(symbol_code2) - sizeof(type);
continue;
}
// Get scope
scope_code = 0;
for (i = 0; i < sizeof(scope_code); ++i)
{
scope_code |= ((symbol_code_t)*it) << (8 * i);
++it;
}
match.scope = m_code_symbol[scope_code];
if ((type == DEFINITION && match.scope != GLOBAL_SCOPE) ||
(type == FUNC_CALLED && match.scope != symbol))
{
it += RECORD_LENGTH - sizeof(symbol_code2) -
sizeof(type) - sizeof(scope_code);
continue;
}
if (type == FUNC_CALLED)
match.scope = m_code_symbol[symbol_code2];
// GLOBAL_SCOPE has the form "`GLOBAL_SCOPE". Get rid of 1st char
if (match.scope == GLOBAL_SCOPE)
match.scope = GLOBAL_SCOPE + 1;
match.file = match_file;
// Get the line nbr
match.lineno = 0;
for (i = 0; i < sizeof(line_nbr_t); ++i)
{
match.lineno |= ((line_nbr_t)*it) << (8 * i);
++it;
}
//match.lineno = *(line_nbr_t*)it;
//it += sizeof(line_nbr_t);
// Add an element in the match list
result.push_back(match);
}
}
}
/*
* FUNCTION: DbInf::flush
*
* DESCRIPTION: Flush the database file
*
* IN:
* IN-OUT:
* RETURN CODE:
*/
void DbInf::flush ()
{
cont_string_symcode_t::iterator it_symbols;
cont_string_time_t::iterator it_time_stamps;
for (list<string>::iterator it = m_files.begin();
it != m_files.end();
++it)
{
// m_time_stamps[*it] = 0;
m_db_modified = true;
}
m_files.erase(m_files.begin(), m_files.end());
// Return directly if database didn't change since opening
if (m_db_modified == false)
return;
// Do we need to flush the buffer?
if (m_buffer.size() != 0)
{
if (m_do_existing_file == true)
{
update_records();
if (m_db_file.tellg() > (streampos)m_st_lower)
m_st_lower = m_db_file.tellg();
}
else
{
m_db_file.write(&m_buffer[0], m_buffer.size());
if (m_do_existing_file == false)
m_st_lower = m_db_file.tellg();
else
{
if (m_db_file.tellg() > (streampos)m_st_lower)
m_st_lower = m_db_file.tellg();
}
}
}
// Flush symbol table, file and time stamp table to the file.
m_db_file.seekg(m_st_lower, ios::beg);
/****************/
/* SYMBOL TABLE */
/****************/
for (it_symbols = m_symbol_code.begin();
it_symbols != m_symbol_code.end();
++it_symbols)
{
// Dump symbol name
m_db_file << it_symbols->first.c_str();
m_db_file << '\0';
// Dump symbol code
m_db_file.write(&m_symbol_code[it_symbols->first],
sizeof(symbol_code_t));
}
m_st_upper = (file_ptr_t)m_db_file.tellg() - 1;
/**************/
/* FILE TABLE */
/**************/
m_ft_lower = (file_ptr_t)m_db_file.tellg();
for (it_time_stamps = m_time_stamps.begin();
it_time_stamps != m_time_stamps.end();
++it_time_stamps)
{
// Dump file name
m_db_file << it_time_stamps->first.c_str();
m_db_file << '\0';
// Dump time stamp
m_db_file.write(&it_time_stamps->second, sizeof(time_t));
}
m_ft_upper = (file_ptr_t)m_db_file.tellg() - 1;
// Dump table boundaries
m_db_file.write(&m_st_lower, sizeof(file_ptr_t));
m_db_file.write(&m_st_upper, sizeof(file_ptr_t));
m_db_file.write(&m_ft_lower, sizeof(file_ptr_t));
m_db_file.write(&m_ft_upper, sizeof(file_ptr_t));
// Flush the file
m_db_file.flush();
m_old_file.erase();
m_do_existing_file = true;
load_files();
m_buffer.clear();
m_files_copy = m_files;
}
/*
* FUNCTION: DbInf::get_timestamp
*
* DESCRIPTION: This function returns the last modification time stamp
* associated with a file.
*
* IN: file The file name
* IN-OUT:
* RETURN CODE: The time stamp associated with the file. 0 if the file hasn't
* been found.
*/
time_t DbInf::get_timestamp (const string& file)
{
return m_time_stamps[file];
}
/*
* FUNCTION: DbInf::add_timestamp
*
* DESCRIPTION: This function adds the last modification time stamp
* associated with a file.
*
* IN: file The file name
* timestamp The time stamp
* INOUT:
* RETURN CODE:
*/
void DbInf::add_timestamp (const string& file, const time_t timestamp)
{
m_time_stamps[file] = timestamp;
}
/*
* FUNCTION: DbInf::set_file
*
* DESCRIPTION: This function indicates which file is being parsed
*
* IN: file The file being parsed
* IN-OUT:
* RETURN CODE:
*/
void DbInf::set_file (const string& file)
{
m_file = file;
}
/*
* FUNCTION: DbInf::next_completion
*
* DESCRIPTION: This function finds the next symbol completing the given
* stub.
*
* IN: stub The stub used for completion.
* IN-OUT:
* RETURN CODE: A symbol if completion symbol was found. An enpty
* string otherwise.
*/
string DbInf::next_completion (const string& stub,
int& index,
int& total)
{
if ((stub != m_stub) || (m_completions.size() == 0))
{
find_completions(stub);
if (m_completions.size() == 0)
{
index = 0;
total = 0;
return "";
}
m_it_completion = m_completions.begin();
m_completion_index = 1;
}
else
{
if (++m_it_completion == m_completions.end())
{
m_it_completion = m_completions.begin();
m_completion_index = 1;
}
else
++m_completion_index;
}
index = m_completion_index;
total = m_completions.size();
return *m_it_completion;
}
/*
* FUNCTION: DbInf::prev_completion
*
* DESCRIPTION: This function finds the previous symbol completing the given
* stub.
*
* IN: stub The stub used for completion.
* index The match index
* total The total number of match
* IN-OUT:
* RETURN CODE: A symbol if completion symbol was found. An enpty
* string otherwise.
*/
string DbInf::prev_completion (const string& stub,
int& index,
int& total)
{
if ((stub != m_stub) || (m_completions.size() == 0))
{
find_completions(stub);
if (m_completions.size() == 0)
{
index = 0;
total = 0;
return "";
}
m_it_completion = m_completions.end();
--m_it_completion;
m_completion_index = m_completions.size();
}
else
{
if (m_it_completion == m_completions.begin())
{
m_it_completion = m_completions.end();
--m_it_completion;
m_completion_index = m_completions.size();
}
else
{
--m_it_completion;
--m_completion_index;
}
}
index = m_completion_index;
total = m_completions.size();
return *m_it_completion;
}
/*
* FUNCTION: DbInf::find_completions
*
* DESCRIPTION: Build a list of all completions matching the given stub and
* sort this list.
*
* index The match index
* total The total number of match
* IN: stub The stub used for finding completions.
* IN-OUT:
* RETURN CODE:
*/
void DbInf::find_completions (const string& stub)
{
cont_string_symcode_t::iterator it;
if (stub != m_stub)
m_stub = stub;
if (m_completions.size() != 0)
m_completions.erase(m_completions.begin(), m_completions.end());
// Find completions
for (it = m_symbol_code.begin(); it != m_symbol_code.end(); ++it)
{
if (it->first.size() < stub.size())
continue;
if (it->first.compare(stub, 0, stub.size()) == 0)
m_completions.push_back(it->first);
}
// Sort completions.
m_completions.sort();
}
/*
* FUNCTION: DbInf::update_records
*
* DESCRIPTION: This function flushes all records stored in the buffer in the
* existing database. This is done the following way:
* 1) Write one record at the cuurent location.
* 2) If the current location is a new file, go to the next place
* in the database that has records associated with the current
* file and execute step 1.
*
* It is possible that we exceed the old number of records in the
* database. In that case, we just write all the records at the
* current location.
* Exceeding the old number of records occurs when we are further
* in the database file than m_st_lower.
* IN:
* IN-OUT:
* OUT:
* RETURN CODE:
*/
void DbInf::update_records ()
{
size_t i;
unsigned index = 0;
static string old_file;
if (m_file != old_file)
{
old_file = m_file;
m_db_file.flush();
find_free_place();
}
if (m_db_file.tellg() >= (streampos)m_st_lower)
{
/* Just write all the records at that place */
m_db_file.write(&m_instance->m_buffer[0], m_instance->m_buffer.size());
m_buffer.clear();
m_st_lower = m_db_file.tellg();
m_db_file.flush();
return;
}
while (index < m_buffer.size())
{
if ((file_ptr_t)m_db_file.tellg() < m_st_lower)
{
symbol_code_t symbol_code;
/* Check if we can write here */
symbol_code = 0;
for (i = 0; i < sizeof(symbol_code_t); ++i)
{
Uint8 c;
m_db_file.read(&c, 1);
symbol_code |= ((symbol_code_t)c) << (8 * i);
}
m_db_file.seekg((-1) * (int)sizeof(symbol_code_t), ios::cur);
if (symbol_code == SYMBOL_NEW_FILE)
{
find_free_place();
if ((file_ptr_t)m_db_file.tellg() >= m_st_lower)
{
m_db_file.write(&m_instance->m_buffer[index],
m_instance->m_buffer.size() - index);
m_buffer.clear();
m_db_file.flush();
m_st_lower = m_db_file.tellg();
return;
}
}
else if (symbol_code != SYMBOL_FREE_RECORD)
m_db_file.seekg(RECORD_LENGTH, ios::cur);
}
/* Write one record */
m_db_file.write(&m_instance->m_buffer[index], RECORD_LENGTH);
index += RECORD_LENGTH;
if ((file_ptr_t)m_db_file.tellg() > m_st_lower)
m_st_lower = m_db_file.tellg();
}
m_instance->m_buffer.clear();
m_db_file.clear();
m_db_file.flush();
}
/*
* FUNCTION: find_free_place
*
* DESCRIPTION: This function finds a place in the database that is suitable
* for writing one record associated with the current file.
*
* IN:
* IN-OUT:
* OUT:
* RETURN CODE:
* SIDE EFFECT:
*/
void DbInf::find_free_place ()
{
for(;;)
{
symbol_code_t symbol_code;
// If we exceed the old database size, then this is a valid place.
if ((file_ptr_t)m_db_file.tellg() >= m_st_lower)
{
for (size_t i = 0; i < sizeof(symbol_code_t); ++i)
{
if (m_db_file.write(&((Uint8*)&SYMBOL_NEW_FILE)[i], 1) == 0)
abort();
}
m_db_file << m_file << '\0';
m_db_file.flush();
break;
}
/* Get this symbol */
symbol_code = 0;
for (size_t i = 0; i < sizeof(symbol_code_t); ++i)
{
Uint8 c;
m_db_file.read(&c, 1);
symbol_code |= ((symbol_code_t)c) << (8 * i);
}
if (symbol_code == SYMBOL_NEW_FILE)
{
string filename;
/* Get the file name */
getline(m_db_file, filename, '\0');
if (filename == m_file)
break; // We found a good place
}
else
{
// Go to the next record
m_db_file.seekg(RECORD_LENGTH - sizeof(symbol_code_t), ios::cur);
}
}
}
/*
* FUNCTION: DbInf::invalidate_records
*
* DESCRIPTION: This function invalidates all records associated with the
* files passed as argument. This should be called whenever a
* set of files is about to be re-scanned following modifications.
*
* IN: files
* IN-OUT:
* OUT:
* RETURN CODE:
*/
void DbInf::invalidate_records (const list<string>& files)
{
symbol_code_t code;
vector<Uint8> buffer(BUFFER_SIZE);
vector<Uint8>::iterator it;
bool invalidate = false;
file_ptr_t cur = 0;
symbol_code_t free_record = SYMBOL_FREE_RECORD;
list<string> invalid_files;
list<string>::iterator it2;
// Append all files that have been removed from freescope.files
invalid_files = files;
invalid_files.insert(invalid_files.end(),
m_files_copy.begin(),
m_files_copy.end());
for (it2 = m_files_copy.begin(); it2 != m_files_copy.end(); ++it2)
{
m_time_stamps[*it2] = 0;
m_files.remove(*it2);
}
if (invalid_files.size() == 0 && m_files.size() == 0)
return;
m_db_file.seekg(0, ios::beg);
while (cur < m_st_lower)
{
bool mod;
file_ptr_t old_cur;
old_cur = cur;
// Fill the buffer
if (m_st_lower - cur <= BUFFER_SIZE)
{
m_db_file.read(&buffer[0], m_st_lower - cur);
buffer.resize(m_st_lower - cur);
cur += m_st_lower - cur;
}
else
{
m_db_file.read(&buffer[0], BUFFER_SIZE);
buffer.resize(BUFFER_SIZE);
cur += BUFFER_SIZE;
}
it = buffer.begin();
mod = false;
while (it != buffer.end())
{
size_t i;
// Do we have enough characters in the buffer to process one record?
if (buffer.end() - it < (int)RECORD_LENGTH)
{
cur = cur - (buffer.end() - it);
m_db_file.seekg(cur, ios::beg);
break;
}
code = 0;
// Get symbol code
for (i = 0; i < sizeof(code); ++i)
{
code |= ((symbol_code_t)*it) << (8 * i);
++it;
}
if (code == SYMBOL_NEW_FILE)
{
string tmp_file;
vector<char>::iterator it2;
// This is a new file
it2 = (char*)(::find(it, buffer.end(), '\0'));
if (it2 == (char*)buffer.end())
{
cur = cur - (buffer.end() - it) - sizeof(symbol_code_t);
m_db_file.seekg(cur, ios::beg);
break;
}
tmp_file.assign((char*)it, it2 - (char*)it);
it = (Uint8*)it2 + 1;
// Is this a file in the list of modified files?
if ((::find(invalid_files.begin(),
invalid_files.end(),
tmp_file) != invalid_files.end()))
{
invalidate = true;
}
else
{
invalidate = false;
}
}
else
{
if (invalidate == true)
{
it -= sizeof(code);
for (i = 0; i < sizeof(symbol_code_t); ++i)
{
*it = ((Uint8*)&free_record)[i];
++it;
}
mod = true;
}
it += sizeof(symbol_type_t) + sizeof(symbol_code_t) +
sizeof(line_nbr_t);
}
} // while iterate buffer
if (mod == true)
{
// The buffer has been modified. Flush it.
m_db_file.seekg(old_cur, ios::beg);
m_db_file.write(&buffer[0], cur - old_cur);
m_db_file.flush();
}
} // while
m_db_file.flush();
m_db_file.seekg(0, ios::beg);
}
/*
* FUNCTION: DbInf::is_file_in_db
*
* DESCRIPTION: This function checks if a given file is already part of the
* database.
*
* IN: file The file to check for.
* IN-OUT:
* OUT:
* RETURN CODE: true if file is already in databse. false otherwise.
* SIDE EFFECT:
*/
bool DbInf::is_file_in_db (string file)
{
list<string>::iterator it;
if (m_do_existing_file == false)
return true;
it = ::find(m_files_copy.begin(), m_files_copy.end(), file);
if (it != m_files_copy.end())
{
m_files_copy.erase(it);
return true;
}
return false;
}
void DbInf::dump ()
{
m_db_file.seekg(0, ios::beg);
cout.setf(ios::hex, ios::basefield);
cout << "SYMBOL TABLE STARTS AT 0x" << m_st_lower << endl << endl;
cout.setf(ios::dec, ios::basefield);
while ((file_ptr_t)m_db_file.tellg() < m_st_lower)
{
symbol_code_t symbol_code;
symbol_code_t scope_code;
symbol_type_t type;
line_nbr_t lineno;
unsigned int i;
// Read symbol code
symbol_code = 0;
for (i = 0; i < sizeof(symbol_code); ++i)
{
Uint8 c;
m_db_file.read(&c, 1);
symbol_code |= ((symbol_code_t)c) << (8 * i);
}
if (symbol_code == SYMBOL_NEW_FILE)
{
string filename;
/* Get the file name */
getline(m_db_file, filename, '\0');
cout << endl << "*** " << filename << endl << endl;
continue;
}
// Get type
m_db_file.read(&type, sizeof(type));
// Read scope code
scope_code = 0;
for (i = 0; i < sizeof(scope_code); ++i)
{
Uint8 c;
m_db_file.read(&c, 1);
scope_code |= ((symbol_code_t)c) << (8 * i);
}
// Get line number
lineno = 0;
for (i = 0; i < sizeof(lineno); ++i)
{
Uint8 c;
m_db_file.read(&c, 1);
lineno |= ((line_nbr_t)c) << (8 * i);
}
cout.setf(ios::hex, ios::basefield);
cout << "0x" << m_db_file.tellg();
cout.setf(ios::dec, ios::basefield);
cout << " -- " << m_code_symbol[symbol_code]
<< " " << (int)type << " " << m_code_symbol[scope_code] << " "
<< lineno << endl;
}
}
syntax highlighted by Code2HTML, v. 0.9.1