// smake -- simple make // // Programmer: Craig Stuart Sapp // Filename: smake.cpp // Creation Date: Fri Mar 29 21:43:39 PST 1996 // Last Modified: Fri May 8 16:33:29 PDT 2009 (updated c++ syntax for GCC 4) // Syntax: C++ // $Smake: g++ -O -o %b %f -static -Llib -lmuseinfo % // && strip %b // // Description: See ManPage() function. // #include "Array.h" #include "Options.h" #include #include #include #include #include #include using namespace std; // command-line option variables: int echoQ = 0; // used with -e option int quietQ = 0; // used with -x option int numSkip =4; // used with -n option string commandmarker = "$Smake:"; // used with -c option string versionSmake = ""; // used with -v option char MagicString[1024] = {0}; Array > passParams; int numPassParams; char* mybasename; string filename; // function declarations: int backwardPosition (char, const char*); void find (const char*, istream&); void getOptions (void); char nextChar (istream&); void readCommand (stringstream&, const char*, istream&); char* unsuffix (const char* input); void ManPage (void); void checkOptions (Options& opts); int usage (const char* command); //////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { Options options(argc, argv); checkOptions(options); // find name of file to extract command from: filename = options.getArg(1); ifstream inFile(filename); if (!inFile) { cerr << "Cannot open file: " << filename << endl; exit(1); } mybasename = unsuffix(filename.c_str()); // determine if any parameters are to be passed: numPassParams = options.getArgCount() - 1; passParams.setSize(numPassParams); int i, len; for (i=0; i; 1996\n"; exit(1); } if (opts.getBoolean("manpage")) { ManPage(); exit(1); } commandmarker = opts.getString("command-marker"); versionSmake = opts.getString("variant"); echoQ = opts.getBoolean("echo"); quietQ = opts.getBoolean("quiet"); numSkip = opts.getInteger("skip"); strncpy(MagicString, commandmarker.c_str(), 128); if (versionSmake.size() != 0) { int len = strlen(MagicString); if (MagicString[len-1] == ':') { MagicString[len-1] = '\0'; strcat(MagicString, "-"); strcat(MagicString, versionSmake.c_str()); strcat(MagicString, ":"); } else { strcat(MagicString, versionSmake.c_str()); } } } ////////////////////////////// // // usage // int usage(const char* command) { cerr << "\nUsage: " << command << " [-s skip][-v variant |-c][-e|-x] input-file [parameters]\n" << " Processes a file according the command string in the file. " << "The command\n" << " string starts with the pattern $Smake: and lasts until the " << "end of the line.\n" << " Symbolic patterns in the command are:\n" << " %f -- replaces with the filename string\n" << " %b -- replaces with the filename base (removes all " << "characters after\n last \".\"" << " of file name and also removes the dot.\n" << "Type " << command << " -h for the man page.\n" << endl; return 0; } ////////////////////////////// // // backwardPosition -- return the position of the last sought // character in the string. // int backwardPosition(char sought, const char *astring) { int i; for (i = strlen(astring) - 1; i>=0; i--) { if (sought == astring[i]) { break; } } return i; } ////////////////////////////// // // unsuffix -- remove the .* extension of a filename. // char* unsuffix(const char *input) { int dotPosition; char *base; dotPosition = backwardPosition('.', input); if (dotPosition != -1) { base = new char[dotPosition+1]; if (base == NULL) { cerr << "Out of memory." << endl; exit(1); } strcpy(base, input); base[dotPosition] = '\0'; } else { base = new char[1]; base[0] = '\0'; } return base; } ////////////////////////////// // // find -- find a string in an istream, and position the istream after // that string or at the end of the file. // void find(const char* searchString, istream& inFile) { char c; int length = strlen(searchString); int position = 0; while (position < length && inFile.get(c)) { if (c == searchString[position]) position++; else position = 0; } } ////////////////////////////// // // nextChar -- return the next non-space, non-return character in the // (text) file. return '\0' if end-of-file or a return character. // char nextChar(istream& inFile) { char c = '\0'; while (inFile.get(c) && isspace(c)) { if (c == '\n') { c = '\0'; return c; } } return c; } ////////////////////////////// // // readCommand -- read the command string from the file. // void readCommand(stringstream& strcommand, const char* commandMarker, istream& inFile) { char c; find(commandMarker, inFile); c = nextChar(inFile); if (c == '\0') return; else strcommand << c; while (inFile.get(c) && c != '\n') { if (c == '%') { inFile.get(c); switch (c) { case '\n': for (int i=0; i (int)c - '0' - 1) { strcommand << passParams[(int)c - '0' - 1].getBase(); } else { strcommand << '%' << c; } break; case 'f': strcommand << filename; break; case 'b': strcommand << mybasename; break; default: strcommand << '%' << c; } } else { strcommand << c; } } } ////////////////////////////// // // ManPage // void ManPage(void) { cout << "\n" "SMAKE(1) UNIX Programmer's Manual SMAKE(1)\n" "\n" "\n" "\n" "NAME\n" " smake - Simple MAKE. Executes a command string embedded in\n" " a file.\n" "\n" "SYNOPSIS\n" " smake [-s skip][-v variant |-c][-e|-x] input-file [parame-\n" " ters]\n" "\n" "DESCRIPTION\n" " Smake searches for the character pattern $Smake: in a file.\n" " When it finds that pattern, smake will execute, as a shell\n" " command, the text following $Smake: to the end of the line.\n" "\n" " There are several symbolic patterns that can be used in the\n" " embedded command:\n" "\n" " %f If this character pattern is in the embedded com-\n" " mand, the input filename passed to smake will be\n" " substituted.\n" "\n" " %F If this character pattern is in the embedded com-\n" " mand, the input filename passed to smake will be\n" " substituted, removing any directory location.\n" "\n" " %b If this character pattern is in the command, the\n" " filename minus the characters after the last period\n" " will be removed, as well as the last period. For\n" " example, the basename of dir/test.c is dir/test .\n" "\n" " %B If this character pattern is in the command, the\n" " filename minus the characters after the last period\n" " will be removed, as well as the last period. For\n" " example, the Basename of dir/test.c is test .\n" "\n" " % Where is the newline character. If % comes\n" " at the end of the line with no other characters\n" " except the newline character following it, the\n" " embedded command continues on the following line.\n" " The -s option determines where the command resumes\n" " on the next line.\n" "\n" " %1--%9 If any parameters follow the input-file on the com-\n" " mand line, then these parameters get matched to the\n" " symbols %1, %2, etc. up to %9. If there is no\n" " passed parameter for a particular symbol, then it\n" " is left unchanged in the command.\n" "\n" "OPTIONS\n" " -s skip skip is the number of characters to skip at the\n" " beginning of a new line for the shell command\n" " line-continuation. The default value of skip is 4.\n" " It is an error to skip beyond the end of the line.\n" "\n" " -v variant\n" " variant is a string used to distinguish between\n" " different possible commands to extract. The form\n" " of the marker in the file is $Smake-variant:.\n" "\n" " -e echo the embedded command string without executing\n" " it.\n" "\n" " -x execute the command string only, without printing\n" " it to the terminal as well.\n" "\n" " -c pattern\n" " pattern is a string to replace the default marker\n" " $Smake: with pattern.\n" "\n" "EXAMPLES\n" " A command to compile a c program, and strip the executable:\n" "\n" " // $Smake: cc -O -o %b %f && strip %b\n" "\n" " A command to produce and display a PostScript file from a\n" " TeX file:\n" "\n" " % $Smake: tex %b && dvips -o %b.ps %b && open %b.ps\n" "\n" "FILES\n" " /user/c/craig/lang/c/smake.cc the source code\n" "\n" "BUGS\n" " Cannot use the -v option and the -c option at the same time.\n" << endl; } // md5sum: f27722754f53bfe620cad5b9d2f3b17d smake.cpp [20090511]