2019-06-13 15:42:39 -05:00
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cerrno> //For errno
|
|
|
|
#include <cstring>
|
|
|
|
#include <memory>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "vtr_util.h"
|
|
|
|
#include "vtr_assert.h"
|
|
|
|
#include "vtr_memory.h"
|
|
|
|
#include "vtr_error.h"
|
|
|
|
|
|
|
|
namespace vtr {
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
std::string out_file_prefix; /* used by fopen */
|
2019-06-13 15:42:39 -05:00
|
|
|
static int file_line_number = 0; /* file in line number being parsed (used by fgets) */
|
2020-01-03 17:14:42 -06:00
|
|
|
static int cont; /* line continued? (used by strtok)*/
|
2019-06-13 15:42:39 -05:00
|
|
|
|
|
|
|
//Splits the string 'text' along the specified delimiter characters in 'delims'
|
|
|
|
//The split strings (excluding the delimiters) are returned
|
|
|
|
std::vector<std::string> split(const char* text, const std::string delims) {
|
2020-01-03 17:14:42 -06:00
|
|
|
if (text) {
|
2019-06-13 15:42:39 -05:00
|
|
|
std::string text_str(text);
|
|
|
|
return split(text_str, delims);
|
|
|
|
}
|
|
|
|
return std::vector<std::string>();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> split(const std::string& text, const std::string delims) {
|
|
|
|
std::vector<std::string> tokens;
|
|
|
|
|
|
|
|
std::string curr_tok;
|
2020-01-03 17:14:42 -06:00
|
|
|
for (char c : text) {
|
|
|
|
if (delims.find(c) != std::string::npos) {
|
2019-06-13 15:42:39 -05:00
|
|
|
//Delimeter character
|
2020-01-03 17:14:42 -06:00
|
|
|
if (!curr_tok.empty()) {
|
2019-06-13 15:42:39 -05:00
|
|
|
//At the end of the token
|
|
|
|
|
|
|
|
//Save it
|
|
|
|
tokens.push_back(curr_tok);
|
|
|
|
|
|
|
|
//Reset token
|
|
|
|
curr_tok.clear();
|
|
|
|
} else {
|
|
|
|
//Pass
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//Non-delimeter append to token
|
|
|
|
curr_tok += c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Add last token
|
2020-01-03 17:14:42 -06:00
|
|
|
if (!curr_tok.empty()) {
|
2019-06-13 15:42:39 -05:00
|
|
|
//Save it
|
|
|
|
tokens.push_back(curr_tok);
|
|
|
|
}
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string replace_first(const std::string& input, const std::string& search, const std::string& replace) {
|
|
|
|
auto pos = input.find(search);
|
|
|
|
|
|
|
|
std::string output(input, 0, pos);
|
|
|
|
output += replace;
|
|
|
|
output += std::string(input, pos + search.size());
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string replace_all(const std::string& input, const std::string& search, const std::string& replace) {
|
|
|
|
std::string output;
|
|
|
|
|
|
|
|
size_t last = 0;
|
|
|
|
size_t pos = input.find(search, last); //Find the first instance of 'search' starting at or after 'last'
|
2020-01-03 17:14:42 -06:00
|
|
|
while (pos != std::string::npos) {
|
2019-06-13 15:42:39 -05:00
|
|
|
output += input.substr(last, pos - last); //Append anything in the input string between last and current match
|
2020-01-03 17:14:42 -06:00
|
|
|
output += replace; //Add the replacement
|
2019-06-13 15:42:39 -05:00
|
|
|
|
|
|
|
last = pos + search.size(); //Advance past the current match
|
|
|
|
|
|
|
|
pos = input.find(search, last); //Look for the next match
|
|
|
|
}
|
|
|
|
output += input.substr(last, pos - last); //Append anything in 'input' after the last match
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Retruns true if str starts with prefix
|
|
|
|
bool starts_with(std::string str, std::string prefix) {
|
|
|
|
return str.find(prefix) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Returns a std::string formatted using a printf-style format string
|
|
|
|
std::string string_fmt(const char* fmt, ...) {
|
|
|
|
// Make a variable argument list
|
|
|
|
va_list va_args;
|
|
|
|
|
|
|
|
// Initialize variable argument list
|
|
|
|
va_start(va_args, fmt);
|
|
|
|
|
|
|
|
//Format string
|
|
|
|
std::string str = vstring_fmt(fmt, va_args);
|
|
|
|
|
|
|
|
// Reset variable argument list
|
|
|
|
va_end(va_args);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Returns a std::string formatted using a printf-style format string taking
|
|
|
|
//an explicit va_list
|
|
|
|
std::string vstring_fmt(const char* fmt, va_list args) {
|
|
|
|
// We need to copy the args so we don't change them before the true formating
|
|
|
|
va_list va_args_copy;
|
|
|
|
va_copy(va_args_copy, args);
|
|
|
|
|
|
|
|
//Determine the formatted length using a copy of the args
|
|
|
|
int len = std::vsnprintf(nullptr, 0, fmt, va_args_copy);
|
|
|
|
|
|
|
|
va_end(va_args_copy); //Clean-up
|
|
|
|
|
|
|
|
//Negative if there is a problem with the format string
|
|
|
|
VTR_ASSERT_MSG(len >= 0, "Problem decoding format string");
|
|
|
|
|
|
|
|
size_t buf_size = len + 1; //For terminator
|
|
|
|
|
|
|
|
//Allocate a buffer
|
|
|
|
// unique_ptr will free buffer automatically
|
|
|
|
std::unique_ptr<char[]> buf(new char[buf_size]);
|
|
|
|
|
|
|
|
//Format into the buffer using the original args
|
|
|
|
len = std::vsnprintf(buf.get(), buf_size, fmt, args);
|
|
|
|
|
|
|
|
VTR_ASSERT_MSG(len >= 0, "Problem decoding format string");
|
|
|
|
VTR_ASSERT(static_cast<size_t>(len) == buf_size - 1);
|
|
|
|
|
|
|
|
//Build the string from the buffer
|
|
|
|
return std::string(buf.get(), len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* An alternate for strncpy since strncpy doesn't work as most
|
|
|
|
* people would expect. This ensures null termination */
|
2020-01-03 17:14:42 -06:00
|
|
|
char* strncpy(char* dest, const char* src, size_t size) {
|
2019-06-13 15:42:39 -05:00
|
|
|
/* Find string's length */
|
|
|
|
size_t len = std::strlen(src);
|
|
|
|
|
|
|
|
/* Cap length at (num - 1) to leave room for \0 */
|
|
|
|
if (size <= len)
|
|
|
|
len = (size - 1);
|
|
|
|
|
|
|
|
/* Copy as much of string as we can fit */
|
|
|
|
std::memcpy(dest, src, len);
|
|
|
|
|
|
|
|
/* explicit null termination */
|
|
|
|
dest[len] = '\0';
|
|
|
|
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
char* strdup(const char* str) {
|
|
|
|
if (str == nullptr) {
|
|
|
|
return nullptr;
|
2019-06-13 15:42:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t Len = std::strlen(str);
|
|
|
|
//use calloc to already make the last char '\0'
|
2020-01-03 17:14:42 -06:00
|
|
|
return (char*)std::memcpy(vtr::calloc(Len + 1, sizeof(char)), str, Len);
|
|
|
|
;
|
2019-06-13 15:42:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
|
|
T atoT(const std::string& value, const std::string& type_name) {
|
|
|
|
//The c version of atof doesn't catch errors.
|
|
|
|
//
|
|
|
|
//This version uses stringstream to detect conversion errors
|
|
|
|
std::istringstream ss(value);
|
|
|
|
|
|
|
|
T val;
|
|
|
|
ss >> val;
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
if (ss.fail() || !ss.eof()) {
|
2019-06-13 15:42:39 -05:00
|
|
|
//Failed to convert, or did not consume all input
|
|
|
|
std::stringstream msg;
|
|
|
|
msg << "Failed to convert string '" << value << "' to " << type_name;
|
|
|
|
throw VtrError(msg.str(), __FILE__, __LINE__);
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
int atoi(const std::string& value) {
|
|
|
|
return atoT<int>(value, "int");
|
|
|
|
}
|
|
|
|
|
|
|
|
double atod(const std::string& value) {
|
|
|
|
return atoT<double>(value, "double");
|
|
|
|
}
|
|
|
|
|
|
|
|
float atof(const std::string& value) {
|
|
|
|
return atoT<float>(value, "float");
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned atou(const std::string& value) {
|
|
|
|
return atoT<unsigned>(value, "unsigned int");
|
|
|
|
}
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
char* strtok(char* ptr, const char* tokens, FILE* fp, char* buf) {
|
2019-06-13 15:42:39 -05:00
|
|
|
/* Get next token, and wrap to next line if \ at end of line. *
|
|
|
|
* There is a bit of a "gotcha" in strtok. It does not make a *
|
|
|
|
* copy of the character array which you pass by pointer on the *
|
|
|
|
* first call. Thus, you must make sure this array exists for *
|
|
|
|
* as long as you are using strtok to parse that line. Don't *
|
|
|
|
* use local buffers in a bunch of subroutines calling each *
|
|
|
|
* other; the local buffer may be overwritten when the stack is *
|
|
|
|
* restored after return from the subroutine. */
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
char* val;
|
2019-06-13 15:42:39 -05:00
|
|
|
|
|
|
|
val = std::strtok(ptr, tokens);
|
|
|
|
for (;;) {
|
|
|
|
if (val != nullptr || cont == 0)
|
|
|
|
return (val);
|
|
|
|
|
|
|
|
/* return unless we have a null value and a continuation line */
|
2020-01-03 17:14:42 -06:00
|
|
|
if (vtr::fgets(buf, bufsize, fp) == nullptr)
|
|
|
|
return (nullptr);
|
2019-06-13 15:42:39 -05:00
|
|
|
|
|
|
|
val = std::strtok(buf, tokens);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
FILE* fopen(const char* fname, const char* flag) {
|
|
|
|
FILE* fp;
|
2019-06-13 15:42:39 -05:00
|
|
|
size_t Len;
|
2020-01-03 17:14:42 -06:00
|
|
|
char* new_fname = nullptr;
|
2019-06-13 15:42:39 -05:00
|
|
|
file_line_number = 0;
|
|
|
|
|
|
|
|
/* Appends a prefix string for output files */
|
|
|
|
if (!out_file_prefix.empty()) {
|
|
|
|
if (std::strchr(flag, 'w')) {
|
|
|
|
Len = 1; /* NULL char */
|
|
|
|
Len += std::strlen(out_file_prefix.c_str());
|
|
|
|
Len += std::strlen(fname);
|
2020-01-03 17:14:42 -06:00
|
|
|
new_fname = (char*)vtr::malloc(Len * sizeof(char));
|
2019-06-13 15:42:39 -05:00
|
|
|
strcpy(new_fname, out_file_prefix.c_str());
|
|
|
|
strcat(new_fname, fname);
|
|
|
|
fname = new_fname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nullptr == (fp = std::fopen(fname, flag))) {
|
|
|
|
throw VtrError(string_fmt("Error opening file %s for %s access: %s.\n", fname, flag, strerror(errno)), __FILE__, __LINE__);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_fname)
|
|
|
|
std::free(new_fname);
|
|
|
|
|
|
|
|
return (fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fclose(FILE* f) {
|
|
|
|
return std::fclose(f);
|
|
|
|
}
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
char* fgets(char* buf, int max_size, FILE* fp) {
|
2019-06-13 15:42:39 -05:00
|
|
|
/* Get an input line, update the line number and cut off *
|
|
|
|
* any comment part. A \ at the end of a line with no *
|
|
|
|
* comment part (#) means continue. vtr::fgets should give *
|
|
|
|
* identical results for Windows (\r\n) and Linux (\n) *
|
|
|
|
* newlines, since it replaces each carriage return \r *
|
|
|
|
* by a newline character \n. Returns NULL after EOF. */
|
|
|
|
|
|
|
|
int ch;
|
|
|
|
int i;
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
cont = 0; /* line continued? */
|
2019-06-13 15:42:39 -05:00
|
|
|
file_line_number++; /* global variable */
|
|
|
|
|
|
|
|
for (i = 0; i < max_size - 1; i++) { /* Keep going until the line finishes or the buffer is full */
|
|
|
|
|
|
|
|
ch = std::fgetc(fp);
|
|
|
|
|
|
|
|
if (std::feof(fp)) { /* end of file */
|
|
|
|
if (i == 0) {
|
2020-01-03 17:14:42 -06:00
|
|
|
return nullptr; /* required so we can write while (vtr::fgets(...) != NULL) */
|
|
|
|
} else { /* no newline before end of file - last line must be returned */
|
2019-06-13 15:42:39 -05:00
|
|
|
buf[i] = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch == '#') { /* comment */
|
|
|
|
buf[i] = '\0';
|
|
|
|
while ((ch = std::fgetc(fp)) != '\n' && !std::feof(fp))
|
|
|
|
; /* skip the rest of the line */
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
if (ch == '\r' || ch == '\n') { /* newline (cross-platform) */
|
2019-06-13 15:42:39 -05:00
|
|
|
if (i != 0 && buf[i - 1] == '\\') { /* if \ at end of line, line continued */
|
|
|
|
cont = 1;
|
|
|
|
buf[i - 1] = '\n'; /* May need this for tokens */
|
|
|
|
buf[i] = '\0';
|
|
|
|
} else {
|
|
|
|
buf[i] = '\n';
|
|
|
|
buf[i + 1] = '\0';
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[i] = ch; /* copy character into the buffer */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Buffer is full but line has not terminated, so error */
|
|
|
|
throw VtrError(string_fmt("Error on line %d -- line is too long for input buffer.\n"
|
2020-01-03 17:14:42 -06:00
|
|
|
"All lines must be at most %d characters long.\n",
|
|
|
|
bufsize - 2),
|
|
|
|
__FILE__, __LINE__);
|
2019-06-13 15:42:39 -05:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns line number of last opened and read file
|
|
|
|
*/
|
|
|
|
int get_file_line_number_of_last_opened_file() {
|
|
|
|
return file_line_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool file_exists(const char* filename) {
|
2020-01-03 17:14:42 -06:00
|
|
|
FILE* file;
|
2019-06-13 15:42:39 -05:00
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
if (filename == nullptr) {
|
2019-06-13 15:42:39 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
file = std::fopen(filename, "r");
|
|
|
|
if (file) {
|
|
|
|
std::fclose(file);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Date:July 17th, 2013
|
|
|
|
* Author: Daniel Chen
|
|
|
|
* Purpose: Checks the file extension of an file to ensure
|
|
|
|
* correct file format. Returns true if format is
|
|
|
|
* correct, and false otherwise.
|
|
|
|
* Note: This is probably a fragile check, but at least
|
|
|
|
* should prevent common problems such as swapping
|
|
|
|
* architecture file and blif file on the VPR
|
|
|
|
* command line.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool check_file_name_extension(const char* file_name,
|
2020-01-03 17:14:42 -06:00
|
|
|
const char* file_extension) {
|
2019-06-13 15:42:39 -05:00
|
|
|
const char* str;
|
|
|
|
int len_extension;
|
|
|
|
|
|
|
|
len_extension = std::strlen(file_extension);
|
|
|
|
str = std::strstr(file_name, file_extension);
|
2020-01-03 17:14:42 -06:00
|
|
|
if (str == nullptr || (*(str + len_extension) != '\0')) {
|
2019-06-13 15:42:39 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
std::vector<std::string> ReadLineTokens(FILE* InFile, int* LineNum) {
|
2019-06-13 15:42:39 -05:00
|
|
|
std::unique_ptr<char[]> buf(new char[vtr::bufsize]);
|
|
|
|
|
|
|
|
const char* line = vtr::fgets(buf.get(), vtr::bufsize, InFile);
|
|
|
|
|
|
|
|
++(*LineNum);
|
|
|
|
|
|
|
|
return vtr::split(line);
|
|
|
|
}
|
|
|
|
|
2020-01-03 17:14:42 -06:00
|
|
|
} // namespace vtr
|