Created
July 20, 2018 19:57
-
-
Save CoryBloyd/c8dce71c37d40f3cc6b60f5c9b8b138b to your computer and use it in GitHub Desktop.
markdown <-> cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _CRT_SECURE_NO_WARNINGS | |
#define _CRT_NONSTDC_NO_DEPRECATE | |
#include <string> | |
#include <vector> | |
#include <stdio.h> | |
#include <io.h> | |
#include <fcntl.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <errno.h> | |
using std::string, std::string_view, std::vector; | |
struct Config { | |
const char *markdownLabel = "markdown"; | |
const char *codeLabel = "cpp"; | |
const char *markdownExt = ".md"; | |
const char *codeExt = ".cpp"; | |
const char *openCodeBlock = "```"; | |
const char *closeCodeBlock = "````\n"; | |
const char *openCommentBlock = "/***"; | |
const char *closeCommentBlock = "****/\n"; | |
}; | |
string readFile(const string &path) { | |
string result; | |
if (int fileDescriptor; (fileDescriptor = open(path.data(), _O_RDONLY | _O_TEXT)) != -1) { | |
if (struct stat fileStat; fstat(fileDescriptor, &fileStat) == 0) { | |
result.resize(fileStat.st_size); | |
result.resize(read(fileDescriptor, &result[0], (int)result.size())); | |
} | |
close(fileDescriptor); | |
} | |
return result; | |
} | |
bool writeFile(const string &path, const string &text) { | |
int fileDescriptor = open(path.data(), O_WRONLY | O_CREAT | O_TRUNC | _O_TEXT, 0640); | |
if (fileDescriptor == -1) | |
return false; | |
int written = write(fileDescriptor, &text[0], (int)text.size()); | |
int closeResult = close(fileDescriptor); | |
return written == text.size() && closeResult != -1; | |
} | |
string join(const vector<string_view> &spans, const char *openBlock, const char *sourceLanguage, const char *closeBlock) { | |
auto joinInto = [](char *dest, const vector<string_view> &spans, const char *openBlock, const char *sourceLanguage, const char *closeBlock) { | |
size_t totalSize = 0; | |
char *cursor = dest; | |
size_t maxSize = dest == nullptr ? 0 : size_t(-1); | |
bool comment = true; | |
for (auto span : spans) { | |
if (dest != nullptr) | |
cursor = dest + totalSize; | |
if (span.size() > 0) { | |
if (comment) | |
totalSize += snprintf(cursor, maxSize, "%s %s\n%.*s%s", openBlock, sourceLanguage, (int)span.size(), span.data(), closeBlock); | |
else | |
totalSize += snprintf(cursor, maxSize, "%.*s", (int)span.size(), span.data()); | |
} | |
comment = !comment; | |
} | |
return totalSize; | |
}; | |
size_t totalSize = joinInto(nullptr, spans, openBlock, sourceLanguage, closeBlock); | |
string result(totalSize, '\0'); | |
joinInto(&result[0], spans, openBlock, sourceLanguage, closeBlock); | |
return result; | |
} | |
string convert(const string &source, | |
const char *destLanguage, const char *sourceLanguage, | |
const char *destOpenBlock, const char *destCloseBlock, | |
const char *srcOpenBlock, const char *srcCloseBlock) { | |
vector<string_view> spans; | |
const char *token = srcOpenBlock; | |
const string::size_type strlenSrcCloseBlock = strlen(srcCloseBlock); | |
const string::size_type sourceSize = source.size(); | |
for (string::size_type spanStart = 0, spanEnd = 0; spanEnd < sourceSize;) { | |
spanEnd = source.find(token, spanStart); | |
if (spanEnd == string::npos) | |
spanEnd = sourceSize; | |
if (token == srcCloseBlock) | |
spanStart = source.find('\n', spanStart) + 1; | |
string_view span = string_view(&source[spanStart], spanEnd - spanStart); | |
spans.push_back(span); | |
if (token == srcCloseBlock) | |
spanEnd += strlenSrcCloseBlock; | |
token = token == srcOpenBlock ? srcCloseBlock : srcOpenBlock; | |
spanStart = spanEnd; | |
} | |
string result = join(spans, destOpenBlock, sourceLanguage, destCloseBlock); | |
return result; | |
} | |
string codeFromMarkdown(const string &markdown, const Config &config) { | |
return convert(markdown, | |
config.codeLabel, config.markdownLabel, | |
config.openCommentBlock, config.closeCommentBlock, | |
config.openCodeBlock, config.closeCodeBlock); | |
} | |
string markdownFromCode(const string &code, const Config &config) { | |
return convert(code, | |
config.markdownLabel, config.codeLabel, | |
config.openCodeBlock, config.closeCodeBlock, | |
config.openCommentBlock, config.closeCommentBlock); | |
} | |
bool endsWith(const string s, const char *x) { return s.rfind(x) == (s.size() - strlen(x)); } | |
int main(int argc, char *argv[]) { | |
Config config; | |
int errors = 0; | |
for (int arg = 1; arg < argc; arg++) { | |
string srcPath(argv[arg]); | |
const char *mdExt = config.markdownExt, *codeExt = config.codeExt; | |
const char *srcExt = endsWith(srcPath, mdExt) ? mdExt : codeExt; | |
const char *dstExt = srcExt == mdExt ? codeExt : mdExt; | |
string srcText = readFile(srcPath.data()); | |
string dstText = dstExt == mdExt ? markdownFromCode(srcText, config) | |
: codeFromMarkdown(srcText, config); | |
if (dstText.empty()) { | |
errors++; | |
fprintf(stderr, "Failed to convert %.*s\n", (int)srcPath.size(), srcPath.data()); | |
} | |
else { | |
string dstPath = srcPath.replace(srcPath.rfind(srcExt), strlen(srcExt), dstExt); | |
if (!writeFile(dstPath.data(), dstText)) { | |
errors++; | |
fprintf(stderr, "Failed to write %.*s\n", (int)dstPath.size(), dstPath.data()); | |
} | |
} | |
} | |
return errors; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment