PageRenderTime 27ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/libreoffice-3.6.0.2/guw/guw.cc

#
C++ | 678 lines | 419 code | 131 blank | 128 comment | 126 complexity | 43921663832deb0bbe826deb445ea842 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, AGPL-1.0, BSD-3-Clause-No-Nuclear-License-2014, GPL-3.0, LGPL-3.0
  1. /*************************************************************************
  2. *
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * Copyright 2000, 2010 Oracle and/or its affiliates.
  6. *
  7. * OpenOffice.org - a multi-platform office productivity suite
  8. *
  9. * This file is part of OpenOffice.org.
  10. *
  11. * OpenOffice.org is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Lesser General Public License version 3
  13. * only, as published by the Free Software Foundation.
  14. *
  15. * OpenOffice.org is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Lesser General Public License version 3 for more details
  19. * (a copy is included in the LICENSE file that accompanied this code).
  20. *
  21. * You should have received a copy of the GNU Lesser General Public License
  22. * version 3 along with OpenOffice.org. If not, see
  23. * <http://www.openoffice.org/license.html>
  24. * for a copy of the LGPLv3 License.
  25. *
  26. ************************************************************************/
  27. // guw - A wrapper program to execute windows programs with parameters that
  28. // contain cygwin (POSIX) style pathnames.
  29. // Todo: Add a -? switch to guw to issue a help page.
  30. #include <string>
  31. #include <list>
  32. #include <vector>
  33. #include <iostream>
  34. #include <sstream>
  35. #include <fstream>
  36. #include <cstddef>
  37. #include <cerrno>
  38. #include <sys/cygwin.h>
  39. #include <windows.h>
  40. #include <regex.h>
  41. using std::string;
  42. using std::list;
  43. using std::vector;
  44. using std::cout;
  45. using std::cerr;
  46. using std::endl;
  47. using std::size_t;
  48. void init_ignorepara(string command);
  49. bool is_ignorepara(const string &para);
  50. int winFormat(string &para);
  51. void do_atcommand(string &para);
  52. void myCygpath(string &path, int shortname = 1 );
  53. void replace_cyg_env( void );
  54. void Fatal( const string text );
  55. int match2s(const string argument, const char *pattern, string &sub1, string &sub2);
  56. void rep_subn_cyg(string &argument);
  57. void rep_subn( string &argument, const char *pattern, int subexp, const char repl);
  58. void rep_char( string &argument, const char from, const char to);
  59. bool debug = false;
  60. bool debug_light = false;
  61. // The commands are treated case insensitive, the parameters
  62. // are case sensitive.
  63. const string ignorepara[] = { "echo /TEST",
  64. "cl -clr: -Z",
  65. "climaker StarOffice/OpenOffice",
  66. "csc -target:",
  67. "g++ -DLOCAL_RULE_LANGS -DUPD -DMINOR"
  68. " -DBUILD_ID -DSC_INFO_OSVERSION",
  69. "gcc -DUDATA_SO_SUFFIX -DSTATIC_O"
  70. " -DPACKAGE -DU_MAKE",
  71. "lib /OUT: -out: -def: -machine:",
  72. "link /BASE: /COMMENT: /DEBUG: /DLL /ENTRY:"
  73. " /MACHINE: /MAP /NODEFAULTLIB /OPT: /RELEASE"
  74. " /STACK: /SUBSYSTEM: -NODEFAULTLIB:"
  75. " -def: delayload: -implib: -map: -out:",
  76. "rc -D",
  77. "regcomp -env: vnd.sun.star.expand:"
  78. " vnd.openoffice.pymodule: file:",
  79. "regmerge /UCR",
  80. "rsc -DOOO_" };
  81. vector<string> ignorepara_vec;
  82. // environment variables that are "winformatted" when -env is given
  83. const string transformvars[] = { "SOLAR_VERSION",
  84. "SOLARVERSION",
  85. "SOLARVER",
  86. "SRC_ROOT",
  87. "SOLARENV",
  88. "CLASSPATH",
  89. "JAVA_HOME" };
  90. int main(int argc, char **argv) {
  91. // initialize arglist with arguments
  92. list<string> arglist(argv, argv + argc);
  93. // Drop the first (filename) argument
  94. arglist.pop_front();
  95. // iterarot over cmdline elements
  96. list<string>::iterator ele = arglist.begin();
  97. // Allowed switch values
  98. bool conv_cyg_arg = false;
  99. // Look for switches to guw
  100. // Supported: -env
  101. // -dbg
  102. // -ldbg
  103. while ( !arglist.empty()
  104. && ele!=arglist.end()
  105. && (ele->find("-", 0) == 0) ) {
  106. if (ele->find("-env", 0) == 0) {
  107. if ( conv_cyg_arg )
  108. Fatal("-env used twice!");
  109. conv_cyg_arg = true;
  110. ele = arglist.erase(ele);
  111. continue;
  112. }
  113. else if (ele->find("-dbg", 0) == 0) {
  114. debug = true;
  115. ele = arglist.erase(ele);
  116. continue;
  117. }
  118. else if (ele->find("-ldbg", 0) == 0) {
  119. debug_light = true;
  120. ele = arglist.erase(ele);
  121. continue;
  122. }
  123. else {
  124. // Ignore this switch
  125. ++ele;
  126. }
  127. }
  128. // The next entry must be the program
  129. string command;
  130. if ( !arglist.empty() ) {
  131. command.assign(*arglist.begin());
  132. arglist.pop_front();
  133. }
  134. else
  135. Fatal("guw needs at least one parameter.");
  136. if ( debug )
  137. cerr << "Command: " << command << "\n" << endl;
  138. // Initialize parameter exception list (for this command)
  139. init_ignorepara(command);
  140. // Do something if -env was found
  141. if ( conv_cyg_arg )
  142. replace_cyg_env();
  143. // loop and and DOSify the parameters
  144. if ( debug )
  145. cerr << "Transform the parameter\n" << endl;
  146. ele=arglist.begin();
  147. while ( ele != arglist.end() ) {
  148. if ((*ele)[0] == '@')
  149. do_atcommand(*ele);
  150. else if (!is_ignorepara(*ele)) {
  151. if ( debug ) {
  152. cerr << "----------------" << endl;
  153. cerr << "Process parameter: " << *ele << endl;
  154. }
  155. winFormat(*ele);
  156. if ( debug )
  157. cerr << "Transformed to: " << *ele << "\n" << endl;
  158. }
  159. ++ele;
  160. }
  161. // create the argv[] for execvp(argv[0], argv);
  162. ele=arglist.begin();
  163. // const char *nargv[arglist.size()+2]; // or ..
  164. char *nargv[arglist.size()+2];
  165. // nargv[0] = command.c_str(); // or ..
  166. nargv[0] = new char[command.length()+1];
  167. // strcpy(nargv[0], command.c_str());
  168. command.copy(nargv[0], command.length());
  169. nargv[0][command.length()] = 0;
  170. if ( debug )
  171. cerr << "----------------\n" << endl;
  172. if ( debug || debug_light )
  173. cerr << "Execute: " << nargv[0];
  174. int count = 1, sLen;
  175. while ( ele != arglist.end() ) {
  176. // nargv[count] = ele->c_str(); // or ..
  177. sLen = ele->length();
  178. nargv[count] = new char[sLen+1];
  179. // strcpy(nargv[count], ele->c_str());
  180. ele->copy(nargv[count], sLen);
  181. nargv[count][sLen] = 0;
  182. if ( debug || debug_light )
  183. cerr << " " << nargv[count];
  184. ++count;
  185. ++ele;
  186. }
  187. // last nargv[] must be NULL
  188. nargv[count] = NULL;
  189. if ( debug || debug_light )
  190. cerr << endl;
  191. // Unfortunately the prototype of execvp does not like const char*,
  192. // actually not const char* nargv[] coming from .c_str(). So either
  193. // we copy everything into newly allocated variables or we force it
  194. // with a cast. const_cast<char * const *>()
  195. // execvp(nargv[0], const_cast<char * const *>(nargv) );
  196. if ( execvp(nargv[0], nargv ) < 0 ) {
  197. perror("Execvp error. Aborting.");
  198. exit(1);
  199. }
  200. // Omit the deleting of the dynamically allocated nargv[] elements
  201. // here as this part will never be reached.
  202. return 0;
  203. }
  204. // Initialize exception list from global var ignorepara[]
  205. void init_ignorepara(string fullcommand) {
  206. const size_t kplen = sizeof(ignorepara)/sizeof(string *);
  207. string shortcommand, cmd, para, sub2;
  208. // First lowercase everything
  209. for(size_t i=0;i<fullcommand.length();i++)
  210. fullcommand[i] = tolower(fullcommand[i]);
  211. // Remove a potential .exe
  212. size_t slen = fullcommand.length();
  213. // for slen == 3 this would yield string::npos otherwise
  214. if ( slen > 4 && fullcommand.rfind(".exe") == slen - 4 )
  215. fullcommand.erase(slen-4);
  216. // get the program name - Only one subexpression
  217. if (!match2s(fullcommand, "([[:alnum:]_~. +-]+)$",
  218. shortcommand, sub2)) {
  219. Fatal("No basename found in: " + fullcommand);
  220. }
  221. for (size_t i=0; i != kplen; ++i) {
  222. std::istringstream line(ignorepara[i]);
  223. line >> cmd;
  224. if (shortcommand == cmd)
  225. while (line >> para) {
  226. ignorepara_vec.push_back(para);
  227. }
  228. }
  229. return ;
  230. }
  231. // Check if command/parameter is in exception list.
  232. bool is_ignorepara(const string &para) {
  233. for( vector<string>::iterator it = ignorepara_vec.begin();
  234. it != ignorepara_vec.end(); ++it ) {
  235. if ( para.find(*it) != string::npos ) {
  236. if ( debug )
  237. cerr << "Found execption para: " << para << endl;
  238. return true;
  239. }
  240. }
  241. return false;
  242. }
  243. // Reformat para to DOSish format
  244. int winFormat(string &para) {
  245. string su1, su2;
  246. // Instead of ([/[:alnum:]_~. +-]+) use ((/?[[:alnum:]_~. +-]+)+)
  247. // find [-][-]X<something>=<path>, sometimes with quotes or "/" at the end
  248. if (match2s(para, "^(-?-?[[:alpha:]][[:alnum:]_.-]*=)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
  249. su1, su2)) {
  250. myCygpath(su2);
  251. para.assign(su1 + su2);
  252. if ( debug )
  253. cerr << " WinFormat - ([-][-]<something>=<path>)\n"
  254. << " " << para << endl;
  255. }
  256. // find -X<something>:<path>, sometimes with quotes or "/" at the end
  257. else if (match2s(para, "^(-[[:alpha:]][[:alnum:]_.]*:)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
  258. su1, su2)) {
  259. myCygpath(su2);
  260. para.assign(su1 + su2);
  261. if ( debug )
  262. cerr << " WinFormat - (-<something>:<path>)\n"
  263. << " " << para << endl;
  264. }
  265. // find -X<something>:<NO-path>, and prevents translating of these.
  266. else if (match2s(para, "^(-[[:alpha:]][[:alnum:]_]*:)(.*)$",
  267. su1, su2)) {
  268. // myCygpath(su2);
  269. // para.assign(su1 + su2);
  270. if ( debug )
  271. cerr << " WinFormat - (-<something>:<NO-path>)\n"
  272. << " " << para << endl;
  273. }
  274. // See iz35982 for the reason for the special treatment of this switch.
  275. // This regex evaluates <something>:///<path>, sometimes with
  276. // quotes or "/" at the end
  277. else if (match2s(para, "^([[:alpha:]][[:alnum:]_]*:)[\'\"]?///((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
  278. su1, su2)) {
  279. myCygpath(su2);
  280. para.assign(su1 + "///" + su2);
  281. // Replace \ to /
  282. rep_char( para, '\\', '/');
  283. if ( debug )
  284. cerr << " WinFormat - (<something>:///<path>)\n"
  285. << " " << para << endl;
  286. }
  287. // find -X<absolute path>, sometimes with quotes or "/" at the end
  288. else if (match2s(para, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)[\'\"]?$",
  289. su1, su2)) {
  290. myCygpath(su2);
  291. para.assign(su1 + su2);
  292. if ( debug )
  293. cerr << " WinFormat - (-X<absolute path>)\n"
  294. << " " << para << endl;
  295. }
  296. // find -FX<path> (MSVC switches for output naming), sometimes with quotes
  297. // or "/" at the end
  298. else if (match2s(para, "^(-F[ARdemopr])[\'\"]?(/[/[:alnum:]_~. +-]+)[\'\"]?$",
  299. su1, su2)) {
  300. myCygpath(su2);
  301. para.assign(su1 + su2);
  302. if ( debug )
  303. cerr << " WinFormat - (compiler naming (-FX<absolute path>) path)\n"
  304. << " " << para << endl;
  305. }
  306. else{
  307. // No parameter found, assume a path
  308. // replace the colon in drives with 0x1F"
  309. // (Unused ascii US - unit separator)
  310. rep_subn( para, "(^|[;,])[[:alpha:]](:)", 2, 0x1F);
  311. // Replace remaining : to ;
  312. rep_char( para, ':', ';');
  313. // Replace back US to ':';
  314. rep_char( para, 0x1F, ':');
  315. /* Search for posix path ;entry; (The regex accepts valid paths with at
  316. * least one /) and replace with DOS path, accept quotes.
  317. * since iz28717 we also accept ',' as path seperator. */
  318. rep_subn_cyg(para);
  319. if ( debug )
  320. cerr << " WinFormat - full path\n"
  321. << " " << para << endl;
  322. }
  323. // Sanity check for -X<abspath>
  324. if (match2s(para, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
  325. su1, su2)) {
  326. Fatal("Not converted -X/... type switch in :" + para);
  327. }
  328. // Sanity check for [-]X<something>(:|=)<abspath> case
  329. if (match2s(para, "^(-?[[:alpha:]][[:alnum:]_.]+[=:])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
  330. su1, su2)) {
  331. Fatal("Not processed [-]X<something>(=|:)/... in :" + para);
  332. }
  333. return 1;
  334. }
  335. // Reformat para to DOSish format
  336. void do_atcommand(string &para) {
  337. string at, filename, token;
  338. // Workaround, iz28717, keep number of @'s.
  339. match2s(para, "^(@+)(.*)",at ,filename);
  340. if ( debug ) {
  341. cerr << "----------------" << endl;
  342. cerr << "Process @-file" << endl;
  343. cerr << " :" << at << ": before filename :" << filename << ":" << endl;
  344. }
  345. // Read at file into memory
  346. std::ifstream atin(filename.c_str());
  347. list<string> newtoken;
  348. while (atin >> token) {
  349. // Read / transform tokens
  350. if ( debug )
  351. cerr << "@ token :" << token << ":" << endl;
  352. if (!is_ignorepara(token))
  353. winFormat(token);
  354. newtoken.push_back(token);
  355. }
  356. atin.close();
  357. // Write token tokens bak to file
  358. if ( debug || debug_light )
  359. cerr << "New @-file parameter:" << endl;
  360. // for debugging ..
  361. // filename += ".bak";
  362. std::ofstream atout(filename.c_str());
  363. list<string>::iterator tok = newtoken.begin();
  364. while ( tok != newtoken.end() ) {
  365. if ( debug || debug_light )
  366. cerr << ( tok != newtoken.begin() ? " " : "" ) << *tok ;
  367. atout << ( tok != newtoken.begin() ? " " : "" ) << *tok ;
  368. ++tok;
  369. }
  370. // We want a dos file
  371. atout << '\r' << endl;
  372. atout.close();
  373. // Transform the filename
  374. winFormat(filename);
  375. para = at + filename;
  376. if ( debug || debug_light ) {
  377. cerr << "\nNew @-file name: " << para << "\n" << endl;
  378. }
  379. }
  380. void myCygpath(string &path, int shortname /* =1 */ )
  381. {
  382. static char convpath[MAX_PATH];
  383. static char buf[MAX_PATH];
  384. int err;
  385. // Only use cygwin_conv_to_win32_path() on absolute paths as it errors
  386. // out if its path doen't exist. Unfortunatelt there are a lot of not
  387. // existing relative paths used as parameters during an OOo build.
  388. if( path.find("/", 0) == 0) {
  389. err = cygwin_conv_to_win32_path( path.c_str(), convpath );
  390. }
  391. else {
  392. rep_char( path, '/', '\\');
  393. // see below, we copy convpath back to path, that's stupid
  394. path.copy( convpath, path.length());
  395. convpath[path.length()] = 0;
  396. err = 0;
  397. }
  398. if (err)
  399. Fatal("converting: " + path + " - " + strerror(errno) );
  400. // Only convert to short dos names when space is present
  401. if (shortname && (path.find(" ", 0) != string::npos) ) {
  402. DWORD len = GetShortPathName (convpath, buf, MAX_PATH);
  403. if (!len) {
  404. Fatal("cannot create short name of " + string(convpath) );
  405. }
  406. path.assign(buf);
  407. }
  408. else
  409. path.assign(convpath);
  410. }
  411. void replace_cyg_env( void ) {
  412. // Transform certain environment variables
  413. if ( debug )
  414. cerr << "Transforming some environment variables" << endl;
  415. const size_t nvars = sizeof(transformvars)/sizeof(string *);
  416. char *currvar;
  417. string newvar;
  418. for (size_t i=0; i != nvars; ++i) {
  419. if ( currvar = getenv(transformvars[i].c_str() ) ) {
  420. // Only transform existent vars
  421. if ( debug )
  422. cerr << "Transform variable: " << transformvars[i] << "="
  423. << currvar << endl;
  424. newvar.assign(currvar);
  425. winFormat(newvar);
  426. if( setenv(transformvars[i].c_str(), newvar.c_str(), 1) )
  427. Fatal("setenv failed on " + transformvars[i] + "=" + newvar +
  428. " with error: " + strerror(errno));
  429. if ( debug )
  430. cerr << "To: " << transformvars[i] << "="
  431. << newvar << "\n" << endl;
  432. }
  433. }
  434. }
  435. void Fatal( const string text ) {
  436. // End with error
  437. cerr << "Error: " << text << endl;
  438. exit(1);
  439. }
  440. int
  441. match2s(const string argument, const char *pattern, string &sub1, string &sub2)
  442. {
  443. int status;
  444. regex_t re;
  445. const int maxsub = 3; // Only 3 needed, 4 is for debug
  446. regmatch_t match[maxsub];
  447. if (regcomp(&re, pattern, REG_EXTENDED) != 0) {
  448. Fatal("regcomp had a problem."); /* report error */
  449. }
  450. status = regexec(&re, argument.c_str(), maxsub, match, 0);
  451. regfree(&re);
  452. if (status == REG_NOMATCH) {
  453. return(0); /* no match */
  454. } else if (status == 0) {
  455. string tstr(argument.c_str() + match[0].rm_so,
  456. match[0].rm_eo - match[0].rm_so);
  457. // cout << "Match: " << tstr << endl;
  458. sub1.assign(argument.c_str() + match[1].rm_so, match[1].rm_eo - match[1].rm_so);
  459. // cout << "Match1: " << sub1 << endl;
  460. sub2.assign(argument.c_str() + match[2].rm_so, match[2].rm_eo - match[2].rm_so);
  461. // cout << "Match2: " << sub2 << endl;
  462. return(1); /* match found */
  463. } else {
  464. Fatal("regexec had a problem.");
  465. }
  466. // Not reached.
  467. return(1);
  468. }
  469. // Replace path entry with myCygpath() version
  470. void rep_subn_cyg(string &argument)
  471. {
  472. // accept ["']<entry>["']:["']<entry>["']:... to make the
  473. // $(WRAPCMD) echo 1 ICON $(EMQ)"$(APP1ICON)$(EMQ)"
  474. // work in ?tg_app.mk.
  475. // FIXME: Better would be to use a DOSified $(APP1ICON) there and remove
  476. // the special " treatment here.
  477. const char *pattern = "(^|[;,])[\'\"]?([[:alnum:]_~. +-]*(/[[:alnum:]_~. +-]+)+/?)[\'\"]?([;,]|$)";
  478. const int subexp = 2;
  479. int status, pos=0;
  480. regex_t re;
  481. string repstr;
  482. string::size_type oLen, nLen;
  483. const int maxsub = subexp+1; // One more than the maximal subexpression
  484. regmatch_t match[maxsub];
  485. if (regcomp(&re, pattern, REG_EXTENDED) != 0) {
  486. Fatal("regcomp had a problem."); /* report error */
  487. }
  488. status = regexec (&re, argument.c_str() + pos, maxsub, match, 0);
  489. while (status == 0) { /* While matches found. */
  490. // Classical assert()
  491. if (match[subexp].rm_eo == -1) {
  492. Fatal("Nonexisting subexpression specified!");
  493. }
  494. oLen = match[subexp].rm_eo - match[subexp].rm_so;
  495. repstr.assign(argument.c_str() + pos + match[subexp].rm_so, oLen);
  496. // Do not replace with shortpaths
  497. myCygpath(repstr, 0);
  498. nLen = repstr.length();
  499. // replace
  500. argument.replace( pos + match[subexp].rm_so, oLen, repstr );
  501. /* Substring found between match[0].rm_so and match[0].rm_eo. */
  502. /* This call to regexec() finds the next match. */
  503. pos += match[0].rm_eo + nLen - oLen ;
  504. // Either the last match did end in ';' or we are at the end of para.
  505. // REG_NOTBOL is not used because we skip over the ';' by using pos.
  506. status = regexec (&re, argument.c_str() + pos, maxsub, match, 0);
  507. }
  508. if (status != REG_NOMATCH) {
  509. Fatal("regexec had a problem.");
  510. }
  511. regfree(&re);
  512. }
  513. // Replace all occurrences of subexpression number "subexp" with "repl"
  514. void rep_subn( string &argument, const char *pattern, int subexp, const char repl)
  515. {
  516. int status, pos=0;
  517. regex_t re;
  518. const int maxsub = subexp+1; // One more than the maximal subexpression
  519. regmatch_t match[maxsub];
  520. if (regcomp(&re, pattern, REG_EXTENDED) != 0) {
  521. Fatal("regcomp had a problem."); /* report error */
  522. }
  523. status = regexec (&re, argument.c_str() + pos, maxsub, match, 0);
  524. while (status == 0) { /* While matches found. */
  525. // Classical assert()
  526. if (match[subexp].rm_eo == -1) {
  527. Fatal("Nonexisting subexpression specified!");
  528. }
  529. argument[pos + match[subexp].rm_so] = repl;
  530. /* Substring found between match[0].rm_so and match[0].rm_eo. */
  531. /* This call to regexec() finds the next match. */
  532. pos += match[0].rm_eo;
  533. status = regexec (&re, argument.c_str() + pos, maxsub, match, REG_NOTBOL);
  534. }
  535. if (status != REG_NOMATCH) {
  536. Fatal("regexec had a problem.");
  537. }
  538. regfree(&re);
  539. }
  540. // Replace all char1 with char2
  541. void rep_char( string &argument, const char from, const char to)
  542. {
  543. string::size_type loc = 0;
  544. while ( (loc = argument.find( from, loc )) != string::npos ) {
  545. argument[loc] = to;
  546. }
  547. }