//============================================================================== // what.cpp // Search for source code revision identification strings ("what-strings") // embedded within one or more files. // // Notice // Copyright ©1985,2009 by David R. Tribble, all rights reserved. // Permission is granted to any person or entity except those designated // by the United States Department of State as a terrorist or terrorist // government or agency, to use and distribute this source code provided // that the original copyright notice remains present and unaltered. //============================================================================== // Identification static char REV[] = "@(#)drt/src/cmd/what.cpp $Revision: 1.4 $$Date: 2009/01/03 03:39:39 $"; static char PROG[] = "what"; static char VERS[] = "1.3"; static char DATE[] = "2009-01-02"; static char BUILT[] = "@(#)" "Built: " __DATE__ " " __TIME__; static char AUTHOR[] = "@(#)Copyright ©1985,2009 by David R. Tribble, all rights reserved."; // System includes #include #include #include #include #if _WIN32 #define WINDOWS_LEAN_AND_MEAN #include #include #endif // System macros #undef std #define std /**/ // Local includes // (None) /******************************************************************************* * class Program * Embodies the execution of the whole program. */ #define Program_VERSION 101 // Class revision number class Program { // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // Constants public: // Manifest constants enum Constants { PREFIX_LEN = 4, // == std::strlen("@(#)") BLK_SIZE = 32*1024, // Input blocking size }; // Termination codes enum ExitCode { RC_OKAY = 0, // Successful RC_USAGE = 255 // Improper command usage }; // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // Variables private: bool m_firstOnly; // Stop after 1st what-string bool m_longForm; // Long listing format const char * m_fileName; // Input file name FILE * m_in; // Input stream FILE * m_out; // Output stream // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // Functions public: /**/ ~Program(); // Destructor /**/ Program(); // Default constructor int main(int argc, const char *const *argv); // Execute this program void usage(); // Print usage message and punt private: /*void*/ Program(const Program &o); // Copy constructor const Program & operator =(const Program &o); // Assignment operator void findWhats(); // Search for whats in a file }; /******************************************************************************* * Program::~Program() * Destructor. */ Program::~Program() { #if Program_VERSION != 101 #error Class Program has changed #endif // Deallocate members m_in = NULL; m_fileName = NULL; } /******************************************************************************* * Program::Program() * Default constructor. */ Program::Program(): m_firstOnly(false), m_longForm(false), m_fileName(NULL), m_in(NULL), m_out(stdout) { #if Program_VERSION != 101 #error Class Program has changed #endif // Initialize } /******************************************************************************* * Program::main() * Executes this program. * * @return * Zero (RC_OKAY) if successful, otherwise a non-zero exit status (RC_XXX). */ int Program::main(int argc, const char *const *argv) { #if Program_VERSION/100 != 1 #error Class Program has changed #endif int i; // Parse the command line options for (i = 1; i < argc and argv[i][0] == '-'; i++) { if (std::strcmp(argv[i], "-") == 0) break; else if (std::strcmp(argv[i], "--") == 0) { i++; break; } else if (std::strcmp(argv[i], "-s") == 0) m_firstOnly = true; else if (std::strcmp(argv[i], "-l") == 0) m_longForm = true; else { std::fprintf(stderr, "%s: Unknown option \"%s\"\n", argv[i]); usage(); } } // Check command line args argc -= i; argv += i; if (argc <= 0) usage(); // Search the named files for (i = 0; i < argc; i++) { // Search a file named in a command line arg m_fileName = argv[i]; // Open the input file if (std::strcmp(m_fileName, "-") == 0) { // Take input from stdin m_in = stdin; #ifdef O_BINARY std::setmode(std::fileno(m_in), O_BINARY); #endif } else { // Open a named file m_in = std::fopen(m_fileName, "rb"); if (m_in == NULL) { std::fprintf(stderr, "%s: Can't read \"%s\": %s\n", PROG, m_fileName, std::strerror(errno)); continue; } } // Search for what-strings in the input stream findWhats(); // Clean up if (m_in != stdin) std::fclose(m_in); m_in = NULL; m_fileName = NULL; } // Done return (RC_OKAY); } /******************************************************************************* * Program::usage() * Display a program usage message, then punt. * * @return * Doesn't, but calls std::exit(). */ void Program::usage() { #if Program_VERSION/100 != 1 #error Class Program has changed #endif // Display a program usage message std::printf("[%s, %s, %s]\n\n", PROG, VERS, DATE); std::printf("Search for source code revision identification strings " "(\"what-strings\")\n"); std::printf("embedded within one or more files.\n"); std::printf("\n"); std::printf("usage: %s [-option...] file...\n", PROG); std::printf("\n"); std::printf("Options:\n"); std::printf(" -l Show filename with each what-string.\n"); std::printf(" -s Stop after the first what-string.\n"); std::printf("\n"); // Punt std::exit(RC_USAGE); } /******************************************************************************* * Program::findWhats() * Search a file for what-strings, writing the results to the output * stream. */ void Program::findWhats() { int len; int i; bool titled; unsigned char buf[BLK_SIZE+PREFIX_LEN]; // Search the contents of the input stream for what-strings i = 0; len = 0; titled = false; do { // Ensure that the buffer has chars in it if (i >= len-PREFIX_LEN) { // Reposition the buffer contents if (i > 0) std::memmove(buf+0, buf+i, len-i); len = len-i; i = 0; // Read a block of characters from the input stream len = std::fread(buf+i, 1, BLK_SIZE-i, m_in); if (len < 1) break; len += i; } // Search the block for what-strings while (i < len-PREFIX_LEN) { // Look for the start of a what-string in the buffer while (i < len-PREFIX_LEN and buf[i] != '@') i++; if (i >= len-PREFIX_LEN) break; if (buf[i+0] == '@' and buf[i+1] == '(' and buf[i+2] == '#' and buf[i+3] == ')') { int j; // Found a what-string prefix, skip leading spaces i += PREFIX_LEN; while (i < len and (buf[i] == ' ' or buf[i] == '\t')) i++; // Found a what-string, ensure the buffered block is big enough if (i > 0) std::memmove(buf+0, buf+i, len-i); i = len-i; // Fill the rest of the block (for really long what-strings) len = std::fread(buf+i, 1, BLK_SIZE-i, m_in); if (len >= 0) len += i; // Find the end of the what-string j = i = 0; while (i < len and buf[i] >= ' ' /*and buf[i] != '\0'*/ and buf[i] != '"') i++; if (i > j) { // Display the file name if (m_longForm) { std::fprintf(m_out, "%s", m_fileName); } else if (not titled) { if (std::strcmp(m_fileName, "-") != 0) std::fprintf(m_out, "%s:\n", m_fileName); titled = true; } // Display the entire what-string contents std::fprintf(m_out, "\t%.*s\n", i-j, buf+j); if (m_firstOnly) return; } } else i++; } } while (len > 0); } /******************************************************************************* * ::main() * Execute this program. * * @return * Zero (Program::RC_OKAY) if successful, otherwise a non-zero exit status * (Program::RC_XXX). */ int main(int argc, char **argv) { Program prog; return (prog.main(argc, (const char *const *) argv)); } // End what.cpp