1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
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.
47 #include <sys/cygwin.h>
60 void init_ignorepara(string command
);
61 bool is_ignorepara(const string
¶
);
62 int winFormat(string
¶
);
63 void do_atcommand(string
¶
);
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
);
74 bool debug_light
= false;
76 // The commands are treated case insensitive, the parameters
77 // are case sensitive.
78 const string ignorepara
[] = { "echo /TEST",
80 "climaker StarOffice/OpenOffice",
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:",
92 "regcomp -env: vnd.sun.star.expand:"
93 " vnd.openoffice.pymodule: file:",
97 vector
<string
> ignorepara_vec
;
99 // environment variables that are "winformatted" when -env is given
100 const string transformvars
[] = { "SOLAR_VERSION",
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
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
135 while ( !arglist
.empty()
136 && ele
!=arglist
.end()
137 && (ele
->find("-", 0) == 0) ) {
138 if (ele
->find("-env", 0) == 0) {
140 Fatal("-env used twice!");
143 ele
= arglist
.erase(ele
);
146 else if (ele
->find("-dbg", 0) == 0) {
149 ele
= arglist
.erase(ele
);
152 else if (ele
->find("-ldbg", 0) == 0) {
155 ele
= arglist
.erase(ele
);
159 // Ignore this switch
164 // The next entry must be the program
166 if ( !arglist
.empty() ) {
167 command
.assign(*arglist
.begin());
171 Fatal("guw needs at least one parameter.");
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
184 // loop and and DOSify the parameters
186 cerr
<< "Transform the parameter\n" << endl
;
189 while ( ele
!= arglist
.end() ) {
191 if ((*ele
)[0] == '@')
193 else if (!is_ignorepara(*ele
)) {
195 cerr
<< "----------------" << endl
;
196 cerr
<< "Process parameter: " << *ele
<< endl
;
200 cerr
<< "Transformed to: " << *ele
<< "\n" << endl
;
206 // create the argv[] for execvp(argv[0], argv);
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;
219 cerr
<< "----------------\n" << endl
;
220 if ( debug
|| debug_light
)
221 cerr
<< "Execute: " << nargv
[0];
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
];
238 // last nargv[] must be NULL
240 if ( debug
|| debug_light
)
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.");
253 // Omit the deleting of the dynamically allocated nargv[] elements
254 // here as this part will never be reached.
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
]);
284 if (shortcommand
== cmd
)
285 while (line
>> para
) {
286 ignorepara_vec
.push_back(para
);
292 // Check if command/parameter is in exception list.
293 bool is_ignorepara(const string
¶
) {
295 for( vector
<string
>::iterator it
= ignorepara_vec
.begin();
296 it
!= ignorepara_vec
.end(); it
++ ) {
297 if ( para
.find(*it
) != string::npos
) {
299 cerr
<< "Found execption para: " << para
<< endl
;
308 // Reformat para to DOSish format
309 int winFormat(string
¶
) {
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:]_~. +-]+)+)[\'\"]?$",
319 para
.assign(su1
+ su2
);
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:]_~. +-]+)+)[\'\"]?$",
330 para
.assign(su1
+ su2
);
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:]_]*:)(.*)$",
341 // para.assign(su1 + su2);
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:]_~. +-]+)+)[\'\"]?$",
354 para
.assign(su1
+ "///" + su2
);
356 rep_char( para
, '\\', '/');
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:]_~. +-]+)+)[\'\"]?$",
368 para
.assign(su1
+ su2
);
370 cerr
<< " WinFormat - (-X<absolute path>)\n"
371 << " " << para
<< endl
;
374 // find -FX<path> (MSVC switches for output naming), sometimes with quotes
376 else if (match2s(para
, "^(-F[ARdemopr])[\'\"]?(/[/[:alnum:]_~. +-]+)[\'\"]?$",
380 para
.assign(su1
+ su2
);
382 cerr
<< " WinFormat - (compiler naming (-FX<absolute path>) path)\n"
383 << " " << para
<< endl
;
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. */
405 cerr
<< " WinFormat - full path\n"
406 << " " << para
<< endl
;
410 // Sanity check for -X<abspath>
411 if (match2s(para
, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
413 Fatal("Not converted -X/... type switch in :" + para
);
415 // Sanity check for [-]X<something>(:|=)<abspath> case
416 if (match2s(para
, "^(-?[[:alpha:]][[:alnum:]_.]+[=:])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
418 Fatal("Not processed [-]X<something>(=|:)/... in :" + para
);
425 // Reformat para to DOSish format
426 void do_atcommand(string
¶
) {
427 string at
, filename
, token
;
429 // Workaround, iz28717, keep number of @'s.
430 match2s(para
, "^(@+)(.*)",at
,filename
);
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
443 cerr
<< "@ token :" << token
<< ":" << endl
;
444 if (!is_ignorepara(token
))
447 newtoken
.push_back(token
);
451 // Write token tokens bak to file
452 if ( debug
|| debug_light
)
453 cerr
<< "New @-file parameter:" << endl
;
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
;
467 // We want a dos file
468 atout
<< '\r' << endl
;
471 // Transform the 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
];
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
);
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;
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
);
506 Fatal("cannot create short name of " + string(convpath
) );
512 path
.assign(convpath
);
516 void replace_cyg_env( void ) {
517 // Transform certain environment variables
519 cerr
<< "Transforming some environment variables" << endl
;
521 const size_t nvars
= sizeof(transformvars
)/sizeof(string
*);
526 for (size_t i
=0; i
!= nvars
; ++i
) {
527 if ( currvar
= getenv(transformvars
[i
].c_str() ) ) {
528 // Only transform existent vars
530 cerr
<< "Transform variable: " << transformvars
[i
] << "="
532 newvar
.assign(currvar
);
534 if( setenv(transformvars
[i
].c_str(), newvar
.c_str(), 1) )
535 Fatal("setenv failed on " + transformvars
[i
] + "=" + newvar
+
536 " with error: " + strerror(errno
));
538 cerr
<< "To: " << transformvars
[i
] << "="
539 << newvar
<< "\n" << endl
;
545 void Fatal( const string text
) {
547 cerr
<< "Error: " << text
<< endl
;
553 match2s(const string argument
, const char *pattern
, string
&sub1
, string
&sub2
)
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);
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 */
582 Fatal("regexec had a problem.");
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;
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();
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.");
647 // Replace all occurences of subexpression number "subexp" with "repl"
648 void rep_subn( string
&argument
, const char *pattern
, int subexp
, const char repl
)
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.");
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
) {