update dev300-m58
[ooovba.git] / guw / guw.cc
blobe7748a908ff157853475c206892bdeb9c40027a7
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: guw.cc,v $
10 * $Revision: 1.7 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // guw - A wrapper program to execute windows programs with parameters that
32 // contain cygwin (POSIX) style pathnames.
34 // Todo: Add a -? switch to guw to issue a help page.
36 #include <string>
37 #include <list>
38 #include <vector>
40 #include <iostream>
41 #include <sstream>
42 #include <fstream>
44 #include <cstddef>
45 #include <cerrno>
47 #include <sys/cygwin.h>
48 #include <windows.h>
49 #include <regex.h>
52 using std::string;
53 using std::list;
54 using std::vector;
55 using std::cout;
56 using std::cerr;
57 using std::endl;
58 using std::size_t;
60 void init_ignorepara(string command);
61 bool is_ignorepara(const string &para);
62 int winFormat(string &para);
63 void do_atcommand(string &para);
64 void myCygpath(string &path, int shortname = 1 );
65 void replace_cyg_env( void );
66 void Fatal( const string text );
68 int match2s(const string argument, const char *pattern, string &sub1, string &sub2);
69 void rep_subn_cyg(string &argument);
70 void rep_subn( string &argument, const char *pattern, int subexp, const char repl);
71 void rep_char( string &argument, const char from, const char to);
73 bool debug = false;
74 bool debug_light = false;
76 // The commands are treated case insensitive, the parameters
77 // are case sensitive.
78 const string ignorepara[] = { "echo /TEST",
79 "cl -clr: -Z",
80 "climaker StarOffice/OpenOffice",
81 "csc -target:",
82 "g++ -DLOCAL_RULE_LANGS -DUPD -DMINOR"
83 " -DBUILD_ID -DSC_INFO_OSVERSION",
84 "gcc -DUDATA_SO_SUFFIX -DSTATIC_O"
85 " -DPACKAGE -DU_MAKE",
86 "lib /OUT: -out: -def: -machine:",
87 "link /BASE: /COMMENT: /DEBUG: /DLL /ENTRY:"
88 " /MACHINE: /MAP /NODEFAULTLIB /OPT: /RELEASE"
89 " /STACK: /SUBSYSTEM: -NODEFAULTLIB:"
90 " -def: delayload: -implib: -map: -out:",
91 "rc -D",
92 "regcomp -env: vnd.sun.star.expand:"
93 " vnd.openoffice.pymodule: file:",
94 "regmerge /UCR",
95 "rsc -DOOO_" };
97 vector<string> ignorepara_vec;
99 // environment variables that are "winformatted" when -env is given
100 const string transformvars[] = { "SOLAR_VERSION",
101 "SOLARVERSION",
102 "SOLARVER",
103 "SRC_ROOT",
104 "LOCALINI",
105 "GLOBALINI",
106 "SOLARENV",
107 "STAR_INSTPATH",
108 "STAR_SOLARPATH",
109 "STAR_PACKMISC",
110 "STAR_SOLARENVPATH",
111 "STAR_INITROOT",
112 "STAR_STANDLST",
113 "CLASSPATH",
114 "JAVA_HOME" };
117 int main(int argc, char **argv) {
119 // initialize arglist with arguments
120 list<string> arglist(argv, argv + argc);
122 // Drop the first (filename) argument
123 arglist.pop_front();
125 // iterarot over cmdline elements
126 list<string>::iterator ele = arglist.begin();
128 // Allowed switch values
129 bool conv_cyg_arg = false;
131 // Look for switches to guw
132 // Supported: -env
133 // -dbg
134 // -ldbg
135 while ( !arglist.empty()
136 && ele!=arglist.end()
137 && (ele->find("-", 0) == 0) ) {
138 if (ele->find("-env", 0) == 0) {
139 if ( conv_cyg_arg )
140 Fatal("-env used twice!");
142 conv_cyg_arg = true;
143 ele = arglist.erase(ele);
144 continue;
146 else if (ele->find("-dbg", 0) == 0) {
148 debug = true;
149 ele = arglist.erase(ele);
150 continue;
152 else if (ele->find("-ldbg", 0) == 0) {
154 debug_light = true;
155 ele = arglist.erase(ele);
156 continue;
158 else {
159 // Ignore this switch
160 ++ele;
164 // The next entry must be the program
165 string command;
166 if ( !arglist.empty() ) {
167 command.assign(*arglist.begin());
168 arglist.pop_front();
170 else
171 Fatal("guw needs at least one parameter.");
173 if ( debug )
174 cerr << "Command: " << command << "\n" << endl;
175 // Initialize parameter exception list (for this command)
176 init_ignorepara(command);
179 // Do something if -env was found
180 if ( conv_cyg_arg )
181 replace_cyg_env();
184 // loop and and DOSify the parameters
185 if ( debug )
186 cerr << "Transform the parameter\n" << endl;
188 ele=arglist.begin();
189 while ( ele != arglist.end() ) {
191 if ((*ele)[0] == '@')
192 do_atcommand(*ele);
193 else if (!is_ignorepara(*ele)) {
194 if ( debug ) {
195 cerr << "----------------" << endl;
196 cerr << "Process parameter: " << *ele << endl;
198 winFormat(*ele);
199 if ( debug )
200 cerr << "Transformed to: " << *ele << "\n" << endl;
203 ++ele;
206 // create the argv[] for execvp(argv[0], argv);
207 ele=arglist.begin();
209 // const char *nargv[arglist.size()+2]; // or ..
210 char *nargv[arglist.size()+2];
212 // nargv[0] = command.c_str(); // or ..
213 nargv[0] = new char[command.length()+1];
214 // strcpy(nargv[0], command.c_str());
215 command.copy(nargv[0], command.length());
216 nargv[0][command.length()] = 0;
218 if ( debug )
219 cerr << "----------------\n" << endl;
220 if ( debug || debug_light )
221 cerr << "Execute: " << nargv[0];
223 int count = 1, sLen;
224 while ( ele != arglist.end() ) {
225 // nargv[count] = ele->c_str(); // or ..
226 sLen = ele->length();
227 nargv[count] = new char[sLen+1];
228 // strcpy(nargv[count], ele->c_str());
229 ele->copy(nargv[count], sLen);
230 nargv[count][sLen] = 0;
232 if ( debug || debug_light )
233 cerr << " " << nargv[count];
235 ++count;
236 ++ele;
238 // last nargv[] must be NULL
239 nargv[count] = NULL;
240 if ( debug || debug_light )
241 cerr << endl;
243 // Unfortunately the prototype of execvp does not like const char*,
244 // actually not const char* nargv[] coming from .c_str(). So either
245 // we copy everything into newly allocated variables or we force it
246 // with a cast. const_cast<char * const *>()
247 // execvp(nargv[0], const_cast<char * const *>(nargv) );
248 if ( execvp(nargv[0], nargv ) < 0 ) {
249 perror("Execvp error. Aborting.");
250 exit(1);
253 // Omit the deleting of the dynamically allocated nargv[] elements
254 // here as this part will never be reached.
256 return 0;
259 // Initialize exception list from global var ignorepara[]
260 void init_ignorepara(string fullcommand) {
261 const size_t kplen = sizeof(ignorepara)/sizeof(string *);
262 string shortcommand, cmd, para, sub2;
264 // First lowercase everything
265 for(size_t i=0;i<fullcommand.length();i++)
266 fullcommand[i] = tolower(fullcommand[i]);
268 // Remove a potential .exe
269 size_t slen = fullcommand.length();
271 // for slen == 3 this would yield string::npos otherwise
272 if ( slen > 4 && fullcommand.rfind(".exe") == slen - 4 )
273 fullcommand.erase(slen-4);
275 // get the program name - Only one subexpression
276 if (!match2s(fullcommand, "([[:alnum:]_~. +-]+)$",
277 shortcommand, sub2)) {
278 Fatal("No basename found in: " + fullcommand);
281 for (size_t i=0; i != kplen; ++i) {
282 std::istringstream line(ignorepara[i]);
283 line >> cmd;
284 if (shortcommand == cmd)
285 while (line >> para) {
286 ignorepara_vec.push_back(para);
289 return ;
292 // Check if command/parameter is in exception list.
293 bool is_ignorepara(const string &para) {
295 for( vector<string>::iterator it = ignorepara_vec.begin();
296 it != ignorepara_vec.end(); it++ ) {
297 if ( para.find(*it) != string::npos ) {
298 if ( debug )
299 cerr << "Found execption para: " << para << endl;
301 return true;
305 return false;
308 // Reformat para to DOSish format
309 int winFormat(string &para) {
310 string su1, su2;
312 // Instead of ([/[:alnum:]_~. +-]+) use ((/?[[:alnum:]_~. +-]+)+)
314 // find [-][-]X<something>=<path>, sometimes with quotes or "/" at the end
315 if (match2s(para, "^(-?-?[[:alpha:]][[:alnum:]_.-]*=)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
316 su1, su2)) {
318 myCygpath(su2);
319 para.assign(su1 + su2);
320 if ( debug )
321 cerr << " WinFormat - ([-][-]<something>=<path>)\n"
322 << " " << para << endl;
325 // find -X<something>:<path>, sometimes with quotes or "/" at the end
326 else if (match2s(para, "^(-[[:alpha:]][[:alnum:]_.]*:)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
327 su1, su2)) {
329 myCygpath(su2);
330 para.assign(su1 + su2);
331 if ( debug )
332 cerr << " WinFormat - (-<something>:<path>)\n"
333 << " " << para << endl;
336 // find -X<something>:<NO-path>, and prevents translating of these.
337 else if (match2s(para, "^(-[[:alpha:]][[:alnum:]_]*:)(.*)$",
338 su1, su2)) {
340 // myCygpath(su2);
341 // para.assign(su1 + su2);
342 if ( debug )
343 cerr << " WinFormat - (-<something>:<NO-path>)\n"
344 << " " << para << endl;
347 // See iz35982 for the reason for the special treatment of this switch.
348 // This regex evaluates <something>:///<path>, sometimes with
349 // quotes or "/" at the end
350 else if (match2s(para, "^([[:alpha:]][[:alnum:]_]*:)[\'\"]?///((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
351 su1, su2)) {
353 myCygpath(su2);
354 para.assign(su1 + "///" + su2);
355 // Replace \ to /
356 rep_char( para, '\\', '/');
358 if ( debug )
359 cerr << " WinFormat - (<something>:///<path>)\n"
360 << " " << para << endl;
363 // find -X<absolute path>, sometimes with quotes or "/" at the end
364 else if (match2s(para, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)[\'\"]?$",
365 su1, su2)) {
367 myCygpath(su2);
368 para.assign(su1 + su2);
369 if ( debug )
370 cerr << " WinFormat - (-X<absolute path>)\n"
371 << " " << para << endl;
374 // find -FX<path> (MSVC switches for output naming), sometimes with quotes
375 // or "/" at the end
376 else if (match2s(para, "^(-F[ARdemopr])[\'\"]?(/[/[:alnum:]_~. +-]+)[\'\"]?$",
377 su1, su2)) {
379 myCygpath(su2);
380 para.assign(su1 + su2);
381 if ( debug )
382 cerr << " WinFormat - (compiler naming (-FX<absolute path>) path)\n"
383 << " " << para << endl;
386 else{
387 // No parameter found, assume a path
389 // replace the colon in drives with 0x1F"
390 // (Unused ascii US - unit separator)
391 rep_subn( para, "(^|[;,])[[:alpha:]](:)", 2, 0x1F);
393 // Replace remaining : to ;
394 rep_char( para, ':', ';');
396 // Replace back US to ':';
397 rep_char( para, 0x1F, ':');
399 /* Search for posix path ;entry; (The regex accepts valid paths with at
400 * least one /) and replace with DOS path, accept quotes.
401 * since iz28717 we also accept ',' as path seperator. */
402 rep_subn_cyg(para);
404 if ( debug )
405 cerr << " WinFormat - full path\n"
406 << " " << para << endl;
410 // Sanity check for -X<abspath>
411 if (match2s(para, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
412 su1, su2)) {
413 Fatal("Not converted -X/... type switch in :" + para);
415 // Sanity check for [-]X<something>(:|=)<abspath> case
416 if (match2s(para, "^(-?[[:alpha:]][[:alnum:]_.]+[=:])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
417 su1, su2)) {
418 Fatal("Not processed [-]X<something>(=|:)/... in :" + para);
422 return 1;
425 // Reformat para to DOSish format
426 void do_atcommand(string &para) {
427 string at, filename, token;
429 // Workaround, iz28717, keep number of @'s.
430 match2s(para, "^(@+)(.*)",at ,filename);
431 if ( debug ) {
432 cerr << "----------------" << endl;
433 cerr << "Process @-file" << endl;
434 cerr << " :" << at << ": before filename :" << filename << ":" << endl;
437 // Read at file into memory
438 std::ifstream atin(filename.c_str());
439 list<string> newtoken;
440 while (atin >> token) {
441 // Read / transform tokens
442 if ( debug )
443 cerr << "@ token :" << token << ":" << endl;
444 if (!is_ignorepara(token))
445 winFormat(token);
447 newtoken.push_back(token);
449 atin.close();
451 // Write token tokens bak to file
452 if ( debug || debug_light )
453 cerr << "New @-file parameter:" << endl;
455 // for debugging ..
456 // filename += ".bak";
458 std::ofstream atout(filename.c_str());
459 list<string>::iterator tok = newtoken.begin();
460 while ( tok != newtoken.end() ) {
461 if ( debug || debug_light )
462 cerr << ( tok != newtoken.begin() ? " " : "" ) << *tok ;
464 atout << ( tok != newtoken.begin() ? " " : "" ) << *tok ;
465 ++tok;
467 // We want a dos file
468 atout << '\r' << endl;
469 atout.close();
471 // Transform the filename
472 winFormat(filename);
473 para = at + filename;
474 if ( debug || debug_light ) {
475 cerr << "\nNew @-file name: " << para << "\n" << endl;
479 void myCygpath(string &path, int shortname /* =1 */ )
481 static char convpath[MAX_PATH];
482 static char buf[MAX_PATH];
483 int err;
485 // Only use cygwin_conv_to_win32_path() on absolute paths as it errors
486 // out if its path doen't exist. Unfortunatelt there are a lot of not
487 // existing relative pathes used as parameters during an OOo build.
488 if( path.find("/", 0) == 0) {
489 err = cygwin_conv_to_win32_path( path.c_str(), convpath );
491 else {
492 rep_char( path, '/', '\\');
493 // see below, we copy convpath back to path, that's stupid
494 path.copy( convpath, path.length());
495 convpath[path.length()] = 0;
496 err = 0;
499 if (err)
500 Fatal("converting: " + path + " - " + strerror(errno) );
502 // Only convert to short dos names when space is present
503 if (shortname && (path.find(" ", 0) != string::npos) ) {
504 DWORD len = GetShortPathName (convpath, buf, MAX_PATH);
505 if (!len) {
506 Fatal("cannot create short name of " + string(convpath) );
509 path.assign(buf);
511 else
512 path.assign(convpath);
516 void replace_cyg_env( void ) {
517 // Transform certain environment variables
518 if ( debug )
519 cerr << "Transforming some environment variables" << endl;
521 const size_t nvars = sizeof(transformvars)/sizeof(string *);
523 char *currvar;
524 string newvar;
526 for (size_t i=0; i != nvars; ++i) {
527 if ( currvar = getenv(transformvars[i].c_str() ) ) {
528 // Only transform existent vars
529 if ( debug )
530 cerr << "Transform variable: " << transformvars[i] << "="
531 << currvar << endl;
532 newvar.assign(currvar);
533 winFormat(newvar);
534 if( setenv(transformvars[i].c_str(), newvar.c_str(), 1) )
535 Fatal("setenv failed on " + transformvars[i] + "=" + newvar +
536 " with error: " + strerror(errno));
537 if ( debug )
538 cerr << "To: " << transformvars[i] << "="
539 << newvar << "\n" << endl;
545 void Fatal( const string text ) {
546 // End with error
547 cerr << "Error: " << text << endl;
548 exit(1);
553 match2s(const string argument, const char *pattern, string &sub1, string &sub2)
555 int status;
556 regex_t re;
558 const int maxsub = 3; // Only 3 needed, 4 is for debug
559 regmatch_t match[maxsub];
561 if (regcomp(&re, pattern, REG_EXTENDED) != 0) {
562 Fatal("regcomp had a problem."); /* report error */
564 status = regexec(&re, argument.c_str(), maxsub, match, 0);
565 regfree(&re);
567 if (status == REG_NOMATCH) {
568 return(0); /* no match */
569 } else if (status == 0) {
570 string tstr(argument.c_str() + match[0].rm_so,
571 match[0].rm_eo - match[0].rm_so);
572 // cout << "Match: " << tstr << endl;
574 sub1.assign(argument.c_str() + match[1].rm_so, match[1].rm_eo - match[1].rm_so);
575 // cout << "Match1: " << sub1 << endl;
577 sub2.assign(argument.c_str() + match[2].rm_so, match[2].rm_eo - match[2].rm_so);
578 // cout << "Match2: " << sub2 << endl;
580 return(1); /* match found */
581 } else {
582 Fatal("regexec had a problem.");
585 // Not reached.
586 return(1);
590 // Replace path entry with myCygpath() version
591 void rep_subn_cyg(string &argument)
593 // accept ["']<entry>["']:["']<entry>["']:... to make the
594 // $(WRAPCMD) echo 1 ICON $(EMQ)"$(APP1ICON)$(EMQ)"
595 // work in ?tg_app.mk.
596 // FIXME: Better would be to use a DOSified $(APP1ICON) there and remove
597 // the special " treatment here.
598 const char *pattern = "(^|[;,])[\'\"]?([[:alnum:]_~. +-]*(/[[:alnum:]_~. +-]+)+/?)[\'\"]?([;,]|$)";
599 const int subexp = 2;
601 int status, pos=0;
602 regex_t re;
604 string repstr;
605 string::size_type oLen, nLen;
607 const int maxsub = subexp+1; // One more than the maximal subexpression
608 regmatch_t match[maxsub];
610 if (regcomp(&re, pattern, REG_EXTENDED) != 0) {
611 Fatal("regcomp had a problem."); /* report error */
613 status = regexec (&re, argument.c_str() + pos, maxsub, match, 0);
614 while (status == 0) { /* While matches found. */
615 // Classical assert()
616 if (match[subexp].rm_eo == -1) {
617 Fatal("Nonexisting subexpression specified!");
620 oLen = match[subexp].rm_eo - match[subexp].rm_so;
621 repstr.assign(argument.c_str() + pos + match[subexp].rm_so, oLen);
623 // Do not replace with shortpaths
624 myCygpath(repstr, 0);
625 nLen = repstr.length();
627 // replace
628 argument.replace( pos + match[subexp].rm_so, oLen, repstr );
630 /* Substring found between match[0].rm_so and match[0].rm_eo. */
631 /* This call to regexec() finds the next match. */
633 pos += match[0].rm_eo + nLen - oLen ;
635 // Either the last match did end in ';' or we are at the end of para.
636 // REG_NOTBOL is not used because we skip over the ';' by using pos.
637 status = regexec (&re, argument.c_str() + pos, maxsub, match, 0);
640 if (status != REG_NOMATCH) {
641 Fatal("regexec had a problem.");
644 regfree(&re);
647 // Replace all occurences of subexpression number "subexp" with "repl"
648 void rep_subn( string &argument, const char *pattern, int subexp, const char repl)
650 int status, pos=0;
651 regex_t re;
653 const int maxsub = subexp+1; // One more than the maximal subexpression
654 regmatch_t match[maxsub];
656 if (regcomp(&re, pattern, REG_EXTENDED) != 0) {
657 Fatal("regcomp had a problem."); /* report error */
659 status = regexec (&re, argument.c_str() + pos, maxsub, match, 0);
660 while (status == 0) { /* While matches found. */
661 // Classical assert()
662 if (match[subexp].rm_eo == -1) {
663 Fatal("Nonexisting subexpression specified!");
666 argument[pos + match[subexp].rm_so] = repl;
668 /* Substring found between match[0].rm_so and match[0].rm_eo. */
669 /* This call to regexec() finds the next match. */
670 pos += match[0].rm_eo;
671 status = regexec (&re, argument.c_str() + pos, maxsub, match, REG_NOTBOL);
674 if (status != REG_NOMATCH) {
675 Fatal("regexec had a problem.");
678 regfree(&re);
681 // Replace all char1 with char2
682 void rep_char( string &argument, const char from, const char to)
684 string::size_type loc = 0;
686 while ( (loc = argument.find( from, loc )) != string::npos ) {
687 argument[loc] = to;