#include #include #include #include #include #include #include #include #include #include #include using namespace std; typedef vector arguments; const string version_file_name = ".versionrc"; const string strmajor = "major"; const string strminor = "minor"; const string strdebug = "debug"; const string strbuild = "build"; map variables; bool req_quiet = false; bool req_init = false; namespace calc { long assign(); long pre(); long expression(); long term(); long factor(); string::iterator first, last; locale l("C"); void skipws() { while(first != last) { if(!isspace(*first, l)) break; first++; } } long main(string src) { first = begin(src); last = end(src); skipws(); return assign(); } void nextch() { first++; skipws(); } bool is_variable_name() { return first != last && isalpha(*first, l); } string get_variable_name() { string dst; while(first != last && isalpha(*first, l)) dst.push_back(*(first++)); skipws(); return dst; } string peek_variable_name() { string::iterator backtrack = first; string dst = get_variable_name(); first = backtrack; return dst; } long get_variable(string name) { return variables[name]; } void set_variable(string name, long value) { variables[name] = value; } string peek_two_char() { string dst; dst.push_back(first != last ? *first : ' '); dst.push_back(first != last && next(first, 1) != last ? *(next(first, 1)) : ' '); return dst; } long number() { if(is_variable_name()) { string variable_name = get_variable_name(); long dst = get_variable(variable_name); if(peek_two_char() == "++") { first = next(first, 2); skipws(); set_variable(variable_name, dst + 1); } else if(peek_two_char() == "--") { first = next(first, 2); skipws(); set_variable(variable_name, dst - 1); } return dst; } else { char sign = first != last ? *first : '\0'; if(sign == '+' || sign == '-') return first++, sign == '-' ? -number() : number(); size_t pos; string src(first, last); long dst; try { dst = stol(src, &pos); } catch(invalid_argument &) { throw exception("invalid expression"); } first = next(first, pos); skipws(); return dst; } } long assign() { if(is_variable_name()) { string::iterator backtrack = first; string variable_name = get_variable_name(); if(*first == '=') { nextch(); long dst = assign(); set_variable(variable_name, dst); return dst; } else { string op = peek_two_char(); if(op == "+=") { first = next(first, 2); skipws(); long dst = assign(); set_variable(variable_name, get_variable(variable_name) + dst); return dst; } else if(op == "-=") { first = next(first, 2); skipws(); long dst = assign(); set_variable(variable_name, get_variable(variable_name) - dst); return dst; } else if(op == "*=") { first = next(first, 2); skipws(); long dst = assign(); set_variable(variable_name, get_variable(variable_name) * dst); return dst; } else if(op == "/=") { first = next(first, 2); skipws(); long dst = assign(); set_variable(variable_name, get_variable(variable_name) / dst); return dst; } else if(op == "%=") { first = next(first, 2); skipws(); long dst = assign(); set_variable(variable_name, get_variable(variable_name) % dst); return dst; } else { first = backtrack; return pre(); } } } else { return pre(); } } long pre() { if(peek_two_char() == "++") { first = next(first, 2); skipws(); if(!is_variable_name()) throw exception("can't pre-increment"); string variable_name = peek_variable_name(); set_variable(variable_name, get_variable(variable_name) + 1); } else if(peek_two_char() == "--") { first = next(first, 2); skipws(); if(!is_variable_name()) throw exception("can't pre-decrement"); string variable_name = peek_variable_name(); set_variable(variable_name, get_variable(variable_name) - 1); } return expression(); } long expression() { long dst = term(); while(true) { if(*first == '+') nextch(), dst += term(); else if(*first == '-') nextch(), dst -= term(); else break; } return dst; } long term() { long dst = factor(); while(true) { if(*first == '*') nextch(), dst *= factor(); else if(*first == '/') nextch(), dst /= factor(); else if(*first == '%') nextch(), dst %= factor(); else break; } return dst; } long factor() { if(*first == '(') { nextch(); long dst = assign(); if(*first != ')') throw exception("missing ')'"); nextch(); return dst; } else { return number(); } } } namespace command { struct entry { string name; arguments::const_iterator(*function)(arguments::const_iterator, arguments::const_iterator); }; const entry * search(string name); template T command_internal_error(T first, T last) { throw exception((*first + "is not a command").c_str()); } template T command_evaluation(T first, T last) { calc::main(*first); return ++first; } template T command_help(T first, T last) { cout << "usage: version [-h] [-q] [-i] expression\n" "\n" " format: major.minor.debug.build\n" " example:\n" " version build++\n" " version debug=0 minor+=1\n" " version major=major+1\n" "\n" " options:\n" " -h this message\n" " -q quiet mode\n" " -i initialize .versionrc file\n" " -C print C language header\n" " -N print nmake.exe format\n" << ends; req_quiet = true; return ++first; } template T command_quiet(T first, T last) { req_quiet = true; return ++first; } template T command_init(T first, T last) { req_init = true; return ++first; } template T command_clang(T first, T last) { cout << "#pragma once\n" << "#define VERSION_MAJOR " << variables[strmajor] << endl << "#define VERSION_MINOR " << variables[strminor] << endl << "#define VERSION_DEBUG " << variables[strdebug] << endl << "#define VERSION_BUILD " << variables[strbuild] << endl << ends; req_quiet = true; return ++first; } template T command_nmake(T first, T last) { cout << "VERSION_MAJOR = " << variables[strmajor] << endl << "VERSION_MINOR = " << variables[strminor] << endl << "VERSION_DEBUG = " << variables[strdebug] << endl << "VERSION_BUILD = " << variables[strbuild] << endl << ends; req_quiet = true; return ++first; } const entry table[] = { {"internal_error", command_internal_error}, {"evaluation", command_evaluation}, {"-h", command_help}, {"-q", command_quiet}, {"-i", command_init}, {"-C", command_clang}, {"-N", command_nmake}, }; const auto size = sizeof(table) / sizeof(*table); const entry * begin = table; const entry * end = table + size; const entry * search(string name) { auto I = find_if(command::begin, command::end, [name](const command::entry & I) -> bool {return I.name == name;}); return I != command::end ? I : search("internal_error"); } const entry * search_and_eval(string name) { auto I = find_if(command::begin, command::end, [name](const command::entry & I) -> bool {return I.name == name;}); return I != command::end ? I : search("evaluation"); } } tuple read_version_txt(string filename) { ifstream in(filename); if(!in) throw exception("file not found"); string version; getline(in, version); if(!in) throw exception("file read error"); smatch matches; regex_search(version, matches, regex("([+-]?\\d+)\\.([+-]?\\d+)\\.([+-]?\\d+)\\.([+-]?\\d+)")); if(matches.size() != 5) throw exception((string("invalid format in ") + filename).c_str()); return make_tuple(stol(matches[1].str()), stol(matches[2].str()), stol(matches[3].str()), stol(matches[4].str())); } void write_version_txt(string filename, long major, long minor, long debug, long build) { string tempname = filename + ".temp"; ofstream out(tempname); out << major << "." << minor << "." << debug << "." << build << endl; out.close(); if(!out) throw exception("file write error"); if(remove(filename.c_str()) != 0) throw exception((filename + " can't remove").c_str()); if(rename(tempname.c_str(), filename.c_str()) != 0) throw exception((tempname + " can't rename").c_str()); } void print_version(long major, long minor, long debug, long build) { cout << major << "." << minor << "." << debug << "." << build << endl; } int main(int argc, char * argv[]) { try { arguments args(&argv[1], &argv[argc]); long major, minor, debug, build; tie(major, minor, debug, build) = read_version_txt(version_file_name); if(args.empty()) return print_version(major, minor, debug, build), EXIT_SUCCESS; variables[strmajor] = major; variables[strminor] = minor; variables[strdebug] = debug; variables[strbuild] = build; arguments::const_iterator first = begin(args), last = end(args); while(first != last) first = (*(command::search_and_eval(*first)->function))(first, last); if(req_init) return write_version_txt(version_file_name, 0, 0, 0, 0), EXIT_SUCCESS; major = variables[strmajor]; minor = variables[strminor]; debug = variables[strdebug]; build = variables[strbuild]; write_version_txt(version_file_name, major, minor, debug, build); if(!req_quiet) print_version(major, minor, debug, build); return EXIT_SUCCESS; } catch(const exception & x) { cout << x.what() << endl; } return EXIT_FAILURE; } // (c) 2012 Strictsoft