307 lines
5.9 KiB
C++
307 lines
5.9 KiB
C++
// Copyright (c) 2015 YamaArashi
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <stack>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#define FATAL_INPUT_ERROR(format, ...) \
|
|
do { \
|
|
fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, __VA_ARGS__); \
|
|
exit(1); \
|
|
} while (0)
|
|
|
|
#define FATAL_ERROR(format, ...) \
|
|
do { \
|
|
fprintf(stderr, format, __VA_ARGS__); \
|
|
exit(1); \
|
|
} while (0)
|
|
|
|
#else
|
|
|
|
#define FATAL_INPUT_ERROR(format, ...) \
|
|
do { \
|
|
fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, ##__VA_ARGS__); \
|
|
exit(1); \
|
|
} while (0)
|
|
|
|
#define FATAL_ERROR(format, ...) \
|
|
do { \
|
|
fprintf(stderr, format, ##__VA_ARGS__); \
|
|
exit(1); \
|
|
} while (0)
|
|
|
|
#endif // _MSC_VER
|
|
|
|
#define SCANINC_MAX_PATH 255
|
|
|
|
enum class IncDirectiveType {
|
|
None,
|
|
Include,
|
|
Incbin
|
|
};
|
|
|
|
class AsmFile
|
|
{
|
|
public:
|
|
AsmFile(std::string path);
|
|
~AsmFile();
|
|
IncDirectiveType ReadUntilIncDirective(std::string &path);
|
|
|
|
private:
|
|
char *m_buffer;
|
|
int m_pos;
|
|
int m_size;
|
|
int m_lineNum;
|
|
std::string m_path;
|
|
|
|
int GetChar()
|
|
{
|
|
if (m_pos >= m_size)
|
|
return -1;
|
|
|
|
int c = m_buffer[m_pos++];
|
|
|
|
if (c == '\r') {
|
|
if (m_pos < m_size && m_buffer[m_pos++] == '\n') {
|
|
m_lineNum++;
|
|
return '\n';
|
|
} else {
|
|
FATAL_INPUT_ERROR("CR line endings are not supported\n");
|
|
}
|
|
}
|
|
|
|
if (c == '\n')
|
|
m_lineNum++;
|
|
|
|
return c;
|
|
}
|
|
|
|
// No newline translation because it's not needed for any use of this function.
|
|
int PeekChar()
|
|
{
|
|
if (m_pos >= m_size)
|
|
return -1;
|
|
|
|
return m_buffer[m_pos];
|
|
}
|
|
|
|
void SkipTabsAndSpaces()
|
|
{
|
|
while (m_pos < m_size && (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' '))
|
|
m_pos++;
|
|
}
|
|
|
|
bool MatchIncDirective(std::string directiveName, std::string &path)
|
|
{
|
|
int length = directiveName.length();
|
|
int i;
|
|
|
|
for (i = 0; i < length && m_pos + i < m_size; i++)
|
|
if (directiveName[i] != m_buffer[m_pos + i])
|
|
return false;
|
|
|
|
if (i < length)
|
|
return false;
|
|
|
|
m_pos += length;
|
|
|
|
SkipTabsAndSpaces();
|
|
|
|
if (GetChar() != '"')
|
|
FATAL_INPUT_ERROR("no path after \".%s\" directive\n", directiveName.c_str());
|
|
|
|
path = ReadPath();
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string ReadPath();
|
|
void SkipEndOfLineComment();
|
|
void SkipMultiLineComment();
|
|
void SkipString();
|
|
};
|
|
|
|
AsmFile::AsmFile(std::string path)
|
|
{
|
|
m_path = path;
|
|
|
|
FILE *fp = fopen(path.c_str(), "rb");
|
|
|
|
if (fp == NULL)
|
|
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path.c_str());
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
|
|
m_size = ftell(fp);
|
|
|
|
m_buffer = new char[m_size];
|
|
|
|
rewind(fp);
|
|
|
|
if (fread(m_buffer, m_size, 1, fp) != 1)
|
|
FATAL_ERROR("Failed to read \"%s\".\n", path.c_str());
|
|
|
|
fclose(fp);
|
|
|
|
m_pos = 0;
|
|
m_lineNum = 1;
|
|
}
|
|
|
|
AsmFile::~AsmFile()
|
|
{
|
|
delete[] m_buffer;
|
|
}
|
|
|
|
IncDirectiveType AsmFile::ReadUntilIncDirective(std::string &path)
|
|
{
|
|
// At the beginning of each loop iteration, the current file position
|
|
// should be at the start of a line or at the end of the file.
|
|
for (;;) {
|
|
SkipTabsAndSpaces();
|
|
|
|
IncDirectiveType incDirectiveType = IncDirectiveType::None;
|
|
|
|
if (PeekChar() == '.') {
|
|
m_pos++;
|
|
|
|
if (MatchIncDirective("incbin", path))
|
|
incDirectiveType = IncDirectiveType::Incbin;
|
|
else if (MatchIncDirective("include", path))
|
|
incDirectiveType = IncDirectiveType::Include;
|
|
}
|
|
|
|
for (;;) {
|
|
int c = GetChar();
|
|
|
|
if (c == -1)
|
|
return incDirectiveType;
|
|
|
|
if (c == ';') {
|
|
SkipEndOfLineComment();
|
|
break;
|
|
} else if (c == '/' && PeekChar() == '*') {
|
|
m_pos++;
|
|
SkipMultiLineComment();
|
|
} else if (c == '"') {
|
|
SkipString();
|
|
} else if (c == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (incDirectiveType != IncDirectiveType::None)
|
|
return incDirectiveType;
|
|
}
|
|
}
|
|
|
|
std::string AsmFile::ReadPath()
|
|
{
|
|
int length = 0;
|
|
int startPos = m_pos;
|
|
|
|
for (;;) {
|
|
int c = GetChar();
|
|
|
|
if (c == '"')
|
|
break;
|
|
|
|
if (c == -1)
|
|
FATAL_INPUT_ERROR("unexpected EOF in include string\n");
|
|
|
|
if (c == 0)
|
|
FATAL_INPUT_ERROR("unexpected NUL character in include string\n");
|
|
|
|
if (c == '\n')
|
|
FATAL_INPUT_ERROR("unexpected end of line character in include string\n");
|
|
|
|
if (c == '\\') {
|
|
c = GetChar();
|
|
|
|
if (c != '"')
|
|
FATAL_INPUT_ERROR("unknown escape \"\\%c\" in include string\n", c);
|
|
}
|
|
|
|
length++;
|
|
|
|
if (length > SCANINC_MAX_PATH)
|
|
FATAL_INPUT_ERROR("path is too long");
|
|
}
|
|
|
|
return std::string(m_buffer, startPos, length);
|
|
}
|
|
|
|
void AsmFile::SkipEndOfLineComment()
|
|
{
|
|
int c;
|
|
|
|
do {
|
|
c = GetChar();
|
|
} while (c != -1 && c != '\n');
|
|
}
|
|
|
|
void AsmFile::SkipMultiLineComment()
|
|
{
|
|
for (;;) {
|
|
int c = GetChar();
|
|
|
|
if (c == '*') {
|
|
if (PeekChar() == '/') {
|
|
m_pos++;
|
|
return;
|
|
}
|
|
} else if (c == -1) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AsmFile::SkipString()
|
|
{
|
|
for (;;) {
|
|
int c = GetChar();
|
|
|
|
if (c == '"')
|
|
break;
|
|
|
|
if (c == -1)
|
|
FATAL_INPUT_ERROR("unexpected EOF in string\n");
|
|
|
|
if (c == '\\') {
|
|
c = GetChar();
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (argc < 2)
|
|
FATAL_ERROR("Usage: scaninc ASM_FILE_PATH\n");
|
|
|
|
std::stack<std::string> filesToProcess;
|
|
std::set<std::string> dependencies;
|
|
|
|
filesToProcess.push(std::string(argv[1]));
|
|
|
|
while (!filesToProcess.empty()) {
|
|
AsmFile file(filesToProcess.top());
|
|
|
|
filesToProcess.pop();
|
|
|
|
IncDirectiveType incDirectiveType;
|
|
std::string path;
|
|
|
|
while ((incDirectiveType = file.ReadUntilIncDirective(path)) != IncDirectiveType::None) {
|
|
bool inserted = dependencies.insert(path).second;
|
|
if (inserted && incDirectiveType == IncDirectiveType::Include)
|
|
filesToProcess.push(path);
|
|
}
|
|
}
|
|
|
|
for (const std::string &path : dependencies) {
|
|
printf("%s\n", path.c_str());
|
|
}
|
|
}
|