Выбрать главу

   C08/

   C09/

   C10/

   C11/

   TestSuite/

The source files containing the examples from each chapter will be in the corresponding directory.

Here’s the program:

//: C03:ExtractCode.cpp

// Extracts code from text

#include <cassert>

#include <cstddef>

#include <cstdio>

#include <cstdlib>

#include <fstream>

#include <iostream>

#include <string>

using namespace std;

// Legacy non-standard C header for mkdir()

#ifdef __GNUC__

#include <sys/stat.h>

#elif defined(__BORLANDC__) || defined(_MSC_VER)

#include <direct.h>

#else

#error Compiler not supported

#endif

// Check to see if directory exists

// by attempting to open a new file

// for output within it.

bool exists(string fname) {

  size_t len = fname.length();

  if(fname[len-1] != '/' && fname[len-1] != '\\')

    fname.append("/");

  fname.append("000.tmp");

  ofstream outf(fname.c_str());

  bool existFlag = outf;

  if (outf) {

    outf.close();

    remove(fname.c_str());

  }

  return existFlag;

}

int main(int argc, char* argv[]) {

  // See if input file name provided

  if(argc == 1) {

    cerr << "usage: extractCode file [dir]\n";

    exit(EXIT_FAILURE);

  }

  // See if input file exists

  ifstream inf(argv[1]);

  if(!inf) {

    cerr << "error opening file: " << argv[1] << endl;

    exit(EXIT_FAILURE);

  }

  // Check for optional output directory

  string root("./");  // current is default

  if(argc == 3) {

    // See if output directory exists

    root = argv[2];

    if(!exists(root)) {

      cerr << "no such directory: " << root << endl;

      exit(EXIT_FAILURE);

    }

    size_t rootLen = root.length();

    if(root[rootLen-1] != '/' && root[rootLen-1] != '\\')

      root.append("/");

  }

  // Read input file line by line

  // checking for code delimiters

  string line;

  bool inCode = false;

  bool printDelims = true;

  ofstream outf;

  while (getline(inf, line)) {

    size_t findDelim = line.find("//" "/:~");

    if(findDelim != string::npos) {

      // Output last line and close file

      if (!inCode) {

        cerr << "Lines out of order\n";

        exit(EXIT_FAILURE);

      }

      assert(outf);

      if (printDelims)

        outf << line << endl;

      outf.close();

      inCode = false;

      printDelims = true;

    } else {

      findDelim = line.find("//" ":");

      if(findDelim == 0) {

        // Check for '!' directive

        if(line[3] == '!') {

          printDelims = false;

          ++findDelim;  // To skip '!' for next search

        }

        // Extract subdirectory name, if any

        size_t startOfSubdir =

          line.find_first_not_of(" \t", findDelim+3);

        findDelim = line.find(':', startOfSubdir);

        if (findDelim == string::npos) {

          cerr << "missing filename information\n" << endl;

          exit(EXIT_FAILURE);

        }

        string subdir;

        if(findDelim > startOfSubdir)

          subdir = line.substr(startOfSubdir,

                findDelim - startOfSubdir);

        // Extract file name (better be one!)

        size_t startOfFile = findDelim + 1;

        size_t endOfFile =

          line.find_first_of(" \t", startOfFile);

        if(endOfFile == startOfFile) {

          cerr << "missing filename\n";

          exit(EXIT_FAILURE);

        }

        // We have all the pieces; build fullPath name

        string fullPath(root);

        if(subdir.length() > 0)

          fullPath.append(subdir).append("/");

        assert(fullPath[fullPath.length()-1] == '/');

        if (!exists(fullPath))

#ifdef __GNUC__

          mkdir(fullPath.c_str(), 0);  // Create subdir

#else

          mkdir(fullPath.c_str());  // Create subdir

#endif

        fullPath.append(line.substr(startOfFile,

         endOfFile - startOfFile));

        outf.open(fullPath.c_str());

        if(!outf) {

          cerr << "error opening " << fullPath

<< " for output\n";

          exit(EXIT_FAILURE);

        }

        inCode = true;

        cout << "Processing " << fullPath << endl;

        if(printDelims)

          outf << line << endl;

      }

      else if(inCode) {

        assert(outf);

        outf << line << endl;  // output middle code line

      }

    }

  }

  exit(EXIT_SUCCESS);

} ///:~

First, you’ll notice some conditional compilation directives. The mkdir( ) function, which creates a directory in the file system, is defined by the POSIX[37] standard in the header <sys/stat.h>. Unfortunately, many compilers still use a different header (<direct.h>). The respective signatures for mkdir( ) also differ: POSIX specifies two arguments, the older versions just one. For this reason, there is more conditional compilation later in the program to choose the right call to mkdir( ). We normally don’t use conditional compilation in the examples in this book, but this particular program is too useful not to put a little extra work into, since you can use it to extract all the code with it.

The exists( ) function in ExtractCode.cpp tests whether a directory exists by opening a temporary file in it. If the open fails, the directory doesn’t exist. You remove a file by sending its name as a char* to std::remove( ).

The main program validates the command-line arguments and then reads the input file a line at a time, looking for the special source code delimiters. The Boolean flag inCode indicates that the program is in the middle of a source file, so lines should be output. The printDelims flag will be true if the opening token is not followed by an exclamation point; otherwise the first and last lines are not written. It is important to check for the closing delimiter first, because the start token is a subset of it, and searching for the start token first would return a successful find for both cases. If we encounter the closing token, we verify that we are in the middle of processing a source file; otherwise, something is wrong with the way the delimiters are laid out in the text file. If inCode is true, all is well, and we (optionally) write the last line and close the file. When the opening token is found, we parse the directory and file name components and open the file. The following string-related functions were used in this example: length( ), append( ), getline( ), find( ) (two versions), find_first_not_of( ), substr( ), find_first_of( ), c_str( ), and, of course, operator<<( ).

вернуться

37

POSIX, an IEEE standard, stands for “Portable Operating System Interface” and is a generalization of many of the low-level system calls found in UNIX systems.