📄 extractcode.cpp
字号:
//: C26:ExtractCode.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// Automatically extracts code files from
// ASCII text of this book.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
using namespace std;
string copyright =
"// From Thinking in C++, 2nd Edition\n"
"// Available at http://www.BruceEckel.com\n"
"// (c) Bruce Eckel 1999\n"
"// Copyright notice in Copyright.txt\n";
string usage =
" Usage:ExtractCode source\n"
"where source is the ASCII file containing \n"
"the embedded tagged sourcefiles. The ASCII \n"
"file must also contain an embedded compiler\n"
"configuration file called CompileDB.txt \n"
"See Thinking in C++, 2nd ed. for details\n";
// Tool to remove the white space from both ends:
string trim(const string& s) {
if(s.length() == 0)
return s;
int b = s.find_first_not_of(" \t");
int e = s.find_last_not_of(" \t");
if(b == -1) // No non-spaces
return "";
return string(s, b, e - b + 1);
}
// Manage all the error messaging:
void error(string problem, string message) {
static const string border(
"-----------------------------------------\n");
class ErrReport {
int count;
string fname;
public:
ofstream errs;
ErrReport(char* fn = "ExtractCodeErrors.txt")
: count(0),fname(fn),errs(fname.c_str()) {}
void operator++(int) { count++; }
~ErrReport() {
errs.close();
// Dump error messages to console
ifstream in(fname.c_str());
cerr << in.rdbuf() << endl;
cerr << count << " Errors found" << endl;
cerr << "Messages in " << fname << endl;
}
};
// Created on first call to this function;
// Destructor reports total errors:
static ErrReport report;
report++;
report.errs << border << message << endl
<< "Problem spot: " << problem << endl;
}
///// OS-specific code, hidden inside a class:
#ifdef __GNUC__ // For egcs under Linux/Unix
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
class OSDirControl {
public:
static string getCurrentDir() {
char path[PATH_MAX];
getcwd(path, PATH_MAX);
return string(path);
}
static void makeDir(string dir) {
mkdir(dir.c_str(), 0777);
}
static void changeDir(string dir) {
chdir(dir.c_str());
}
};
#else // For Dos/Windows:
#include <direct.h>
class OSDirControl {
public:
static string getCurrentDir() {
char path[_MAX_PATH];
getcwd(path, _MAX_PATH);
return string(path);
}
static void makeDir(string dir) {
mkdir(dir.c_str());
}
static void changeDir(string dir) {
chdir(dir.c_str());
}
};
#endif ///// End of OS-specific code
class PushDirectory {
string oldpath;
public:
PushDirectory(string path);
~PushDirectory() {
OSDirControl::changeDir(oldpath);
}
void pushOneDir(string dir) {
OSDirControl::makeDir(dir);
OSDirControl::changeDir(dir);
}
};
PushDirectory::PushDirectory(string path) {
oldpath = OSDirControl::getCurrentDir();
while(path.length() != 0) {
int colon = path.find(':');
if(colon != string::npos) {
pushOneDir(path.substr(0, colon));
path = path.substr(colon + 1);
} else {
pushOneDir(path);
return;
}
}
}
//--------------- Manage code files -------------
// A CodeFile object knows everything about a
// particular code file, including contents, path
// information, how to compile, link, and test
// it, and which compilers it won't compile with.
enum TType {header, object, executable, none};
class CodeFile {
TType _targetType;
string _rawName, // Original name from input
_path, // Where the source file lives
_file, // Name of the source file
_base, // Name without extension
_tname, // Target name
_testArgs; // Command-line arguments
vector<string>
lines, // Contains the file
_compile, // Compile dependencies
_link; // How to link the executable
set<string>
_noBuild; // Compilers it won't compile with
bool writeTags; // Whether to write the markers
// Initial makefile processing for the file:
void target(const string& s);
// For quoted #include headers:
void headerLine(const string& s);
// For special dependency tag marks:
void dependLine(const string& s);
public:
CodeFile(istream& in, string& s);
const string& rawName() { return _rawName; }
const string& path() { return _path; }
const string& file() { return _file; }
const string& base() { return _base; }
const string& targetName() { return _tname; }
TType targetType() { return _targetType; }
const vector<string>& compile() {
return _compile;
}
const vector<string>& link() {
return _link;
}
const set<string>& noBuild() {
return _noBuild;
}
const string& testArgs() { return _testArgs; }
// Add a compiler it won't compile with:
void addFailure(const string& failure) {
_noBuild.insert(failure);
}
bool compilesOK(string compiler) {
return _noBuild.count(compiler) == 0;
}
friend ostream&
operator<<(ostream& os, const CodeFile& cf) {
copy(cf.lines.begin(), cf.lines.end(),
ostream_iterator<string>(os, ""));
return os;
}
void write() {
PushDirectory pd(_path);
ofstream listing(_file.c_str());
listing << *this; // Write the file
}
void dumpInfo(ostream& os);
};
void CodeFile::target(const string& s) {
// Find the base name of the file (without
// the extension):
int lastDot = _file.find_last_of('.');
if(lastDot == string::npos) {
error(s, "Missing extension");
exit(1);
}
_base = _file.substr(0, lastDot);
// Determine the type of file and target:
if(s.find(".h") != string::npos ||
s.find(".H") != string::npos) {
_targetType = header;
_tname = _file;
return;
}
if(s.find(".txt") != string::npos
|| s.find(".TXT") != string::npos
|| s.find(".dat") != string::npos
|| s.find(".DAT") != string::npos) {
// Text file, not involved in make
_targetType = none;
_tname = _file;
return;
}
// C++ objs/exes depend on their own source:
_compile.push_back(_file);
if(s.find("{O}") != string::npos) {
// Don't build an executable from this file
_targetType = object;
_tname = _base;
} else {
_targetType = executable;
_tname = _base;
// The exe depends on its own object file:
_link.push_back(_base);
}
}
void CodeFile::headerLine(const string& s) {
int start = s.find('\"');
int end = s.find('\"', start + 1);
int len = end - start - 1;
_compile.push_back(s.substr(start + 1, len));
}
void CodeFile::dependLine(const string& s) {
const string linktag("//{L} ");
string deps = trim(s.substr(linktag.length()));
while(true) {
int end = deps.find(' ');
string dep = deps.substr(0, end);
_link.push_back(dep);
if(end == string::npos) // Last one
break;
else
deps = trim(deps.substr(end));
}
}
CodeFile::CodeFile(istream& in, string& s) {
// If false, don't write begin & end tags:
writeTags = (s[3] != '!');
// Assume a space after the starting tag:
_file = s.substr(s.find(' ') + 1);
// There will always be at least one colon:
int lastColon = _file.find_last_of(':');
if(lastColon == string::npos) {
error(s, "Missing path");
lastColon = 0; // Recover from error
}
_rawName = trim(_file);
_path = _file.substr(0, lastColon);
_file = _file.substr(lastColon + 1);
_file =_file.substr(0,_file.find_last_of(' '));
cout << "path = [" << _path << "] "
<< "file = [" << _file << "]" << endl;
target(s); // Determine target type
if(writeTags){
lines.push_back(s + '\n');
lines.push_back(copyright);
}
string s2;
while(getline(in, s2)) {
// Look for specified link dependencies:
if(s2.find("//{L}") == 0) // 0: Start of line
dependLine(s2);
// Look for command-line arguments for test:
if(s2.find("//{T}") == 0) // 0: Start of line
_testArgs = s2.substr(strlen("//{T}") + 1);
// Look for quoted includes:
if(s2.find("#include \"") != string::npos) {
headerLine(s2); // Grab makefile info
}
// Look for end marker:
if(s2.find("//" "/:~") != string::npos) {
if(writeTags)
lines.push_back(s2 + '\n');
return; // Found the end
}
// Make sure you don't see another start:
if(s2.find("//" ":") != string::npos
|| s2.find("/*" ":") != string::npos) {
error(s, "Error: new file started before"
" previous file concluded");
return;
}
// Write ordinary line:
lines.push_back(s2 + '\n');
}
}
void CodeFile::dumpInfo(ostream& os) {
os << _path << ':' << _file << endl;
os << "target: " << _tname << endl;
os << "compile: " << endl;
for(int i = 0; i < _compile.size(); i++)
os << '\t' << _compile[i] << endl;
os << "link: " << endl;
for(int i = 0; i < _link.size(); i++)
os << '\t' << _link[i] << endl;
if(_noBuild.size() != 0) {
os << "Won't build with: " << endl;
copy(_noBuild.begin(), _noBuild.end(),
ostream_iterator<string>(os, "\n"));
}
}
//--------- Manage compiler information ---------
class CompilerData {
// Information about each compiler:
vector<string> rules; // Makefile rules
set<string> fails; // Non-compiling files
string objExtension; // File name extensions
string exeExtension;
// For OS-specific activities:
bool _dos, _unix;
// Store the information for all the compilers:
static map<string, CompilerData> compilerInfo;
static set<string> _compilerNames;
public:
CompilerData() : _dos(false), _unix(false) {}
// Read database of various compiler's
// information and failure listings for
// compiling the book files:
static void readDB(istream& in);
// For enumerating all the compiler names:
static set<string>& compilerNames() {
return _compilerNames;
}
// Tell this CodeFile which compilers
// don't work with it:
static void addFailures(CodeFile& cf);
// Produce the proper object file name
// extension for this compiler:
static string obj(string compiler);
// Produce the proper executable file name
// extension for this compiler:
static string exe(string compiler);
// For inserting a particular compiler's
// rules into a makefile:
static void
writeRules(string compiler, ostream& os);
// Change forward slashes to backward
// slashes if necessary:
static string
adjustPath(string compiler, string path);
// So you can ask if it's a Unix compiler:
static bool isUnix(string compiler) {
return compilerInfo[compiler]._unix;
}
// So you can ask if it's a dos compiler:
static bool isDos(string compiler) {
return compilerInfo[compiler]._dos;
}
// Display information (for debugging):
static void dump(ostream& os = cout);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -