mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 00:55:08 +00:00
Update
This commit is contained in:
parent
5dbde940b6
commit
0dc27bd3ac
@ -7,7 +7,7 @@ clang::CodeCompleteResults::CodeCompleteResults(CXTranslationUnit &cx_tu,
|
|||||||
const std::string &buffer,
|
const std::string &buffer,
|
||||||
unsigned line_num, unsigned column) {
|
unsigned line_num, unsigned column) {
|
||||||
CXUnsavedFile files[1];
|
CXUnsavedFile files[1];
|
||||||
auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu));
|
auto file_path=ToString(clang_getTranslationUnitSpelling(cx_tu));
|
||||||
files[0].Filename = file_path.c_str();
|
files[0].Filename = file_path.c_str();
|
||||||
files[0].Contents = buffer.c_str();
|
files[0].Contents = buffer.c_str();
|
||||||
files[0].Length = buffer.size();
|
files[0].Length = buffer.size();
|
||||||
@ -41,5 +41,5 @@ clang::CompletionString clang::CodeCompleteResults::get(unsigned i) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string clang::CodeCompleteResults::get_usr() const {
|
std::string clang::CodeCompleteResults::get_usr() const {
|
||||||
return to_string(clang_codeCompleteGetContainerUSR(cx_results));
|
return ToString(clang_codeCompleteGetContainerUSR(cx_results));
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ std::string clang::CompileCommand::get_command() {
|
|||||||
std::string res;
|
std::string res;
|
||||||
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
|
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
|
||||||
for (unsigned i = 0; i < N; i++) {
|
for (unsigned i = 0; i < N; i++) {
|
||||||
res += to_string(clang_CompileCommand_getArg(cx_command, i));
|
res += ToString(clang_CompileCommand_getArg(cx_command, i));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -15,7 +15,7 @@ std::vector<std::string> clang::CompileCommand::get_command_as_args() {
|
|||||||
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
|
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
|
||||||
std::vector<std::string> res(N);
|
std::vector<std::string> res(N);
|
||||||
for (unsigned i = 0; i < N; i++) {
|
for (unsigned i = 0; i < N; i++) {
|
||||||
res[i] = to_string(clang_CompileCommand_getArg(cx_command, i));
|
res[i] = ToString(clang_CompileCommand_getArg(cx_command, i));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,13 @@ unsigned clang::CompletionString::get_num_chunks() {
|
|||||||
std::vector<clang::CompletionChunk> clang::CompletionString::get_chunks() {
|
std::vector<clang::CompletionChunk> clang::CompletionString::get_chunks() {
|
||||||
std::vector<CompletionChunk> res;
|
std::vector<CompletionChunk> res;
|
||||||
for (unsigned i = 0; i < get_num_chunks(); i++) {
|
for (unsigned i = 0; i < get_num_chunks(); i++) {
|
||||||
res.emplace_back(to_string(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast<CompletionChunkKind> (clang_getCompletionChunkKind(cx_completion_sting, i)));
|
res.emplace_back(ToString(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast<CompletionChunkKind> (clang_getCompletionChunkKind(cx_completion_sting, i)));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string clang::CompletionString::get_brief_comments() {
|
std::string clang::CompletionString::get_brief_comments() {
|
||||||
return to_string(clang_getCompletionBriefComment(cx_completion_sting));
|
return ToString(clang_getCompletionBriefComment(cx_completion_sting));
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::CompletionChunk::CompletionChunk(std::string chunk, CompletionChunkKind kind) :
|
clang::CompletionChunk::CompletionChunk(std::string chunk, CompletionChunkKind kind) :
|
||||||
|
@ -4,28 +4,38 @@
|
|||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
|
||||||
std::string Cursor::Type::get_spelling() const {
|
static_assert(sizeof(Cursor) == sizeof(CXCursor), "Cursor must be the same size as CXCursor");
|
||||||
return to_string(clang_getTypeSpelling(cx_type));
|
|
||||||
|
std::string Type::get_spelling() const {
|
||||||
|
return ToString(clang_getTypeSpelling(cx_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor::Type Cursor::Type::get_result() const {
|
Type Cursor::get_type() const {
|
||||||
return Type(clang_getResultType(cx_type));
|
return Type(clang_getCursorType(cx_cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor::Cursor() = default;
|
bool Type::operator==(const Type& rhs) const {
|
||||||
|
|
||||||
Cursor::Cursor(const CXCursor& other) : cx_cursor(cx_cursor) {}
|
|
||||||
|
|
||||||
bool Cursor::Type::operator==(const Cursor::Type& rhs) const {
|
|
||||||
return clang_equalTypes(cx_type, rhs.cx_type);
|
return clang_equalTypes(cx_type, rhs.cx_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
CXCursorKind Cursor::get_kind() const {
|
Type Type::get_result() const {
|
||||||
return clang_getCursorKind(cx_cursor);
|
return Type(clang_getResultType(cx_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor::Type Cursor::get_type() const {
|
Cursor::Cursor() : cx_cursor(clang_getNullCursor()) {}
|
||||||
return Type(clang_getCursorType(cx_cursor));
|
|
||||||
|
Cursor::Cursor(const CXCursor& other) : cx_cursor(other) {}
|
||||||
|
|
||||||
|
Cursor::operator bool() const {
|
||||||
|
return !clang_Cursor_isNull(cx_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cursor::operator==(const Cursor& rhs) const {
|
||||||
|
return clang_equalCursors(cx_cursor, rhs.cx_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
CXCursorKind Cursor::get_kind() const {
|
||||||
|
return cx_cursor.kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceLocation Cursor::get_source_location() const {
|
SourceLocation Cursor::get_source_location() const {
|
||||||
@ -37,15 +47,19 @@ SourceRange Cursor::get_source_range() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string Cursor::get_spelling() const {
|
std::string Cursor::get_spelling() const {
|
||||||
return to_string(clang_getCursorSpelling(cx_cursor));
|
return ToString(clang_getCursorSpelling(cx_cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Cursor::get_display_name() const {
|
std::string Cursor::get_display_name() const {
|
||||||
return to_string(clang_getCursorDisplayName(cx_cursor));
|
return ToString(clang_getCursorDisplayName(cx_cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Cursor::get_usr() const {
|
std::string Cursor::get_usr() const {
|
||||||
return to_string(clang_getCursorUSR(cx_cursor));
|
return ToString(clang_getCursorUSR(cx_cursor));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cursor::is_definition() const {
|
||||||
|
return clang_isCursorDefinition(cx_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor Cursor::get_referenced() const {
|
Cursor Cursor::get_referenced() const {
|
||||||
@ -72,14 +86,6 @@ std::vector<Cursor> Cursor::get_arguments() const {
|
|||||||
return cursors;
|
return cursors;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor::operator bool() const {
|
|
||||||
return !clang_Cursor_isNull(cx_cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Cursor::operator==(const Cursor& rhs) const {
|
|
||||||
return clang_equalCursors(cx_cursor, rhs.cx_cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Cursor::is_valid_kind() const {
|
bool Cursor::is_valid_kind() const {
|
||||||
CXCursor referenced = clang_getCursorReferenced(cx_cursor);
|
CXCursor referenced = clang_getCursorReferenced(cx_cursor);
|
||||||
if (clang_Cursor_isNull(referenced))
|
if (clang_Cursor_isNull(referenced))
|
||||||
@ -96,13 +102,13 @@ std::string Cursor::get_type_description() const {
|
|||||||
auto referenced = clang_getCursorReferenced(cx_cursor);
|
auto referenced = clang_getCursorReferenced(cx_cursor);
|
||||||
if (!clang_Cursor_isNull(referenced)) {
|
if (!clang_Cursor_isNull(referenced)) {
|
||||||
auto type = clang_getCursorType(referenced);
|
auto type = clang_getCursorType(referenced);
|
||||||
spelling = to_string(clang_getTypeSpelling(type));
|
spelling = ToString(clang_getTypeSpelling(type));
|
||||||
|
|
||||||
#if CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32
|
#if CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32
|
||||||
const std::string auto_str = "auto";
|
const std::string auto_str = "auto";
|
||||||
if (spelling.size() >= 4 && std::equal(auto_str.begin(), auto_str.end(), spelling.begin())) {
|
if (spelling.size() >= 4 && std::equal(auto_str.begin(), auto_str.end(), spelling.begin())) {
|
||||||
auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor));
|
auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor));
|
||||||
auto canonical_spelling = to_string(clang_getTypeSpelling(canonical_type));
|
auto canonical_spelling = ToString(clang_getTypeSpelling(canonical_type));
|
||||||
if (spelling.size() > 5 && spelling[4] == ' ' && spelling[5] == '&' && spelling != canonical_spelling)
|
if (spelling.size() > 5 && spelling[4] == ' ' && spelling[5] == '&' && spelling != canonical_spelling)
|
||||||
return canonical_spelling + " &";
|
return canonical_spelling + " &";
|
||||||
else
|
else
|
||||||
@ -112,7 +118,7 @@ std::string Cursor::get_type_description() const {
|
|||||||
const std::string const_auto_str = "const auto";
|
const std::string const_auto_str = "const auto";
|
||||||
if (spelling.size() >= 10 && std::equal(const_auto_str.begin(), const_auto_str.end(), spelling.begin())) {
|
if (spelling.size() >= 10 && std::equal(const_auto_str.begin(), const_auto_str.end(), spelling.begin())) {
|
||||||
auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor));
|
auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor));
|
||||||
auto canonical_spelling = to_string(clang_getTypeSpelling(canonical_type));
|
auto canonical_spelling = ToString(clang_getTypeSpelling(canonical_type));
|
||||||
if (spelling.size() > 11 && spelling[10] == ' ' && spelling[11] == '&' && spelling != canonical_spelling)
|
if (spelling.size() > 11 && spelling[10] == ' ' && spelling[11] == '&' && spelling != canonical_spelling)
|
||||||
return canonical_spelling + " &";
|
return canonical_spelling + " &";
|
||||||
else
|
else
|
||||||
@ -127,10 +133,10 @@ std::string Cursor::get_type_description() const {
|
|||||||
return spelling;
|
return spelling;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Cursor::get_brief_comments() const {
|
std::string Cursor::get_comments() const {
|
||||||
Cursor referenced = get_referenced();
|
Cursor referenced = get_referenced();
|
||||||
if (referenced)
|
if (referenced)
|
||||||
return to_string(clang_Cursor_getBriefCommentText(referenced.cx_cursor));
|
return ToString(clang_Cursor_getRawCommentText(referenced.cx_cursor));
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -3,28 +3,40 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <clang-c/Index.h>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <clang-c/Index.h>
|
||||||
#include "SourceLocation.h"
|
#include "SourceLocation.h"
|
||||||
#include "SourceRange.h"
|
#include "SourceRange.h"
|
||||||
|
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
class Cursor {
|
|
||||||
public:
|
|
||||||
|
|
||||||
class Type {
|
class Type {
|
||||||
public:
|
public:
|
||||||
Type(const CXType &cx_type) : cx_type(cx_type) {}
|
Type(const CXType &cx_type) : cx_type(cx_type) {}
|
||||||
|
|
||||||
|
bool operator==(const Type& rhs) const;
|
||||||
|
|
||||||
std::string get_spelling() const;
|
std::string get_spelling() const;
|
||||||
Type get_result() const;
|
Type get_result() const;
|
||||||
bool operator==(const Cursor::Type& rhs) const;
|
|
||||||
|
|
||||||
CXType cx_type;
|
CXType cx_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class VisiterResult {
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Recurse
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cursor {
|
||||||
|
public:
|
||||||
Cursor();
|
Cursor();
|
||||||
explicit Cursor(const CXCursor& cx_cursor);
|
explicit Cursor(const CXCursor& other);
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
bool operator==(const Cursor& rhs) const;
|
||||||
|
|
||||||
CXCursorKind get_kind() const;
|
CXCursorKind get_kind() const;
|
||||||
Type get_type() const;
|
Type get_type() const;
|
||||||
@ -33,19 +45,34 @@ namespace clang {
|
|||||||
std::string get_spelling() const;
|
std::string get_spelling() const;
|
||||||
std::string get_display_name() const;
|
std::string get_display_name() const;
|
||||||
std::string get_usr() const;
|
std::string get_usr() const;
|
||||||
|
|
||||||
|
bool is_definition() const;
|
||||||
|
|
||||||
Cursor get_referenced() const;
|
Cursor get_referenced() const;
|
||||||
Cursor get_canonical() const;
|
Cursor get_canonical() const;
|
||||||
Cursor get_definition() const;
|
Cursor get_definition() const;
|
||||||
Cursor get_semantic_parent() const;
|
Cursor get_semantic_parent() const;
|
||||||
std::vector<Cursor> get_arguments() const;
|
std::vector<Cursor> get_arguments() const;
|
||||||
operator bool() const;
|
|
||||||
bool operator==(const Cursor& rhs) const;
|
|
||||||
|
|
||||||
bool is_valid_kind() const;
|
bool is_valid_kind() const;
|
||||||
std::string get_type_description() const;
|
std::string get_type_description() const;
|
||||||
std::string get_brief_comments() const;
|
std::string get_comments() const;
|
||||||
|
|
||||||
CXCursor cx_cursor = clang_getNullCursor();
|
template<typename TClientData>
|
||||||
|
using Visitor = VisiterResult(*)(Cursor cursor, Cursor parent, TClientData* client_data);
|
||||||
|
|
||||||
|
enum class VisitResult {
|
||||||
|
Completed, EndedEarly
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TClientData>
|
||||||
|
VisitResult VisitChildren(Visitor<TClientData> visitor, TClientData* client_data) const {
|
||||||
|
if (clang_visitChildren(cx_cursor, reinterpret_cast<CXCursorVisitor>(visitor), client_data) == 0)
|
||||||
|
return VisitResult::Completed;
|
||||||
|
return VisitResult::EndedEarly;
|
||||||
|
}
|
||||||
|
|
||||||
|
CXCursor cx_cursor;
|
||||||
};
|
};
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
||||||
#endif // CURSOR_H_
|
#endif // CURSOR_H_
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
clang::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic) {
|
clang::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic) {
|
||||||
severity=clang_getDiagnosticSeverity(cx_diagnostic);
|
severity=clang_getDiagnosticSeverity(cx_diagnostic);
|
||||||
severity_spelling=get_severity_spelling(severity);
|
severity_spelling=get_severity_spelling(severity);
|
||||||
spelling=to_string(clang_getDiagnosticSpelling(cx_diagnostic));
|
spelling=ToString(clang_getDiagnosticSpelling(cx_diagnostic));
|
||||||
|
|
||||||
SourceLocation start_location(clang_getDiagnosticLocation(cx_diagnostic));
|
SourceLocation start_location(clang_getDiagnosticLocation(cx_diagnostic));
|
||||||
path=start_location.get_path();
|
path=start_location.get_path();
|
||||||
@ -18,7 +18,7 @@ clang::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnos
|
|||||||
unsigned num_fix_its=clang_getDiagnosticNumFixIts(cx_diagnostic);
|
unsigned num_fix_its=clang_getDiagnosticNumFixIts(cx_diagnostic);
|
||||||
for(unsigned c=0;c<num_fix_its;c++) {
|
for(unsigned c=0;c<num_fix_its;c++) {
|
||||||
CXSourceRange fix_it_range;
|
CXSourceRange fix_it_range;
|
||||||
auto source=to_string(clang_getDiagnosticFixIt(cx_diagnostic, c, &fix_it_range));
|
auto source=ToString(clang_getDiagnosticFixIt(cx_diagnostic, c, &fix_it_range));
|
||||||
fix_its.emplace_back(source, SourceRange(fix_it_range).get_offsets());
|
fix_its.emplace_back(source, SourceRange(fix_it_range).get_offsets());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "SourceRange.h"
|
#include "SourceRange.h"
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
|
||||||
class Diagnostic {
|
class Diagnostic {
|
||||||
friend class TranslationUnit;
|
friend class TranslationUnit;
|
||||||
Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic);
|
Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic);
|
||||||
@ -27,6 +28,6 @@ namespace clang {
|
|||||||
std::pair<clang::Offset, clang::Offset> offsets;
|
std::pair<clang::Offset, clang::Offset> offsets;
|
||||||
std::vector<FixIt> fix_its;
|
std::vector<FixIt> fix_its;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
#endif // DIAGNOSTIC_H_
|
#endif // DIAGNOSTIC_H_
|
||||||
|
@ -31,7 +31,7 @@ void SourceLocation::get_data(std::string* path, unsigned *line, unsigned *colum
|
|||||||
CXFile file;
|
CXFile file;
|
||||||
clang_getExpansionLocation(cx_location, &file, line, column, offset);
|
clang_getExpansionLocation(cx_location, &file, line, column, offset);
|
||||||
if (file != nullptr) {
|
if (file != nullptr) {
|
||||||
*path = to_string(clang_getFileName(file));
|
*path = ToString(clang_getFileName(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ clang::SourceRange clang::Token::get_source_range() const {
|
|||||||
}
|
}
|
||||||
// returns a string description of this tokens kind
|
// returns a string description of this tokens kind
|
||||||
std::string clang::Token::get_spelling() const {
|
std::string clang::Token::get_spelling() const {
|
||||||
return to_string(clang_getTokenSpelling(cx_tu, cx_token));
|
return ToString(clang_getTokenSpelling(cx_tu, cx_token));
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::Token::Kind clang::Token::get_kind() const {
|
clang::Token::Kind clang::Token::get_kind() const {
|
||||||
|
@ -5,10 +5,9 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <iostream> //TODO: remove
|
namespace clang {
|
||||||
using namespace std; //TODO: remove
|
|
||||||
|
|
||||||
clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
|
TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
|
||||||
const std::vector<std::string> &command_line_args,
|
const std::vector<std::string> &command_line_args,
|
||||||
const std::string &buffer, unsigned flags) {
|
const std::string &buffer, unsigned flags) {
|
||||||
std::vector<const char*> args;
|
std::vector<const char*> args;
|
||||||
@ -25,7 +24,7 @@ clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_pa
|
|||||||
args.size(), files, 1, flags);
|
args.size(), files, 1, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
|
TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
|
||||||
const std::vector<std::string> &command_line_args,
|
const std::vector<std::string> &command_line_args,
|
||||||
unsigned flags) {
|
unsigned flags) {
|
||||||
std::vector<const char*> args;
|
std::vector<const char*> args;
|
||||||
@ -37,11 +36,11 @@ clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_pa
|
|||||||
args.size(), nullptr, 0, flags);
|
args.size(), nullptr, 0, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::TranslationUnit::~TranslationUnit() {
|
TranslationUnit::~TranslationUnit() {
|
||||||
clang_disposeTranslationUnit(cx_tu);
|
clang_disposeTranslationUnit(cx_tu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clang::TranslationUnit::parse(Index &index, const std::string &file_path,
|
void TranslationUnit::parse(Index &index, const std::string &file_path,
|
||||||
const std::vector<std::string> &command_line_args,
|
const std::vector<std::string> &command_line_args,
|
||||||
const std::map<std::string, std::string> &buffers, unsigned flags) {
|
const std::map<std::string, std::string> &buffers, unsigned flags) {
|
||||||
std::vector<CXUnsavedFile> files;
|
std::vector<CXUnsavedFile> files;
|
||||||
@ -60,10 +59,10 @@ void clang::TranslationUnit::parse(Index &index, const std::string &file_path,
|
|||||||
args.size(), files.data(), files.size(), flags);
|
args.size(), files.data(), files.size(), flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int clang::TranslationUnit::ReparseTranslationUnit(const std::string &buffer, unsigned flags) {
|
int TranslationUnit::ReparseTranslationUnit(const std::string &buffer, unsigned flags) {
|
||||||
CXUnsavedFile files[1];
|
CXUnsavedFile files[1];
|
||||||
|
|
||||||
auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu));
|
auto file_path = ToString(clang_getTranslationUnitSpelling(cx_tu));
|
||||||
|
|
||||||
files[0].Filename = file_path.c_str();
|
files[0].Filename = file_path.c_str();
|
||||||
files[0].Contents = buffer.c_str();
|
files[0].Contents = buffer.c_str();
|
||||||
@ -72,7 +71,7 @@ int clang::TranslationUnit::ReparseTranslationUnit(const std::string &buffer, un
|
|||||||
return clang_reparseTranslationUnit(cx_tu, 1, files, flags);
|
return clang_reparseTranslationUnit(cx_tu, 1, files, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned clang::TranslationUnit::DefaultFlags() {
|
unsigned TranslationUnit::DefaultFlags() {
|
||||||
auto flags =
|
auto flags =
|
||||||
CXTranslationUnit_CacheCompletionResults |
|
CXTranslationUnit_CacheCompletionResults |
|
||||||
CXTranslationUnit_PrecompiledPreamble |
|
CXTranslationUnit_PrecompiledPreamble |
|
||||||
@ -84,13 +83,13 @@ unsigned clang::TranslationUnit::DefaultFlags() {
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::CodeCompleteResults clang::TranslationUnit::get_code_completions(const std::string &buffer,
|
CodeCompleteResults TranslationUnit::get_code_completions(const std::string &buffer,
|
||||||
unsigned line_number, unsigned column) {
|
unsigned line_number, unsigned column) {
|
||||||
CodeCompleteResults results(cx_tu, buffer, line_number, column);
|
CodeCompleteResults results(cx_tu, buffer, line_number, column);
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<clang::Diagnostic> clang::TranslationUnit::get_diagnostics() {
|
std::vector<Diagnostic> TranslationUnit::get_diagnostics() {
|
||||||
std::vector<Diagnostic> diagnostics;
|
std::vector<Diagnostic> diagnostics;
|
||||||
for (unsigned c = 0; c < clang_getNumDiagnostics(cx_tu); c++) {
|
for (unsigned c = 0; c < clang_getNumDiagnostics(cx_tu); c++) {
|
||||||
CXDiagnostic clang_diagnostic = clang_getDiagnostic(cx_tu, c);
|
CXDiagnostic clang_diagnostic = clang_getDiagnostic(cx_tu, c);
|
||||||
@ -100,29 +99,34 @@ std::vector<clang::Diagnostic> clang::TranslationUnit::get_diagnostics() {
|
|||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<clang::Tokens> clang::TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) {
|
std::unique_ptr<Tokens> TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) {
|
||||||
auto path=clang::to_string(clang_getTranslationUnitSpelling(cx_tu));
|
auto path = ToString(clang_getTranslationUnitSpelling(cx_tu));
|
||||||
SourceLocation start_location(cx_tu, path, start_offset);
|
SourceLocation start_location(cx_tu, path, start_offset);
|
||||||
SourceLocation end_location(cx_tu, path, end_offset);
|
SourceLocation end_location(cx_tu, path, end_offset);
|
||||||
SourceRange range(start_location, end_location);
|
SourceRange range(start_location, end_location);
|
||||||
return std::unique_ptr<Tokens>(new Tokens(cx_tu, range));
|
return std::unique_ptr<Tokens>(new Tokens(cx_tu, range));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<clang::Tokens> clang::TranslationUnit::get_tokens(unsigned start_line, unsigned start_column,
|
std::unique_ptr<Tokens> TranslationUnit::get_tokens(unsigned start_line, unsigned start_column, unsigned end_line, unsigned end_column) {
|
||||||
unsigned end_line, unsigned end_column) {
|
auto path = ToString(clang_getTranslationUnitSpelling(cx_tu));
|
||||||
auto path=to_string(clang_getTranslationUnitSpelling(cx_tu));
|
|
||||||
SourceLocation start_location(cx_tu, path, start_line, start_column);
|
SourceLocation start_location(cx_tu, path, start_line, start_column);
|
||||||
SourceLocation end_location(cx_tu, path, end_line, end_column);
|
SourceLocation end_location(cx_tu, path, end_line, end_column);
|
||||||
SourceRange range(start_location, end_location);
|
SourceRange range(start_location, end_location);
|
||||||
return std::unique_ptr<Tokens>(new Tokens(cx_tu, range));
|
return std::unique_ptr<Tokens>(new Tokens(cx_tu, range));
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::Cursor clang::TranslationUnit::get_cursor(std::string path, unsigned offset) {
|
Cursor TranslationUnit::document_cursor() const {
|
||||||
|
return Cursor(clang_getTranslationUnitCursor(cx_tu));
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor TranslationUnit::get_cursor(std::string path, unsigned offset) {
|
||||||
SourceLocation location(cx_tu, path, offset);
|
SourceLocation location(cx_tu, path, offset);
|
||||||
return Cursor(clang_getCursor(cx_tu, location.cx_location));
|
return Cursor(clang_getCursor(cx_tu, location.cx_location));
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::Cursor clang::TranslationUnit::get_cursor(std::string path, unsigned line, unsigned column) {
|
Cursor TranslationUnit::get_cursor(std::string path, unsigned line, unsigned column) {
|
||||||
SourceLocation location(cx_tu, path, line, column);
|
SourceLocation location(cx_tu, path, line, column);
|
||||||
return Cursor(clang_getCursor(cx_tu, location.cx_location));
|
return Cursor(clang_getCursor(cx_tu, location.cx_location));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
@ -44,6 +44,8 @@ namespace clang {
|
|||||||
std::unique_ptr<Tokens> get_tokens(unsigned start_line, unsigned start_column,
|
std::unique_ptr<Tokens> get_tokens(unsigned start_line, unsigned start_column,
|
||||||
unsigned end_line, unsigned end_column);
|
unsigned end_line, unsigned end_column);
|
||||||
|
|
||||||
|
Cursor document_cursor() const;
|
||||||
|
|
||||||
clang::Cursor get_cursor(std::string path, unsigned offset);
|
clang::Cursor get_cursor(std::string path, unsigned offset);
|
||||||
clang::Cursor get_cursor(std::string path, unsigned line, unsigned column);
|
clang::Cursor get_cursor(std::string path, unsigned line, unsigned column);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "Utility.h"
|
#include "Utility.h"
|
||||||
|
|
||||||
std::string clang::to_string(CXString cx_string) {
|
std::string clang::ToString(CXString cx_string) {
|
||||||
std::string string;
|
std::string string;
|
||||||
if(cx_string.data!=nullptr) {
|
if(cx_string.data!=nullptr) {
|
||||||
string=clang_getCString(cx_string);
|
string=clang_getCString(cx_string);
|
||||||
@ -9,7 +9,7 @@ std::string clang::to_string(CXString cx_string) {
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string clang::to_string(CXCursorKind kind) {
|
std::string clang::ToString(CXCursorKind kind) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case CXCursor_UnexposedDecl: return "UnexposedDecl";
|
case CXCursor_UnexposedDecl: return "UnexposedDecl";
|
||||||
case CXCursor_StructDecl: return "StructDecl";
|
case CXCursor_StructDecl: return "StructDecl";
|
||||||
@ -177,5 +177,5 @@ std::string clang::to_string(CXCursorKind kind) {
|
|||||||
//case CXCursor_FirstExtraDecl: return "FirstExtraDecl";
|
//case CXCursor_FirstExtraDecl: return "FirstExtraDecl";
|
||||||
case CXCursor_LastExtraDecl: return "LastExtraDecl";
|
case CXCursor_LastExtraDecl: return "LastExtraDecl";
|
||||||
}
|
}
|
||||||
return "";
|
return "<unknown kind>";
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#ifndef UTILITY_H_
|
#pragma once
|
||||||
#define UTILITY_H_
|
|
||||||
#include <clang-c/Index.h>
|
#include <clang-c/Index.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
std::string to_string(CXString cx_string);
|
|
||||||
std::string to_string(CXCursorKind cursor_kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // UTILITY_H_
|
std::string ToString(CXString cx_string);
|
||||||
|
std::string ToString(CXCursorKind cursor_kind);
|
||||||
|
|
||||||
|
} // namespace clang
|
548
main.cpp
548
main.cpp
@ -1,85 +1,513 @@
|
|||||||
|
#include <optional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
#include "libclangmm\clangmm.h"
|
#include "libclangmm\clangmm.h"
|
||||||
#include "libclangmm\Utility.h"
|
#include "libclangmm\Utility.h"
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
//#include <clang-c\Index.h>
|
//#include <clang-c\Index.h>
|
||||||
|
|
||||||
CXChildVisitResult visitPrint(CXCursor cursor0, CXCursor parent, CXClientData param) {
|
|
||||||
int* level = static_cast<int*>(param);
|
|
||||||
|
|
||||||
clang::Cursor cursor(cursor0);
|
// While indexing, we should refer to symbols by USR. When joining into the db, we can have optimized access.
|
||||||
|
|
||||||
for (int i = 0; i < *level; ++i)
|
struct TypeDef;
|
||||||
std::cout << " ";
|
struct FuncDef;
|
||||||
std::cout << cursor.get_spelling() << " " << clang::to_string(cursor.get_kind()) << std::endl;
|
struct VarDef;
|
||||||
|
|
||||||
*level += 1;
|
|
||||||
clang_visitChildren(cursor0, &visitPrint, level);
|
|
||||||
*level -= 1;
|
|
||||||
|
|
||||||
return CXChildVisitResult::CXChildVisit_Continue;
|
template<typename T>
|
||||||
/*
|
struct Id {
|
||||||
echo " ".repeat(param.level), cursor.kind, " ", getSpelling(cursor)
|
uint64_t file_id;
|
||||||
|
uint64_t local_id;
|
||||||
|
|
||||||
var visitParam : VisitPrintParam
|
Id() : file_id(0), local_id(0) {} // Needed for containers. Do not use directly.
|
||||||
visitParam.level = param.level + 1
|
Id(uint64_t file_id, uint64_t local_id)
|
||||||
visitChildren(cursor, visitPrint, addr visitParam)
|
: file_id(file_id), local_id(local_id) {}
|
||||||
|
};
|
||||||
|
using TypeId = Id<TypeDef>;
|
||||||
|
using FuncId = Id<FuncDef>;
|
||||||
|
using VarId = Id<VarDef>;
|
||||||
|
|
||||||
return VisitResult.Continu
|
|
||||||
*/
|
template<typename T>
|
||||||
|
struct Ref {
|
||||||
|
Id<T> id;
|
||||||
|
clang::SourceLocation loc;
|
||||||
|
};
|
||||||
|
using TypeRef = Ref<TypeDef>;
|
||||||
|
using FuncRef = Ref<FuncDef>;
|
||||||
|
using VarRef = Ref<VarDef>;
|
||||||
|
|
||||||
|
|
||||||
|
struct TypeDef {
|
||||||
|
TypeDef(TypeId id);
|
||||||
|
|
||||||
|
// General metadata.
|
||||||
|
TypeId id;
|
||||||
|
std::string usr;
|
||||||
|
std::string shortName;
|
||||||
|
std::string qualifiedName;
|
||||||
|
std::optional<clang::SourceLocation> definition;
|
||||||
|
|
||||||
|
// Immediate parent and immediate derived types.
|
||||||
|
std::vector<TypeId> parents;
|
||||||
|
std::vector<TypeId> derived;
|
||||||
|
|
||||||
|
// Types, functions, and variables defined in this type.
|
||||||
|
std::vector<TypeId> types;
|
||||||
|
std::vector<FuncId> funcs;
|
||||||
|
std::vector<VarId> vars;
|
||||||
|
|
||||||
|
// Usages.
|
||||||
|
std::vector<clang::SourceLocation> uses;
|
||||||
|
};
|
||||||
|
|
||||||
|
TypeDef::TypeDef(TypeId id) : id(id) {}
|
||||||
|
|
||||||
|
struct FuncDef {
|
||||||
|
FuncDef(FuncId id);
|
||||||
|
|
||||||
|
// General metadata.
|
||||||
|
FuncId id;
|
||||||
|
std::string usr;
|
||||||
|
std::string shortName;
|
||||||
|
std::string qualifiedName;
|
||||||
|
std::optional<clang::SourceLocation> declaration;
|
||||||
|
std::optional<clang::SourceLocation> definition;
|
||||||
|
|
||||||
|
// Type which declares this one (ie, it is a method)
|
||||||
|
std::optional<TypeId> declaringType;
|
||||||
|
// Method this method overrides.
|
||||||
|
std::optional<FuncId> baseFunc;
|
||||||
|
// Methods which directly override this one.
|
||||||
|
std::vector<FuncId> derived;
|
||||||
|
|
||||||
|
// Local variables defined in this function.
|
||||||
|
std::vector<VarId> locals;
|
||||||
|
|
||||||
|
// Functions which call this one.
|
||||||
|
std::vector<FuncRef> callers;
|
||||||
|
// Functions that this function calls.
|
||||||
|
std::vector<FuncRef> callees;
|
||||||
|
|
||||||
|
// Usages.
|
||||||
|
std::vector<clang::SourceLocation> uses;
|
||||||
|
};
|
||||||
|
|
||||||
|
FuncDef::FuncDef(FuncId id) : id(id) {}
|
||||||
|
|
||||||
|
struct VarDef {
|
||||||
|
VarDef(VarId id);
|
||||||
|
|
||||||
|
// General metadata.
|
||||||
|
VarId id;
|
||||||
|
std::string usr;
|
||||||
|
std::string shortName;
|
||||||
|
std::string qualifiedName;
|
||||||
|
std::optional<clang::SourceLocation> declaration;
|
||||||
|
std::vector<clang::SourceLocation> initializations;
|
||||||
|
|
||||||
|
// Type of the variable.
|
||||||
|
std::optional<TypeId> variableType;
|
||||||
|
|
||||||
|
// Type which declares this one (ie, it is a method)
|
||||||
|
std::optional<TypeId> declaringType;
|
||||||
|
|
||||||
|
// Usages.
|
||||||
|
std::vector<clang::SourceLocation> uses;
|
||||||
|
};
|
||||||
|
|
||||||
|
VarDef::VarDef(VarId id) : id(id) {}
|
||||||
|
|
||||||
|
|
||||||
|
struct ParsingDatabase {
|
||||||
|
// NOTE: Every Id is resolved to a file_id of 0. The correct file_id needs
|
||||||
|
// to get fixed up when inserting into the real db.
|
||||||
|
std::unordered_map<std::string, TypeId> usrToTypeId;
|
||||||
|
std::unordered_map<std::string, FuncId> usrToFuncId;
|
||||||
|
std::unordered_map<std::string, VarId> usrToVarId;
|
||||||
|
|
||||||
|
std::vector<TypeDef> types;
|
||||||
|
std::vector<FuncDef> funcs;
|
||||||
|
std::vector<VarDef> vars;
|
||||||
|
|
||||||
|
TypeId ToTypeId(const std::string& usr);
|
||||||
|
FuncId ToFuncId(const std::string& usr);
|
||||||
|
VarId ToVarId(const std::string& usr);
|
||||||
|
|
||||||
|
TypeDef* Resolve(TypeId id);
|
||||||
|
FuncDef* Resolve(FuncId id);
|
||||||
|
VarDef* Resolve(VarId id);
|
||||||
|
|
||||||
|
std::vector<std::string> ToString();
|
||||||
|
};
|
||||||
|
|
||||||
|
TypeId ParsingDatabase::ToTypeId(const std::string& usr) {
|
||||||
|
auto it = usrToTypeId.find(usr);
|
||||||
|
if (it != usrToTypeId.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
TypeId id(0, types.size());
|
||||||
|
types.push_back(TypeDef(id));
|
||||||
|
usrToTypeId[usr] = id;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
FuncId ParsingDatabase::ToFuncId(const std::string& usr) {
|
||||||
|
auto it = usrToFuncId.find(usr);
|
||||||
|
if (it != usrToFuncId.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
FuncId id(0, funcs.size());
|
||||||
|
funcs.push_back(FuncDef(id));
|
||||||
|
usrToFuncId[usr] = id;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
VarId ParsingDatabase::ToVarId(const std::string& usr) {
|
||||||
|
auto it = usrToVarId.find(usr);
|
||||||
|
if (it != usrToVarId.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
VarId id(0, vars.size());
|
||||||
|
vars.push_back(VarDef(id));
|
||||||
|
usrToVarId[usr] = id;
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
TypeDef* ParsingDatabase::Resolve(TypeId id) {
|
||||||
|
return &types[id.local_id];
|
||||||
|
}
|
||||||
|
FuncDef* ParsingDatabase::Resolve(FuncId id) {
|
||||||
|
return &funcs[id.local_id];
|
||||||
|
}
|
||||||
|
VarDef* ParsingDatabase::Resolve(VarId id) {
|
||||||
|
return &vars[id.local_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ParsingDatabase::ToString() {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
|
||||||
|
result.push_back("Types:");
|
||||||
|
for (TypeDef& def : types) {
|
||||||
|
result.push_back(" " + def.qualifiedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_back("Funcs:");
|
||||||
|
for (FuncDef& def : funcs) {
|
||||||
|
result.push_back(" " + def.qualifiedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_back("Vars:");
|
||||||
|
for (VarDef& def : vars) {
|
||||||
|
result.push_back(" " + def.qualifiedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileDef {
|
||||||
|
uint64_t id;
|
||||||
|
std::string path;
|
||||||
|
std::vector<TypeDef> types;
|
||||||
|
std::vector<FuncDef> funcs;
|
||||||
|
std::vector<VarDef> vars;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct Database {
|
||||||
|
std::unordered_map<std::string, TypeId> usrToTypeId;
|
||||||
|
std::unordered_map<std::string, FuncId> usrToFuncId;
|
||||||
|
std::unordered_map<std::string, VarId> usrToVarId;
|
||||||
|
|
||||||
|
std::vector<FileDef> files;
|
||||||
|
|
||||||
|
TypeId ToTypeId(const std::string& usr);
|
||||||
|
FuncId ToFuncId(const std::string& usr);
|
||||||
|
VarId ToVarId(const std::string& usr);
|
||||||
|
};
|
||||||
|
|
||||||
|
TypeId Database::ToTypeId(const std::string& usr) {
|
||||||
|
auto it = usrToTypeId.find(usr);
|
||||||
|
assert(it != usrToTypeId.end() && "Usr is not registered");
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
FuncId Database::ToFuncId(const std::string& usr) {
|
||||||
|
auto it = usrToFuncId.find(usr);
|
||||||
|
assert(it != usrToFuncId.end() && "Usr is not registered");
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
VarId Database::ToVarId(const std::string& usr) {
|
||||||
|
auto it = usrToVarId.find(usr);
|
||||||
|
assert(it != usrToVarId.end() && "Usr is not registered");
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDef* Resolve(FileDef* file, TypeId id) {
|
||||||
|
assert(file->id == id.file_id);
|
||||||
|
return &file->types[id.local_id];
|
||||||
|
}
|
||||||
|
FuncDef* Resolve(FileDef* file, FuncId id) {
|
||||||
|
assert(file->id == id.file_id);
|
||||||
|
return &file->funcs[id.local_id];
|
||||||
|
}
|
||||||
|
VarDef* Resolve(FileDef* file, VarId id) {
|
||||||
|
assert(file->id == id.file_id);
|
||||||
|
return &file->vars[id.local_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDef* Resolve(Database* db, TypeId id) {
|
||||||
|
return Resolve(&db->files[id.file_id], id);
|
||||||
|
}
|
||||||
|
FuncDef* Resolve(Database* db, FuncId id) {
|
||||||
|
return Resolve(&db->files[id.file_id], id);
|
||||||
|
}
|
||||||
|
VarDef* Resolve(Database* db, VarId id) {
|
||||||
|
return Resolve(&db->files[id.file_id], id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct NamespaceStack {
|
||||||
|
std::vector<std::string> stack;
|
||||||
|
|
||||||
|
void Push(const std::string& ns);
|
||||||
|
void Pop();
|
||||||
|
std::string ComputeQualifiedPrefix();
|
||||||
|
};
|
||||||
|
|
||||||
|
void NamespaceStack::Push(const std::string& ns) {
|
||||||
|
stack.push_back(ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NamespaceStack::Pop() {
|
||||||
|
stack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NamespaceStack::ComputeQualifiedPrefix() {
|
||||||
|
std::string result;
|
||||||
|
for (const std::string& ns : stack)
|
||||||
|
result += ns + "::";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct FuncDefinitionParam {};
|
||||||
|
|
||||||
|
clang::VisiterResult VisitFuncDefinition(clang::Cursor cursor, clang::Cursor parent, FuncDefinitionParam* param) {
|
||||||
/*
|
/*
|
||||||
echo "Parsing ", filename
|
switch (cursor.get_kind()) {
|
||||||
let index : libclang.CXIndex = libclang.createIndex(#[excludeDeclsFromPCH]# 0, #[displayDiagnostics]# 0)
|
|
||||||
|
|
||||||
let translationUnit : libclang.CXTranslationUnit =
|
default:
|
||||||
libclang.parseTranslationUnit(index, filename.cstring, nil, 0, nil, 0, 0)
|
std::cerr << "Unhandled VisitFuncDefinition kind " << clang::ToString(cursor.get_kind()) << std::endl;
|
||||||
if translationUnit == nil :
|
break;
|
||||||
echo "Error: cannot create translation unit for '", filename, "'"
|
}
|
||||||
return
|
|
||||||
|
|
||||||
let cursor = libclang.getTranslationUnitCursor(translationUnit)
|
|
||||||
|
|
||||||
var param : VisitFileParam
|
|
||||||
param.db = db
|
|
||||||
|
|
||||||
echo "=== START AST ==="
|
|
||||||
# printChildren(cursor, 0)
|
|
||||||
echo "=== DONE AST ==="
|
|
||||||
|
|
||||||
var file : File
|
|
||||||
file.path = filename
|
|
||||||
file.vars = newList[VarRef]()
|
|
||||||
file.funcs = newList[FuncRef]()
|
|
||||||
file.types = newList[TypeRef]()
|
|
||||||
|
|
||||||
visitChildren(cursor, visitFile, addr param)
|
|
||||||
|
|
||||||
libclang.disposeTranslationUnit(translationUnit)
|
|
||||||
libclang.disposeIndex(index)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
return clang::VisiterResult::Break;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleFunc(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor func, std::optional<TypeId> declaringType) {
|
||||||
|
// What this method must process:
|
||||||
|
// - function declaration
|
||||||
|
// - function definition
|
||||||
|
// - method declaration
|
||||||
|
// - method inline definition
|
||||||
|
// - method definition
|
||||||
|
|
||||||
|
// TODO: Make sure we only process once for declared/defined types.
|
||||||
|
|
||||||
|
// TODO: method_definition_in_namespace.cc is failing because we process decl with correct declaringType, but
|
||||||
|
// processing method definition fails to resolve correct declaringType.
|
||||||
|
//if (func.is_definition()) // RM after addressed above
|
||||||
|
// return; // RM after addressed above
|
||||||
|
|
||||||
|
FuncId id = db->ToFuncId(func.get_usr());
|
||||||
|
db->Resolve(id)->shortName = func.get_spelling();
|
||||||
|
|
||||||
|
std::string typeName;
|
||||||
|
if (declaringType)
|
||||||
|
typeName = db->Resolve(declaringType.value())->shortName + "::";
|
||||||
|
db->Resolve(id)->qualifiedName = ns->ComputeQualifiedPrefix() + typeName + func.get_spelling();
|
||||||
|
std::cout << func.get_usr() << ": Set qualified name to " << db->Resolve(id)->qualifiedName << std::endl;
|
||||||
|
|
||||||
|
//std::cout << "!! HandleFunc " << func.get_type_description() << std::endl;
|
||||||
|
//std::cout << " comment: " << func.get_comments() << std::endl;
|
||||||
|
//std::cout << " spelling: " << func.get_spelling() << std::endl;
|
||||||
|
//for (clang::Cursor argument : func.get_arguments())
|
||||||
|
// std::cout << " arg: " << clang::ToString(argument.get_kind()) << " " << argument.get_spelling() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct ClassDeclParam {
|
||||||
|
ParsingDatabase* db;
|
||||||
|
NamespaceStack* ns;
|
||||||
|
TypeId active_type;
|
||||||
|
|
||||||
|
ClassDeclParam(ParsingDatabase* db, NamespaceStack* ns, TypeId active_type)
|
||||||
|
: db(db), ns(ns), active_type(active_type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
clang::VisiterResult VisitClassDecl(clang::Cursor cursor, clang::Cursor parent, ClassDeclParam* param) {
|
||||||
|
ParsingDatabase* db = param->db;
|
||||||
|
|
||||||
|
switch (cursor.get_kind()) {
|
||||||
|
case CXCursor_CXXMethod:
|
||||||
|
HandleFunc(param->db, param->ns, cursor, param->active_type);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
std::cerr << "Unhandled VisitClassDecl kind " << clang::ToString(cursor.get_kind()) << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clang::VisiterResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleClassDecl(clang::Cursor cursor, ParsingDatabase* db, NamespaceStack* ns) {
|
||||||
|
TypeId active_type = db->ToTypeId(cursor.get_usr());
|
||||||
|
db->Resolve(active_type)->shortName = cursor.get_spelling();
|
||||||
|
db->Resolve(active_type)->qualifiedName = ns->ComputeQualifiedPrefix() + cursor.get_spelling();
|
||||||
|
|
||||||
|
ClassDeclParam classDeclParam(db, ns, active_type);
|
||||||
|
cursor.VisitChildren(&VisitClassDecl, &classDeclParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct FileParam {
|
||||||
|
ParsingDatabase* db;
|
||||||
|
NamespaceStack* ns;
|
||||||
|
|
||||||
|
FileParam(ParsingDatabase* db, NamespaceStack* ns) : db(db), ns(ns) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
clang::VisiterResult VisitFile(clang::Cursor cursor, clang::Cursor parent, FileParam* param) {
|
||||||
|
switch (cursor.get_kind()) {
|
||||||
|
case CXCursor_Namespace:
|
||||||
|
// For a namespace, visit the children of the namespace, but this time with
|
||||||
|
// a pushed namespace stack.
|
||||||
|
param->ns->Push(cursor.get_display_name());
|
||||||
|
cursor.VisitChildren(&VisitFile, param);
|
||||||
|
param->ns->Pop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CXCursor_ClassDecl:
|
||||||
|
// TODO: Cleanup Handle* param order.
|
||||||
|
HandleClassDecl(cursor, param->db, param->ns);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CXCursor_CXXMethod:
|
||||||
|
case CXCursor_FunctionDecl:
|
||||||
|
HandleFunc(param->db, param->ns, cursor, std::nullopt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
std::cerr << "Unhandled VisitFile kind " << clang::ToString(cursor.get_kind()) << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clang::VisiterResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
clang::VisiterResult DumpVisitor(clang::Cursor cursor, clang::Cursor parent, int* level) {
|
||||||
|
for (int i = 0; i < *level; ++i)
|
||||||
|
std::cout << " ";
|
||||||
|
std::cout << cursor.get_spelling() << " " << clang::ToString(cursor.get_kind()) << std::endl;
|
||||||
|
|
||||||
|
*level += 1;
|
||||||
|
cursor.VisitChildren(&DumpVisitor, level);
|
||||||
|
*level -= 1;
|
||||||
|
|
||||||
|
return clang::VisiterResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dump(clang::Cursor cursor) {
|
||||||
|
int level = 0;
|
||||||
|
cursor.VisitChildren(&DumpVisitor, &level);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsingDatabase Parse(std::string filename) {
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
|
||||||
clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/);
|
clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/);
|
||||||
clang::TranslationUnit translationUnit(index, "test.cc", args);
|
clang::TranslationUnit tu(index, filename, args);
|
||||||
|
|
||||||
int level = 0;
|
//std::cout << "Start document dump" << std::endl;
|
||||||
auto cursor = clang_getTranslationUnitCursor(translationUnit.cx_tu);
|
//Dump(tu.document_cursor());
|
||||||
clang_visitChildren(cursor, &visitPrint, &level);
|
//std::cout << "Done document dump" << std::endl << std::endl;
|
||||||
/*
|
|
||||||
TranslationUnit(Index &index,
|
ParsingDatabase db;
|
||||||
const std::string &file_path,
|
NamespaceStack ns;
|
||||||
const std::vector<std::string> &command_line_args,
|
FileParam fileParam(&db, &ns);
|
||||||
const std::string &buffer,
|
tu.document_cursor().VisitChildren(&VisitFile, &fileParam);
|
||||||
unsigned flags = DefaultFlags());
|
return db;
|
||||||
TranslationUnit(Index &index,
|
}
|
||||||
const std::string &file_path,
|
|
||||||
const std::vector<std::string> &command_line_args,
|
template<typename T>
|
||||||
unsigned flags = DefaultFlags());
|
bool AreEqual(const std::vector<T>& a, const std::vector<T>& b) {
|
||||||
*/
|
if (a.size() != b.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < a.size(); ++i) {
|
||||||
|
if (a[i] != b[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(const std::vector<std::string>& strs) {
|
||||||
|
for (const std::string& str : strs) {
|
||||||
|
std::cout << str << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
for (std::string path : GetFilesInFolder("tests")) {
|
||||||
|
// TODO: Fix all existing tests.
|
||||||
|
if (path != "tests/method_definition_in_namespace.cc") continue;
|
||||||
|
|
||||||
|
std::vector<std::string> expected_output;
|
||||||
|
ParseTestExpectation(path, &expected_output);
|
||||||
|
|
||||||
|
std::cout << "[START] " << path << std::endl;
|
||||||
|
|
||||||
|
ParsingDatabase db = Parse(path);
|
||||||
|
std::vector<std::string> actual_output = db.ToString();
|
||||||
|
|
||||||
|
if (AreEqual(expected_output, actual_output)) {
|
||||||
|
std::cout << "[PASSED] " << path << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "[FAILED] " << path << std::endl;
|
||||||
|
std::cout << "Expected output for " << path << ":" << std::endl;
|
||||||
|
Write(expected_output);
|
||||||
|
std::cout << "Actual output for " << path << ":" << std::endl;
|
||||||
|
Write(actual_output);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::cin.get();
|
std::cin.get();
|
||||||
|
return 0;
|
||||||
}
|
}
|
2
test.cc
2
test.cc
@ -1,2 +1,2 @@
|
|||||||
void bar() {}
|
void bar(int a, int b) {}
|
||||||
void foo() {}
|
void foo() {}
|
9
tests/function_declaration.cc
Normal file
9
tests/function_declaration.cc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
void foo(int a, int b);
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
foo
|
||||||
|
Vars:
|
||||||
|
*/
|
11
tests/function_declaration_in_namespace.cc
Normal file
11
tests/function_declaration_in_namespace.cc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace hello {
|
||||||
|
void foo(int a, int b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
hello::foo
|
||||||
|
Vars:
|
||||||
|
*/
|
9
tests/function_definition.cc
Normal file
9
tests/function_definition.cc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
void foo(int a, int b) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
foo
|
||||||
|
Vars:
|
||||||
|
*/
|
11
tests/function_definition_in_namespace.cc
Normal file
11
tests/function_definition_in_namespace.cc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace hello {
|
||||||
|
void foo(int a, int b) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
hello::foo
|
||||||
|
Vars:
|
||||||
|
*/
|
11
tests/method_declaration.cc
Normal file
11
tests/method_declaration.cc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
class Foo {
|
||||||
|
void foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
foo
|
||||||
|
Vars:
|
||||||
|
*/
|
13
tests/method_declaration_in_namespace.cc
Normal file
13
tests/method_declaration_in_namespace.cc
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace hello {
|
||||||
|
class Foo {
|
||||||
|
void foo();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
hello::Foo::foo
|
||||||
|
Vars:
|
||||||
|
*/
|
13
tests/method_definition.cc
Normal file
13
tests/method_definition.cc
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
class Foo {
|
||||||
|
void foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
void Foo::foo() {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
foo
|
||||||
|
Vars:
|
||||||
|
*/
|
15
tests/method_definition_in_namespace.cc
Normal file
15
tests/method_definition_in_namespace.cc
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
namespace hello {
|
||||||
|
class Foo {
|
||||||
|
void foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
void Foo::foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
hello::Foo::foo
|
||||||
|
Vars:
|
||||||
|
*/
|
11
tests/method_inline_declaration.cc
Normal file
11
tests/method_inline_declaration.cc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
class Foo {
|
||||||
|
void foo() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
foo
|
||||||
|
Vars:
|
||||||
|
*/
|
13
tests/method_inline_declaration_in_namespace.cc
Normal file
13
tests/method_inline_declaration_in_namespace.cc
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace hello {
|
||||||
|
class Foo {
|
||||||
|
void foo() {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OUTPUT:
|
||||||
|
Types:
|
||||||
|
Funcs:
|
||||||
|
hello::Foo::foo
|
||||||
|
Vars:
|
||||||
|
*/
|
804
tinydir.h
Normal file
804
tinydir.h
Normal file
@ -0,0 +1,804 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2013-2016, tinydir authors:
|
||||||
|
- Cong Xu
|
||||||
|
- Lautis Sun
|
||||||
|
- Baudouin Feildel
|
||||||
|
- Andargor <andargor@yahoo.com>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef TINYDIR_H
|
||||||
|
#define TINYDIR_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((defined _UNICODE) && !(defined UNICODE))
|
||||||
|
#define UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((defined UNICODE) && !(defined _UNICODE))
|
||||||
|
#define _UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# include <windows.h>
|
||||||
|
# include <tchar.h>
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning (disable : 4996)
|
||||||
|
#else
|
||||||
|
# include <dirent.h>
|
||||||
|
# include <libgen.h>
|
||||||
|
# include <sys/stat.h>
|
||||||
|
# include <stddef.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <tchar.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* types */
|
||||||
|
|
||||||
|
/* Windows UNICODE wide character support */
|
||||||
|
#if defined _MSC_VER || defined __MINGW32__
|
||||||
|
#define _tinydir_char_t TCHAR
|
||||||
|
#define TINYDIR_STRING(s) _TEXT(s)
|
||||||
|
#define _tinydir_strlen _tcslen
|
||||||
|
#define _tinydir_strcpy _tcscpy
|
||||||
|
#define _tinydir_strcat _tcscat
|
||||||
|
#define _tinydir_strcmp _tcscmp
|
||||||
|
#define _tinydir_strrchr _tcsrchr
|
||||||
|
#define _tinydir_strncmp _tcsncmp
|
||||||
|
#else
|
||||||
|
#define _tinydir_char_t char
|
||||||
|
#define TINYDIR_STRING(s) s
|
||||||
|
#define _tinydir_strlen strlen
|
||||||
|
#define _tinydir_strcpy strcpy
|
||||||
|
#define _tinydir_strcat strcat
|
||||||
|
#define _tinydir_strcmp strcmp
|
||||||
|
#define _tinydir_strrchr strrchr
|
||||||
|
#define _tinydir_strncmp strncmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined _MSC_VER || defined __MINGW32__)
|
||||||
|
#include <windows.h>
|
||||||
|
#define _TINYDIR_PATH_MAX MAX_PATH
|
||||||
|
#elif defined __linux__
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#define _TINYDIR_PATH_MAX PATH_MAX
|
||||||
|
#else
|
||||||
|
#define _TINYDIR_PATH_MAX 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/* extra chars for the "\\*" mask */
|
||||||
|
# define _TINYDIR_PATH_EXTRA 2
|
||||||
|
#else
|
||||||
|
# define _TINYDIR_PATH_EXTRA 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _TINYDIR_FILENAME_MAX 256
|
||||||
|
|
||||||
|
#if (defined _MSC_VER || defined __MINGW32__)
|
||||||
|
#define _TINYDIR_DRIVE_MAX 3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# define _TINYDIR_FUNC static __inline
|
||||||
|
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
|
||||||
|
# define _TINYDIR_FUNC static __inline__
|
||||||
|
#else
|
||||||
|
# define _TINYDIR_FUNC static inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
|
||||||
|
#ifdef TINYDIR_USE_READDIR_R
|
||||||
|
|
||||||
|
/* readdir_r is a POSIX-only function, and may not be available under various
|
||||||
|
* environments/settings, e.g. MinGW. Use readdir fallback */
|
||||||
|
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
|
||||||
|
_POSIX_SOURCE
|
||||||
|
# define _TINYDIR_HAS_READDIR_R
|
||||||
|
#endif
|
||||||
|
#if _POSIX_C_SOURCE >= 200112L
|
||||||
|
# define _TINYDIR_HAS_FPATHCONF
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#if _BSD_SOURCE || _SVID_SOURCE || \
|
||||||
|
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
|
||||||
|
# define _TINYDIR_HAS_DIRFD
|
||||||
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
|
||||||
|
defined _PC_NAME_MAX
|
||||||
|
# define _TINYDIR_USE_FPATHCONF
|
||||||
|
#endif
|
||||||
|
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
|
||||||
|
!(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
|
||||||
|
# define _TINYDIR_USE_READDIR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Use readdir by default */
|
||||||
|
#else
|
||||||
|
# define _TINYDIR_USE_READDIR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#if (defined __MINGW32__) && (defined _UNICODE)
|
||||||
|
#define _TINYDIR_DIR _WDIR
|
||||||
|
#define _tinydir_dirent _wdirent
|
||||||
|
#define _tinydir_opendir _wopendir
|
||||||
|
#define _tinydir_readdir _wreaddir
|
||||||
|
#define _tinydir_closedir _wclosedir
|
||||||
|
#else
|
||||||
|
#define _TINYDIR_DIR DIR
|
||||||
|
#define _tinydir_dirent dirent
|
||||||
|
#define _tinydir_opendir opendir
|
||||||
|
#define _tinydir_readdir readdir
|
||||||
|
#define _tinydir_closedir closedir
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
|
||||||
|
#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
|
||||||
|
#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
|
||||||
|
#else
|
||||||
|
#error "Either define both alloc and free or none of them!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_TINYDIR_MALLOC)
|
||||||
|
#define _TINYDIR_MALLOC(_size) malloc(_size)
|
||||||
|
#define _TINYDIR_FREE(_ptr) free(_ptr)
|
||||||
|
#endif /* !defined(_TINYDIR_MALLOC) */
|
||||||
|
|
||||||
|
typedef struct tinydir_file
|
||||||
|
{
|
||||||
|
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||||
|
_tinydir_char_t name[_TINYDIR_FILENAME_MAX];
|
||||||
|
_tinydir_char_t *extension;
|
||||||
|
int is_dir;
|
||||||
|
int is_reg;
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
struct _stat _s;
|
||||||
|
#else
|
||||||
|
struct stat _s;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} tinydir_file;
|
||||||
|
|
||||||
|
typedef struct tinydir_dir
|
||||||
|
{
|
||||||
|
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||||
|
int has_next;
|
||||||
|
size_t n_files;
|
||||||
|
|
||||||
|
tinydir_file *_files;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
HANDLE _h;
|
||||||
|
WIN32_FIND_DATA _f;
|
||||||
|
#else
|
||||||
|
_TINYDIR_DIR *_d;
|
||||||
|
struct _tinydir_dirent *_e;
|
||||||
|
#ifndef _TINYDIR_USE_READDIR
|
||||||
|
struct _tinydir_dirent *_ep;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} tinydir_dir;
|
||||||
|
|
||||||
|
|
||||||
|
/* declarations */
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
void tinydir_close(tinydir_dir *dir);
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_next(tinydir_dir *dir);
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
void _tinydir_get_ext(tinydir_file *file);
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int _tinydir_file_cmp(const void *a, const void *b);
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#ifndef _TINYDIR_USE_READDIR
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* definitions*/
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||||
|
{
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#ifndef _TINYDIR_USE_READDIR
|
||||||
|
int error;
|
||||||
|
int size; /* using int size */
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
_tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
|
||||||
|
#endif
|
||||||
|
_tinydir_char_t *pathp;
|
||||||
|
|
||||||
|
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||||
|
{
|
||||||
|
errno = ENAMETOOLONG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialise dir */
|
||||||
|
dir->_files = NULL;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
dir->_h = INVALID_HANDLE_VALUE;
|
||||||
|
#else
|
||||||
|
dir->_d = NULL;
|
||||||
|
#ifndef _TINYDIR_USE_READDIR
|
||||||
|
dir->_ep = NULL;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
tinydir_close(dir);
|
||||||
|
|
||||||
|
_tinydir_strcpy(dir->path, path);
|
||||||
|
/* Remove trailing slashes */
|
||||||
|
pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
|
||||||
|
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
|
||||||
|
{
|
||||||
|
*pathp = TINYDIR_STRING('\0');
|
||||||
|
pathp++;
|
||||||
|
}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
_tinydir_strcpy(path_buf, dir->path);
|
||||||
|
_tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
|
||||||
|
dir->_h = FindFirstFile(path_buf, &dir->_f);
|
||||||
|
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
#else
|
||||||
|
dir->_d = _tinydir_opendir(path);
|
||||||
|
if (dir->_d == NULL)
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read first file */
|
||||||
|
dir->has_next = 1;
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#ifdef _TINYDIR_USE_READDIR
|
||||||
|
dir->_e = _tinydir_readdir(dir->_d);
|
||||||
|
#else
|
||||||
|
/* allocate dirent buffer for readdir_r */
|
||||||
|
size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
|
||||||
|
if (size == -1) return -1;
|
||||||
|
dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
|
||||||
|
if (dir->_ep == NULL) return -1;
|
||||||
|
|
||||||
|
error = readdir_r(dir->_d, dir->_ep, &dir->_e);
|
||||||
|
if (error != 0) return -1;
|
||||||
|
#endif
|
||||||
|
if (dir->_e == NULL)
|
||||||
|
{
|
||||||
|
dir->has_next = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bail:
|
||||||
|
tinydir_close(dir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||||
|
{
|
||||||
|
/* Count the number of files first, to pre-allocate the files array */
|
||||||
|
size_t n_files = 0;
|
||||||
|
if (tinydir_open(dir, path) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (dir->has_next)
|
||||||
|
{
|
||||||
|
n_files++;
|
||||||
|
if (tinydir_next(dir) == -1)
|
||||||
|
{
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tinydir_close(dir);
|
||||||
|
|
||||||
|
if (tinydir_open(dir, path) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir->n_files = 0;
|
||||||
|
dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
|
||||||
|
if (dir->_files == NULL)
|
||||||
|
{
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
while (dir->has_next)
|
||||||
|
{
|
||||||
|
tinydir_file *p_file;
|
||||||
|
dir->n_files++;
|
||||||
|
|
||||||
|
p_file = &dir->_files[dir->n_files - 1];
|
||||||
|
if (tinydir_readfile(dir, p_file) == -1)
|
||||||
|
{
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tinydir_next(dir) == -1)
|
||||||
|
{
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Just in case the number of files has changed between the first and
|
||||||
|
second reads, terminate without writing into unallocated memory */
|
||||||
|
if (dir->n_files == n_files)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bail:
|
||||||
|
tinydir_close(dir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
void tinydir_close(tinydir_dir *dir)
|
||||||
|
{
|
||||||
|
if (dir == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(dir->path, 0, sizeof(dir->path));
|
||||||
|
dir->has_next = 0;
|
||||||
|
dir->n_files = 0;
|
||||||
|
_TINYDIR_FREE(dir->_files);
|
||||||
|
dir->_files = NULL;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
if (dir->_h != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
FindClose(dir->_h);
|
||||||
|
}
|
||||||
|
dir->_h = INVALID_HANDLE_VALUE;
|
||||||
|
#else
|
||||||
|
if (dir->_d)
|
||||||
|
{
|
||||||
|
_tinydir_closedir(dir->_d);
|
||||||
|
}
|
||||||
|
dir->_d = NULL;
|
||||||
|
dir->_e = NULL;
|
||||||
|
#ifndef _TINYDIR_USE_READDIR
|
||||||
|
_TINYDIR_FREE(dir->_ep);
|
||||||
|
dir->_ep = NULL;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_next(tinydir_dir *dir)
|
||||||
|
{
|
||||||
|
if (dir == NULL)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!dir->has_next)
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
if (FindNextFile(dir->_h, &dir->_f) == 0)
|
||||||
|
#else
|
||||||
|
#ifdef _TINYDIR_USE_READDIR
|
||||||
|
dir->_e = _tinydir_readdir(dir->_d);
|
||||||
|
#else
|
||||||
|
if (dir->_ep == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (dir->_e == NULL)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
dir->has_next = 0;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
if (GetLastError() != ERROR_SUCCESS &&
|
||||||
|
GetLastError() != ERROR_NO_MORE_FILES)
|
||||||
|
{
|
||||||
|
tinydir_close(dir);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
|
||||||
|
{
|
||||||
|
if (dir == NULL || file == NULL)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||||
|
#else
|
||||||
|
if (dir->_e == NULL)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (_tinydir_strlen(dir->path) +
|
||||||
|
_tinydir_strlen(
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
dir->_f.cFileName
|
||||||
|
#else
|
||||||
|
dir->_e->d_name
|
||||||
|
#endif
|
||||||
|
) + 1 + _TINYDIR_PATH_EXTRA >=
|
||||||
|
_TINYDIR_PATH_MAX)
|
||||||
|
{
|
||||||
|
/* the path for the file will be too long */
|
||||||
|
errno = ENAMETOOLONG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (_tinydir_strlen(
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
dir->_f.cFileName
|
||||||
|
#else
|
||||||
|
dir->_e->d_name
|
||||||
|
#endif
|
||||||
|
) >= _TINYDIR_FILENAME_MAX)
|
||||||
|
{
|
||||||
|
errno = ENAMETOOLONG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tinydir_strcpy(file->path, dir->path);
|
||||||
|
_tinydir_strcat(file->path, TINYDIR_STRING("/"));
|
||||||
|
_tinydir_strcpy(file->name,
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
dir->_f.cFileName
|
||||||
|
#else
|
||||||
|
dir->_e->d_name
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
_tinydir_strcat(file->path, file->name);
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
if (_tstat(
|
||||||
|
#else
|
||||||
|
if (stat(
|
||||||
|
#endif
|
||||||
|
file->path, &file->_s) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_tinydir_get_ext(file);
|
||||||
|
|
||||||
|
file->is_dir =
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||||
|
#else
|
||||||
|
S_ISDIR(file->_s.st_mode);
|
||||||
|
#endif
|
||||||
|
file->is_reg =
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
|
||||||
|
(
|
||||||
|
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
|
||||||
|
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||||||
|
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
|
||||||
|
#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
|
||||||
|
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
|
||||||
|
#endif
|
||||||
|
#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
|
||||||
|
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
|
||||||
|
#endif
|
||||||
|
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
|
||||||
|
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
|
||||||
|
#else
|
||||||
|
S_ISREG(file->_s.st_mode);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
|
||||||
|
{
|
||||||
|
if (dir == NULL || file == NULL)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (i >= dir->n_files)
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(file, &dir->_files[i], sizeof(tinydir_file));
|
||||||
|
_tinydir_get_ext(file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
|
||||||
|
{
|
||||||
|
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||||
|
if (dir == NULL)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (i >= dir->n_files || !dir->_files[i].is_dir)
|
||||||
|
{
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tinydir_strcpy(path, dir->_files[i].path);
|
||||||
|
tinydir_close(dir);
|
||||||
|
if (tinydir_open_sorted(dir, path) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open a single file given its path */
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
|
||||||
|
{
|
||||||
|
tinydir_dir dir;
|
||||||
|
int result = 0;
|
||||||
|
int found = 0;
|
||||||
|
_tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
|
||||||
|
_tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
|
||||||
|
_tinydir_char_t *dir_name;
|
||||||
|
_tinydir_char_t *base_name;
|
||||||
|
#if (defined _MSC_VER || defined __MINGW32__)
|
||||||
|
_tinydir_char_t drive_buf[_TINYDIR_DRIVE_MAX];
|
||||||
|
_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||||
|
{
|
||||||
|
errno = ENAMETOOLONG;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the parent path */
|
||||||
|
#if (defined _MSC_VER || defined __MINGW32__)
|
||||||
|
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
|
||||||
|
_tsplitpath_s(
|
||||||
|
path,
|
||||||
|
drive_buf, _TINYDIR_DRIVE_MAX,
|
||||||
|
dir_name_buf, _TINYDIR_FILENAME_MAX,
|
||||||
|
file_name_buf, _TINYDIR_FILENAME_MAX,
|
||||||
|
ext_buf, _TINYDIR_FILENAME_MAX);
|
||||||
|
#else
|
||||||
|
_tsplitpath(
|
||||||
|
path,
|
||||||
|
drive_buf,
|
||||||
|
dir_name_buf,
|
||||||
|
file_name_buf,
|
||||||
|
ext_buf);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* _splitpath_s not work fine with only filename and widechar support */
|
||||||
|
#ifdef _UNICODE
|
||||||
|
if (drive_buf[0] == L'\xFEFE')
|
||||||
|
drive_buf[0] = '\0';
|
||||||
|
if (dir_name_buf[0] == L'\xFEFE')
|
||||||
|
dir_name_buf[0] = '\0';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (errno)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Emulate the behavior of dirname by returning "." for dir name if it's
|
||||||
|
empty */
|
||||||
|
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
|
||||||
|
{
|
||||||
|
_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
|
||||||
|
}
|
||||||
|
/* Concatenate the drive letter and dir name to form full dir name */
|
||||||
|
_tinydir_strcat(drive_buf, dir_name_buf);
|
||||||
|
dir_name = drive_buf;
|
||||||
|
/* Concatenate the file name and extension to form base name */
|
||||||
|
_tinydir_strcat(file_name_buf, ext_buf);
|
||||||
|
base_name = file_name_buf;
|
||||||
|
#else
|
||||||
|
_tinydir_strcpy(dir_name_buf, path);
|
||||||
|
dir_name = dirname(dir_name_buf);
|
||||||
|
_tinydir_strcpy(file_name_buf, path);
|
||||||
|
base_name =basename(file_name_buf);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Open the parent directory */
|
||||||
|
if (tinydir_open(&dir, dir_name) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read through the parent directory and look for the file */
|
||||||
|
while (dir.has_next)
|
||||||
|
{
|
||||||
|
if (tinydir_readfile(&dir, file) == -1)
|
||||||
|
{
|
||||||
|
result = -1;
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
if (_tinydir_strcmp(file->name, base_name) == 0)
|
||||||
|
{
|
||||||
|
/* File found */
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tinydir_next(&dir);
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
result = -1;
|
||||||
|
errno = ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bail:
|
||||||
|
tinydir_close(&dir);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
void _tinydir_get_ext(tinydir_file *file)
|
||||||
|
{
|
||||||
|
_tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
|
||||||
|
if (period == NULL)
|
||||||
|
{
|
||||||
|
file->extension = &(file->name[_tinydir_strlen(file->name)]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file->extension = period + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
int _tinydir_file_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const tinydir_file *fa = (const tinydir_file *)a;
|
||||||
|
const tinydir_file *fb = (const tinydir_file *)b;
|
||||||
|
if (fa->is_dir != fb->is_dir)
|
||||||
|
{
|
||||||
|
return -(fa->is_dir - fb->is_dir);
|
||||||
|
}
|
||||||
|
return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#ifndef _TINYDIR_USE_READDIR
|
||||||
|
/*
|
||||||
|
The following authored by Ben Hutchings <ben@decadent.org.uk>
|
||||||
|
from https://womble.decadent.org.uk/readdir_r-advisory.html
|
||||||
|
*/
|
||||||
|
/* Calculate the required buffer size (in bytes) for directory *
|
||||||
|
* entries read from the given directory handle. Return -1 if this *
|
||||||
|
* this cannot be done. *
|
||||||
|
* *
|
||||||
|
* This code does not trust values of NAME_MAX that are less than *
|
||||||
|
* 255, since some systems (including at least HP-UX) incorrectly *
|
||||||
|
* define it to be a smaller value. */
|
||||||
|
_TINYDIR_FUNC
|
||||||
|
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
|
||||||
|
{
|
||||||
|
long name_max;
|
||||||
|
size_t name_end;
|
||||||
|
/* parameter may be unused */
|
||||||
|
(void)dirp;
|
||||||
|
|
||||||
|
#if defined _TINYDIR_USE_FPATHCONF
|
||||||
|
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
|
||||||
|
if (name_max == -1)
|
||||||
|
#if defined(NAME_MAX)
|
||||||
|
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||||
|
#else
|
||||||
|
return (size_t)(-1);
|
||||||
|
#endif
|
||||||
|
#elif defined(NAME_MAX)
|
||||||
|
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||||
|
#else
|
||||||
|
#error "buffer size for readdir_r cannot be determined"
|
||||||
|
#endif
|
||||||
|
name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
|
||||||
|
return (name_end > sizeof(struct _tinydir_dirent) ?
|
||||||
|
name_end : sizeof(struct _tinydir_dirent));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
# if defined (_MSC_VER)
|
||||||
|
# pragma warning(pop)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#endif
|
68
utils.cc
Normal file
68
utils.cc
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "tinydir.h"
|
||||||
|
|
||||||
|
std::vector<std::string> GetFilesInFolder(std::string folder) {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
|
||||||
|
tinydir_dir dir;
|
||||||
|
if (tinydir_open(&dir, folder.c_str()) == -1) {
|
||||||
|
perror("Error opening file");
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (dir.has_next) {
|
||||||
|
tinydir_file file;
|
||||||
|
if (tinydir_readfile(&dir, &file) == -1) {
|
||||||
|
perror("Error getting file");
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string full_path = folder + "/" + file.name;
|
||||||
|
if (file.is_dir) {
|
||||||
|
if (strcmp(file.name, ".") != 0 && strcmp(file.name, "..") != 0) {
|
||||||
|
for (std::string nested_file : GetFilesInFolder(full_path))
|
||||||
|
result.push_back(nested_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.push_back(full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tinydir_next(&dir) == -1) {
|
||||||
|
perror("Error getting next file");
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bail:
|
||||||
|
tinydir_close(&dir);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ReadLines(std::string filename) {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
|
||||||
|
std::ifstream input(filename);
|
||||||
|
for (std::string line; getline(input, line); )
|
||||||
|
result.push_back(line);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseTestExpectation(std::string filename, std::vector<std::string>* expected_output) {
|
||||||
|
bool in_output = false;
|
||||||
|
|
||||||
|
for (std::string line : ReadLines(filename)) {
|
||||||
|
if (line == "*/")
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (in_output)
|
||||||
|
expected_output->push_back(line);
|
||||||
|
|
||||||
|
if (line == "OUTPUT:")
|
||||||
|
in_output = true;
|
||||||
|
}
|
||||||
|
}
|
8
utils.h
Normal file
8
utils.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
std::vector<std::string> GetFilesInFolder(std::string folder);
|
||||||
|
std::vector<std::string> ReadLines(std::string filename);
|
||||||
|
void ParseTestExpectation(std::string filename, std::vector<std::string>* expected_output);
|
Loading…
Reference in New Issue
Block a user