/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: guw.cc,v $ * * $Revision: 1.3 $ * * last change: $Author: hr $ $Date: 2006-10-24 15:35:01 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ // guw - A wrapper program to execute windows programs with parameters that // contain cygwin (POSIX) style pathnames. // Todo: Add a -? switch to guw to issue a help page. #include #include #include #include #include #include #include #include #include #include #include using std::string; using std::list; using std::vector; using std::cout; using std::cerr; using std::endl; using std::size_t; void init_ignorepara(string command); bool is_ignorepara(const string ¶); int winFormat(string ¶); void do_atcommand(string ¶); void myCygpath(string &path, int shortname = 1 ); void replace_cyg_env( void ); void Fatal( const string text ); int match2s(const string argument, const char *pattern, string &sub1, string &sub2); void rep_subn_cyg(string &argument); void rep_subn( string &argument, const char *pattern, int subexp, const char repl); void rep_char( string &argument, const char from, const char to); bool debug = false; bool debug_light = false; // The commands are treated case insensitive, the parameters // are case sensitive. const string ignorepara[] = { "echo /TEST", "cl -clr: -Z", "climaker StarOffice/OpenOffice", "csc -target:", "ccache -DUDATA_SO_SUFFIX -DSTATIC_O" " -DLOCAL_RULE_LANGS", "gcc -DUDATA_SO_SUFFIX -DSTATIC_O" " -DLOCAL_RULE_LANGS", "lib /OUT: -out: -def: -machine:", "link /BASE: /COMMENT: /DEBUG: /DLL /ENTRY:" " /MACHINE: /MAP /NODEFAULTLIB /OPT: /RELEASE" " /STACK: /SUBSYSTEM: -NODEFAULTLIB:" " -def: delayload: -implib: -map: -out:", "rc -D", "regcomp -env: vnd.sun.star.expand:" " vnd.openoffice.pymodule:", "regmerge /UCR", "rsc -DOOO_" }; vector ignorepara_vec; // environment variables that are "winformatted" when -env is given const string transformvars[] = { "SOLAR_VERSION", "SOLARVERSION", "SOLARVER", "SRC_ROOT", "LOCALINI", "GLOBALINI", "SOLARENV", "STAR_INSTPATH", "STAR_SOLARPATH", "STAR_PACKMISC", "STAR_SOLARENVPATH", "STAR_INITROOT", "STAR_STANDLST", "CLASSPATH", "JAVA_HOME" }; int main(int argc, char **argv) { // initialize arglist with arguments list arglist(argv, argv + argc); // Drop the first (filename) argument arglist.pop_front(); // iterarot over cmdline elements list::iterator ele = arglist.begin(); // Allowed switch values bool conv_cyg_arg = false; // Look for switches to guw // Supported: -env // -dbg // -ldbg while ( !arglist.empty() && ele!=arglist.end() && (ele->find("-", 0) == 0) ) { if (ele->find("-env", 0) == 0) { if ( conv_cyg_arg ) Fatal("-env used twice!"); conv_cyg_arg = true; ele = arglist.erase(ele); continue; } else if (ele->find("-dbg", 0) == 0) { debug = true; ele = arglist.erase(ele); continue; } else if (ele->find("-ldbg", 0) == 0) { debug_light = true; ele = arglist.erase(ele); continue; } else { // Ignore this switch ++ele; } } // The next entry must be the program string command; if ( !arglist.empty() ) { command.assign(*arglist.begin()); arglist.pop_front(); } else Fatal("guw needs at least one parameter."); if ( debug ) cerr << "Command: " << command << "\n" << endl; // Initialize parameter exception list (for this command) init_ignorepara(command); // Do something if -env was found if ( conv_cyg_arg ) replace_cyg_env(); // loop and and DOSify the parameters if ( debug ) cerr << "Transform the parameter\n" << endl; ele=arglist.begin(); while ( ele != arglist.end() ) { if ((*ele)[0] == '@') do_atcommand(*ele); else if (!is_ignorepara(*ele)) { if ( debug ) { cerr << "----------------" << endl; cerr << "Process parameter: " << *ele << endl; } winFormat(*ele); if ( debug ) cerr << "Transformed to: " << *ele << "\n" << endl; } ++ele; } // create the argv[] for execvp(argv[0], argv); ele=arglist.begin(); // const char *nargv[arglist.size()+2]; // or .. char *nargv[arglist.size()+2]; // nargv[0] = command.c_str(); // or .. nargv[0] = new char[command.length()+1]; // strcpy(nargv[0], command.c_str()); command.copy(nargv[0], command.length()); nargv[0][command.length()] = 0; if ( debug ) cerr << "----------------\n" << endl; if ( debug || debug_light ) cerr << "Execute: " << nargv[0]; int count = 1, sLen; while ( ele != arglist.end() ) { // nargv[count] = ele->c_str(); // or .. sLen = ele->length(); nargv[count] = new char[sLen+1]; // strcpy(nargv[count], ele->c_str()); ele->copy(nargv[count], sLen); nargv[count][sLen] = 0; if ( debug || debug_light ) cerr << " " << nargv[count]; ++count; ++ele; } // last nargv[] must be NULL nargv[count] = NULL; if ( debug || debug_light ) cerr << endl; // Unfortunately the prototype of execvp does not like const char*, // actually not const char* nargv[] coming from .c_str(). So either // we copy everything into newly allocated variables or we force it // with a cast. const_cast() // execvp(nargv[0], const_cast(nargv) ); if ( execvp(nargv[0], nargv ) < 0 ) { perror("Execvp error. Aborting."); exit(1); } // Omit the deleting of the dynamically allocated nargv[] elements // here as this part will never be reached. return 0; } // Initialize exception list from global var ignorepara[] void init_ignorepara(string fullcommand) { const size_t kplen = sizeof(ignorepara)/sizeof(string *); string shortcommand, cmd, para, sub2; // First lowercase everything for(size_t i=0;i 4 && fullcommand.rfind(".exe") == slen - 4 ) fullcommand.erase(slen-4); // get the program name - Only one subexpression if (!match2s(fullcommand, "([[:alnum:]_~\\. \\-]+)$", shortcommand, sub2)) { Fatal("No basename found in: " + fullcommand); } for (size_t i=0; i != kplen; ++i) { std::istringstream line(ignorepara[i]); line >> cmd; if (shortcommand == cmd) while (line >> para) { ignorepara_vec.push_back(para); } } return ; } // Check if command/parameter is in exception list. bool is_ignorepara(const string ¶) { for( vector::iterator it = ignorepara_vec.begin(); it != ignorepara_vec.end(); it++ ) { if ( para.find(*it) != string::npos ) { if ( debug ) cerr << "Found execption para: " << para << endl; return true; } } return false; } // Reformat para to DOSish format int winFormat(string ¶) { string su1, su2; // Instead of ([/[:alnum:]_~. +-]+) use ((/?[[:alnum:]_~. +-]+)+) // find [-][-]X=, sometimes with quotes or "/" at the end if (match2s(para, "^(-?-?[[:alpha:]][[:alnum:]_.-]*=)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$", su1, su2)) { myCygpath(su2); para.assign(su1 + su2); if ( debug ) cerr << " WinFormat - ([-][-]=)\n" << " " << para << endl; } // find -X:, sometimes with quotes or "/" at the end else if (match2s(para, "^(-[[:alpha:]][[:alnum:]_.]*:)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$", su1, su2)) { myCygpath(su2); para.assign(su1 + su2); if ( debug ) cerr << " WinFormat - (-:)\n" << " " << para << endl; } // find -X:, and prevents translating of these. else if (match2s(para, "^(-[[:alpha:]][[:alnum:]_]*:)(.*)$", su1, su2)) { // myCygpath(su2); // para.assign(su1 + su2); if ( debug ) cerr << " WinFormat - (-:)\n" << " " << para << endl; } // See iz35982 for the reason for the special treatment of this switch. // This regex evaluates :///, sometimes with // quotes or "/" at the end else if (match2s(para, "^([[:alpha:]][[:alnum:]_]*:)[\'\"]?///((/?[[:alnum:]_~. +-]+)+)[\'\"]?$", su1, su2)) { myCygpath(su2); para.assign(su1 + "///" + su2); // Replace \ to / rep_char( para, '\\', '/'); if ( debug ) cerr << " WinFormat - (:///)\n" << " " << para << endl; } // find -X, sometimes with quotes or "/" at the end else if (match2s(para, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)[\'\"]?$", su1, su2)) { myCygpath(su2); para.assign(su1 + su2); if ( debug ) cerr << " WinFormat - (-X)\n" << " " << para << endl; } // find -FX (MSVC switches for output naming), sometimes with quotes // or "/" at the end else if (match2s(para, "^(-F[ARdemopr])[\'\"]?(/[/[:alnum:]_~. +-]+)[\'\"]?$", su1, su2)) { myCygpath(su2); para.assign(su1 + su2); if ( debug ) cerr << " WinFormat - (compiler naming (-FX) path)\n" << " " << para << endl; } else{ // No parameter found, assume a path // replace the colon in drives with 0x1F" // (Unused ascii US - unit separator) rep_subn( para, "(^|[;,])[[:alpha:]](:)", 2, 0x1F); // Replace remaining : to ; rep_char( para, ':', ';'); // Replace back US to ':'; rep_char( para, 0x1F, ':'); /* Search for posix path ;entry; (The regex accepts valid paths with at * least one /) and replace with DOS path, accept quotes. * since iz28717 we also accept ',' as path seperator. */ rep_subn_cyg(para); if ( debug ) cerr << " WinFormat - full path\n" << " " << para << endl; } // Sanity check for -X if (match2s(para, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)", su1, su2)) { Fatal("Not converted -X/... type switch in :" + para); } // Sanity check for [-]X(:|=) case if (match2s(para, "^(-?[[:alpha:]][[:alnum:]_.]*[=:])[\'\"]?((/[[:alnum:]_~. +-]+)+)", su1, su2)) { Fatal("Not processed [-]X(=|:)/... in :" + para); } return 1; } // Reformat para to DOSish format void do_atcommand(string ¶) { string at, filename, token; // Workaround, iz28717, keep number of @'s. match2s(para, "^(@+)(.*)",at ,filename); if ( debug ) { cerr << "----------------" << endl; cerr << "Process @-file" << endl; cerr << " :" << at << ": before filename :" << filename << ":" << endl; } // Read at file into memory std::ifstream atin(filename.c_str()); list newtoken; while (atin >> token) { // Read / transform tokens if ( debug ) cerr << "@ token :" << token << ":" << endl; if (!is_ignorepara(token)) winFormat(token); newtoken.push_back(token); } atin.close(); // Write token tokens bak to file if ( debug || debug_light ) cerr << "New @-file parameter:" << endl; // for debugging .. // filename += ".bak"; std::ofstream atout(filename.c_str()); list::iterator tok = newtoken.begin(); while ( tok != newtoken.end() ) { if ( debug || debug_light ) cerr << ( tok != newtoken.begin() ? " " : "" ) << *tok ; atout << ( tok != newtoken.begin() ? " " : "" ) << *tok ; ++tok; } // We want a dos file atout << '\r' << endl; atout.close(); // Transform the filename winFormat(filename); para = at + filename; if ( debug || debug_light ) { cerr << "\nNew @-file name: " << para << "\n" << endl; } } void myCygpath(string &path, int shortname /* =1 */ ) { static char convpath[MAX_PATH]; static char buf[MAX_PATH]; int err; // Only use cygwin_conv_to_win32_path() on absolute paths as it errors // out if its path doen't exist. Unfortunatelt there are a lot of not // existing relative pathes used as parameters during an OOo build. if( path.find("/", 0) == 0) { err = cygwin_conv_to_win32_path( path.c_str(), convpath ); } else { rep_char( path, '/', '\\'); // see below, we copy convpath back to path, that's stupid path.copy( convpath, path.length()); convpath[path.length()] = 0; err = 0; } if (err) Fatal("converting: " + path + " - " + strerror(errno) ); // Only convert to short dos names when space is present if (shortname && (path.find(" ", 0) != string::npos) ) { DWORD len = GetShortPathName (convpath, buf, MAX_PATH); if (!len) { Fatal("cannot create short name of " + string(convpath) ); } path.assign(buf); } else path.assign(convpath); } void replace_cyg_env( void ) { // Transform certain environment variables if ( debug ) cerr << "Transforming some environment variables" << endl; const size_t nvars = sizeof(transformvars)/sizeof(string *); char *currvar; string newvar; for (size_t i=0; i != nvars; ++i) { if ( currvar = getenv(transformvars[i].c_str() ) ) { // Only transform existent vars if ( debug ) cerr << "Transform variable: " << transformvars[i] << "=" << currvar << endl; newvar.assign(currvar); winFormat(newvar); if( setenv(transformvars[i].c_str(), newvar.c_str(), 1) ) Fatal("setenv failed on " + transformvars[i] + "=" + newvar + " with error: " + strerror(errno)); if ( debug ) cerr << "To: " << transformvars[i] << "=" << newvar << "\n" << endl; } } } void Fatal( const string text ) { // End with error cerr << "Error: " << text << endl; exit(1); } int match2s(const string argument, const char *pattern, string &sub1, string &sub2) { int status; regex_t re; const int maxsub = 3; // Only 3 needed, 4 is for debug regmatch_t match[maxsub]; if (regcomp(&re, pattern, REG_EXTENDED) != 0) { Fatal("regcomp had a problem."); /* report error */ } status = regexec(&re, argument.c_str(), maxsub, match, 0); regfree(&re); if (status == REG_NOMATCH) { return(0); /* no match */ } else if (status == 0) { string tstr(argument.c_str() + match[0].rm_so, match[0].rm_eo - match[0].rm_so); // cout << "Match: " << tstr << endl; sub1.assign(argument.c_str() + match[1].rm_so, match[1].rm_eo - match[1].rm_so); // cout << "Match1: " << sub1 << endl; sub2.assign(argument.c_str() + match[2].rm_so, match[2].rm_eo - match[2].rm_so); // cout << "Match2: " << sub2 << endl; return(1); /* match found */ } else { Fatal("regexec had a problem."); } // Not reached. return(1); } // Replace path entry with myCygpath() version void rep_subn_cyg(string &argument) { // accept ["']["']:["']["']:... to make the // $(WRAPCMD) echo 1 ICON $(EMQ)"$(APP1ICON)$(EMQ)" // work in ?tg_app.mk. // FIXME: Better would be to use a DOSified $(APP1ICON) there and remove // the special " treatment here. const char *pattern = "(^|[;,])[\'\"]?([[:alnum:]_~. +-]*(/[[:alnum:]_~. +-]+)+/?)[\'\"]?([;,]|$)"; const int subexp = 2; int status, pos=0; regex_t re; string repstr; string::size_type oLen, nLen; const int maxsub = subexp+1; // One more than the maximal subexpression regmatch_t match[maxsub]; if (regcomp(&re, pattern, REG_EXTENDED) != 0) { Fatal("regcomp had a problem."); /* report error */ } status = regexec (&re, argument.c_str() + pos, maxsub, match, 0); while (status == 0) { /* While matches found. */ // Classical assert() if (match[subexp].rm_eo == -1) { Fatal("Nonexisting subexpression specified!"); } oLen = match[subexp].rm_eo - match[subexp].rm_so; repstr.assign(argument.c_str() + pos + match[subexp].rm_so, oLen); // Do not replace with shortpaths myCygpath(repstr, 0); nLen = repstr.length(); // replace argument.replace( pos + match[subexp].rm_so, oLen, repstr ); /* Substring found between match[0].rm_so and match[0].rm_eo. */ /* This call to regexec() finds the next match. */ pos += match[0].rm_eo + nLen - oLen ; // Either the last match did end in ';' or we are at the end of para. // REG_NOTBOL is not used because we skip over the ';' by using pos. status = regexec (&re, argument.c_str() + pos, maxsub, match, 0); } if (status != REG_NOMATCH) { Fatal("regexec had a problem."); } regfree(&re); } // Replace all occurences of subexpression number "subexp" with "repl" void rep_subn( string &argument, const char *pattern, int subexp, const char repl) { int status, pos=0; regex_t re; const int maxsub = subexp+1; // One more than the maximal subexpression regmatch_t match[maxsub]; if (regcomp(&re, pattern, REG_EXTENDED) != 0) { Fatal("regcomp had a problem."); /* report error */ } status = regexec (&re, argument.c_str() + pos, maxsub, match, 0); while (status == 0) { /* While matches found. */ // Classical assert() if (match[subexp].rm_eo == -1) { Fatal("Nonexisting subexpression specified!"); } argument[pos + match[subexp].rm_so] = repl; /* Substring found between match[0].rm_so and match[0].rm_eo. */ /* This call to regexec() finds the next match. */ pos += match[0].rm_eo; status = regexec (&re, argument.c_str() + pos, maxsub, match, REG_NOTBOL); } if (status != REG_NOMATCH) { Fatal("regexec had a problem."); } regfree(&re); } // Replace all char1 with char2 void rep_char( string &argument, const char from, const char to) { string::size_type loc = 0; while ( (loc = argument.find( from, loc )) != string::npos ) { argument[loc] = to; } }