yosys/kernel/io.cc

458 lines
10 KiB
C++

#include "kernel/yosys_common.h"
#include "kernel/log.h"
#include <iostream>
#include <dirent.h>
#include <string>
#include <cstdarg>
#include <cstdio>
YOSYS_NAMESPACE_BEGIN
// Set of utilities for handling files
int readsome(std::istream &f, char *s, int n)
{
int rc = int(f.readsome(s, n));
// f.readsome() sometimes returns 0 on a non-empty stream..
if (rc == 0) {
int c = f.get();
if (c != EOF) {
*s = c;
rc = 1;
}
}
return rc;
}
std::string next_token(std::string &text, const char *sep, bool long_strings)
{
size_t pos_begin = text.find_first_not_of(sep);
if (pos_begin == std::string::npos)
pos_begin = text.size();
if (long_strings && pos_begin != text.size() && text[pos_begin] == '"') {
std::string sep_string = sep;
for (size_t i = pos_begin+1; i < text.size(); i++) {
if (text[i] == '"' && (i+1 == text.size() || sep_string.find(text[i+1]) != std::string::npos)) {
std::string token = text.substr(pos_begin, i-pos_begin+1);
text = text.substr(i+1);
return token;
}
if (i+1 < text.size() && text[i] == '"' && text[i+1] == ';' && (i+2 == text.size() || sep_string.find(text[i+2]) != std::string::npos)) {
std::string token = text.substr(pos_begin, i-pos_begin+1);
text = text.substr(i+2);
return token + ";";
}
}
}
size_t pos_end = text.find_first_of(sep, pos_begin);
if (pos_end == std::string::npos)
pos_end = text.size();
std::string token = text.substr(pos_begin, pos_end-pos_begin);
text = text.substr(pos_end);
return token;
}
std::vector<std::string> split_tokens(const std::string &text, const char *sep)
{
std::vector<std::string> tokens;
std::string current_token;
for (char c : text) {
if (strchr(sep, c)) {
if (!current_token.empty()) {
tokens.push_back(current_token);
current_token.clear();
}
} else
current_token += c;
}
if (!current_token.empty()) {
tokens.push_back(current_token);
current_token.clear();
}
return tokens;
}
// this is very similar to fnmatch(). the exact rules used by this
// function are:
//
// ? matches any character except
// * matches any sequence of characters
// [...] matches any of the characters in the list
// [!..] matches any of the characters not in the list
//
// a backslash may be used to escape the next characters in the
// pattern. each special character can also simply match itself.
//
bool patmatch(const char *pattern, const char *string)
{
if (*pattern == 0)
return *string == 0;
if (*pattern == '\\') {
if (pattern[1] == string[0] && patmatch(pattern+2, string+1))
return true;
}
if (*pattern == '?') {
if (*string == 0)
return false;
return patmatch(pattern+1, string+1);
}
if (*pattern == '*') {
while (*string) {
if (patmatch(pattern+1, string++))
return true;
}
return pattern[1] == 0;
}
if (*pattern == '[') {
bool found_match = false;
bool inverted_list = pattern[1] == '!';
const char *p = pattern + (inverted_list ? 1 : 0);
while (*++p) {
if (*p == ']') {
if (found_match != inverted_list && patmatch(p+1, string+1))
return true;
break;
}
if (*p == '\\') {
if (*++p == *string)
found_match = true;
} else
if (*p == *string)
found_match = true;
}
}
if (*pattern == *string)
return patmatch(pattern+1, string+1);
return false;
}
std::string get_base_tmpdir()
{
static std::string tmpdir;
if (!tmpdir.empty()) {
return tmpdir;
}
#if defined(_WIN32)
# ifdef __MINGW32__
char longpath[MAX_PATH + 1];
char shortpath[MAX_PATH + 1];
# else
WCHAR longpath[MAX_PATH + 1];
TCHAR shortpath[MAX_PATH + 1];
# endif
if (!GetTempPath(MAX_PATH+1, longpath))
log_error("GetTempPath() failed.\n");
if (!GetShortPathName(longpath, shortpath, MAX_PATH + 1))
log_error("GetShortPathName() failed.\n");
for (int i = 0; shortpath[i]; i++)
tmpdir += char(shortpath[i]);
#else
char * var = std::getenv("TMPDIR");
if (var && strlen(var)!=0) {
tmpdir.assign(var);
// We return the directory name without the trailing '/'
while (!tmpdir.empty() && (tmpdir.back() == '/')) {
tmpdir.pop_back();
}
} else {
tmpdir.assign("/tmp");
}
#endif
return tmpdir;
}
std::string make_temp_file(std::string template_str)
{
size_t pos = template_str.rfind("XXXXXX");
log_assert(pos != std::string::npos);
#if defined(__wasm)
static size_t index = 0;
template_str.replace(pos, 6, stringf("%06zu", index++));
#elif defined(_WIN32)
#ifndef YOSYS_WIN32_UNIX_DIR
std::replace(template_str.begin(), template_str.end(), '/', '\\');
#endif
while (1) {
for (int i = 0; i < 6; i++) {
static std::string y = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static uint32_t x = 314159265 ^ uint32_t(time(NULL));
x ^= x << 13, x ^= x >> 17, x ^= x << 5;
template_str[pos+i] = y[x % y.size()];
}
if (_access(template_str.c_str(), 0) != 0)
break;
}
#else
int suffixlen = template_str.size() - pos - 6;
char *p = strdup(template_str.c_str());
close(mkstemps(p, suffixlen));
template_str = p;
free(p);
#endif
return template_str;
}
std::string make_temp_dir(std::string template_str)
{
#if defined(_WIN32)
template_str = make_temp_file(template_str);
mkdir(template_str.c_str());
return template_str;
#elif defined(__wasm)
template_str = make_temp_file(template_str);
mkdir(template_str.c_str(), 0777);
return template_str;
#else
# ifndef NDEBUG
size_t pos = template_str.rfind("XXXXXX");
log_assert(pos != std::string::npos);
int suffixlen = template_str.size() - pos - 6;
log_assert(suffixlen == 0);
# endif
char *p = strdup(template_str.c_str());
char *res = mkdtemp(p);
log_assert(res != NULL);
template_str = p;
free(p);
return template_str;
#endif
}
bool check_directory_exists(const std::string& dirname)
{
#if defined(_WIN32)
struct _stat info;
if (_stat(dirname.c_str(), &info) != 0)
{
return false;
}
return (info.st_mode & _S_IFDIR) != 0;
#else
struct stat info;
if (stat(dirname.c_str(), &info) != 0)
{
return false;
}
return (info.st_mode & S_IFDIR) != 0;
#endif
}
#ifdef _WIN32
bool check_file_exists(std::string filename, bool)
{
return _access(filename.c_str(), 0) == 0;
}
#else
bool check_file_exists(std::string filename, bool is_exec)
{
return access(filename.c_str(), is_exec ? X_OK : F_OK) == 0;
}
#endif
bool is_absolute_path(std::string filename)
{
#ifdef _WIN32
return filename[0] == '/' || filename[0] == '\\' || (filename[0] != 0 && filename[1] == ':');
#else
return filename[0] == '/';
#endif
}
void remove_directory(std::string dirname)
{
#ifdef _WIN32
run_command(stringf("rmdir /s /q \"%s\"", dirname.c_str()));
#else
struct stat stbuf;
struct dirent **namelist;
int n = scandir(dirname.c_str(), &namelist, nullptr, alphasort);
log_assert(n >= 0);
for (int i = 0; i < n; i++) {
if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) {
std::string buffer = stringf("%s/%s", dirname.c_str(), namelist[i]->d_name);
if (!stat(buffer.c_str(), &stbuf) && S_ISREG(stbuf.st_mode)) {
remove(buffer.c_str());
} else
remove_directory(buffer);
}
free(namelist[i]);
}
free(namelist);
rmdir(dirname.c_str());
#endif
}
bool create_directory(const std::string& dirname)
{
#if defined(_WIN32)
int ret = _mkdir(dirname.c_str());
#else
mode_t mode = 0755;
int ret = mkdir(dirname.c_str(), mode);
#endif
if (ret == 0)
return true;
switch (errno)
{
case ENOENT:
// parent didn't exist, try to create it
{
std::string::size_type pos = dirname.find_last_of('/');
if (pos == std::string::npos)
#if defined(_WIN32)
pos = dirname.find_last_of('\\');
if (pos == std::string::npos)
#endif
return false;
if (!create_directory( dirname.substr(0, pos) ))
return false;
}
// now, try to create again
#if defined(_WIN32)
return 0 == _mkdir(dirname.c_str());
#else
return 0 == mkdir(dirname.c_str(), mode);
#endif
case EEXIST:
// done!
return check_directory_exists(dirname);
default:
return false;
}
}
std::string escape_filename_spaces(const std::string& filename)
{
std::string out;
out.reserve(filename.size());
for (auto c : filename)
{
if (c == ' ')
out += "\\ ";
else
out.push_back(c);
}
return out;
}
#ifdef YOSYS_ENABLE_ZLIB
PRIVATE_NAMESPACE_BEGIN
static const size_t GZ_BUFFER_SIZE = 8192;
static void decompress_gzip(const std::string &filename, std::stringstream &out)
{
char buffer[GZ_BUFFER_SIZE];
int bytes_read;
gzFile gzf = gzopen(filename.c_str(), "rb");
while(!gzeof(gzf)) {
bytes_read = gzread(gzf, reinterpret_cast<void *>(buffer), GZ_BUFFER_SIZE);
out.write(buffer, bytes_read);
}
gzclose(gzf);
}
PRIVATE_NAMESPACE_END
gzip_ostream::gzip_ostream() : std::ostream(nullptr) {
rdbuf(&outbuf);
}
bool gzip_ostream::open(const std::string &filename) {
return outbuf.open(filename);
}
gzip_ostream::gzip_streambuf::gzip_streambuf() {
setp(buffer, buffer + buffer_size - 1);
}
bool gzip_ostream::gzip_streambuf::open(const std::string &filename) {
gzf = gzopen(filename.c_str(), "wb");
return gzf != nullptr;
}
int gzip_ostream::gzip_streambuf::sync() {
int num = pptr() - pbase();
if (num > 0) {
if (gzwrite(gzf, reinterpret_cast<const void*>(pbase()), num) != num) {
return -1;
}
pbump(-num);
}
return 0;
}
gzip_ostream::gzip_streambuf::~gzip_streambuf() {
if (gzf) {
sync();
gzclose(gzf);
}
}
#endif // YOSYS_ENABLE_ZLIB
// Takes a successfully opened ifstream. If it's gzipped, returns an istream
// over a buffer of the file fully decompressed in memory. Otherwise,
// returns the original ifstream, rewound to the start.
std::istream* uncompressed(std::ifstream* f, const std::string filename) {
if (!f)
return nullptr;
// Check for gzip magic
unsigned char magic[3];
int n = 0;
while (n < 3)
{
int c = f->get();
if (c != EOF) {
magic[n] = (unsigned char) c;
}
n++;
}
if (n == 3 && magic[0] == 0x1f && magic[1] == 0x8b) {
#ifdef YOSYS_ENABLE_ZLIB
log("Found gzip magic in file `%s', decompressing using zlib.\n", filename.c_str());
if (magic[2] != 8)
log_cmd_error("gzip file `%s' uses unsupported compression type %02x\n",
filename.c_str(), unsigned(magic[2]));
delete f;
std::stringstream *df = new std::stringstream();
decompress_gzip(filename, *df);
return df;
#else
log_cmd_error("File `%s' is a gzip file, but Yosys is compiled without zlib.\n", filename.c_str());
#endif // YOSYS_ENABLE_ZLIB
} else {
f->clear();
f->seekg(0, std::ios::beg);
return f;
}
}
YOSYS_NAMESPACE_END