1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
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.
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).
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.
26 ************************************************************************/
28 // guw - A wrapper program to execute windows programs with parameters that
29 // contain cygwin (POSIX) style pathnames.
31 // Todo: Add a -? switch to guw to issue a help page.
44 #include <sys/cygwin.h>
57 void init_ignorepara(string command
);
58 bool is_ignorepara(const string
¶
);
59 int winFormat(string
¶
);
60 void do_atcommand(string
¶
);
61 void myCygpath(string
&path
, int shortname
= 1 );
62 void replace_cyg_env( void );
63 void Fatal( const string text
);
65 int match2s(const string argument
, const char *pattern
, string
&sub1
, string
&sub2
);
66 void rep_subn_cyg(string
&argument
);
67 void rep_subn( string
&argument
, const char *pattern
, int subexp
, const char repl
);
68 void rep_char( string
&argument
, const char from
, const char to
);
71 bool debug_light
= false;
73 // The commands are treated case insensitive, the parameters
74 // are case sensitive.
75 const string ignorepara
[] = { "echo /TEST",
77 "climaker StarOffice/OpenOffice",
79 "g++ -DLOCAL_RULE_LANGS -DUPD -DMINOR"
80 " -DBUILD_ID -DSC_INFO_OSVERSION",
81 "gcc -DUDATA_SO_SUFFIX -DSTATIC_O"
82 " -DPACKAGE -DU_MAKE",
83 "lib /OUT: -out: -def: -machine:",
84 "link /BASE: /COMMENT: /DEBUG: /DLL /ENTRY:"
85 " /MACHINE: /MAP /NODEFAULTLIB /OPT: /RELEASE"
86 " /STACK: /SUBSYSTEM: -NODEFAULTLIB:"
87 " -def: delayload: -implib: -map: -out:",
89 "regcomp -env: vnd.sun.star.expand:"
90 " vnd.openoffice.pymodule: file:",
94 vector
<string
> ignorepara_vec
;
96 // environment variables that are "winformatted" when -env is given
97 const string transformvars
[] = { "SOLAR_VERSION",
106 int main(int argc
, char **argv
) {
108 // initialize arglist with arguments
109 list
<string
> arglist(argv
, argv
+ argc
);
111 // Drop the first (filename) argument
114 // iterarot over cmdline elements
115 list
<string
>::iterator ele
= arglist
.begin();
117 // Allowed switch values
118 bool conv_cyg_arg
= false;
120 // Look for switches to guw
124 while ( !arglist
.empty()
125 && ele
!=arglist
.end()
126 && (ele
->find("-", 0) == 0) ) {
127 if (ele
->find("-env", 0) == 0) {
129 Fatal("-env used twice!");
132 ele
= arglist
.erase(ele
);
135 else if (ele
->find("-dbg", 0) == 0) {
138 ele
= arglist
.erase(ele
);
141 else if (ele
->find("-ldbg", 0) == 0) {
144 ele
= arglist
.erase(ele
);
148 // Ignore this switch
153 // The next entry must be the program
155 if ( !arglist
.empty() ) {
156 command
.assign(*arglist
.begin());
160 Fatal("guw needs at least one parameter.");
163 cerr
<< "Command: " << command
<< "\n" << endl
;
164 // Initialize parameter exception list (for this command)
165 init_ignorepara(command
);
168 // Do something if -env was found
173 // loop and and DOSify the parameters
175 cerr
<< "Transform the parameter\n" << endl
;
178 while ( ele
!= arglist
.end() ) {
180 if ((*ele
)[0] == '@')
182 else if (!is_ignorepara(*ele
)) {
184 cerr
<< "----------------" << endl
;
185 cerr
<< "Process parameter: " << *ele
<< endl
;
189 cerr
<< "Transformed to: " << *ele
<< "\n" << endl
;
195 // create the argv[] for execvp(argv[0], argv);
198 // const char *nargv[arglist.size()+2]; // or ..
199 char *nargv
[arglist
.size()+2];
201 // nargv[0] = command.c_str(); // or ..
202 nargv
[0] = new char[command
.length()+1];
203 // strcpy(nargv[0], command.c_str());
204 command
.copy(nargv
[0], command
.length());
205 nargv
[0][command
.length()] = 0;
208 cerr
<< "----------------\n" << endl
;
209 if ( debug
|| debug_light
)
210 cerr
<< "Execute: " << nargv
[0];
213 while ( ele
!= arglist
.end() ) {
214 // nargv[count] = ele->c_str(); // or ..
215 sLen
= ele
->length();
216 nargv
[count
] = new char[sLen
+1];
217 // strcpy(nargv[count], ele->c_str());
218 ele
->copy(nargv
[count
], sLen
);
219 nargv
[count
][sLen
] = 0;
221 if ( debug
|| debug_light
)
222 cerr
<< " " << nargv
[count
];
227 // last nargv[] must be NULL
229 if ( debug
|| debug_light
)
232 // Unfortunately the prototype of execvp does not like const char*,
233 // actually not const char* nargv[] coming from .c_str(). So either
234 // we copy everything into newly allocated variables or we force it
235 // with a cast. const_cast<char * const *>()
236 // execvp(nargv[0], const_cast<char * const *>(nargv) );
237 if ( execvp(nargv
[0], nargv
) < 0 ) {
238 perror("Execvp error. Aborting.");
242 // Omit the deleting of the dynamically allocated nargv[] elements
243 // here as this part will never be reached.
248 // Initialize exception list from global var ignorepara[]
249 void init_ignorepara(string fullcommand
) {
250 const size_t kplen
= sizeof(ignorepara
)/sizeof(string
*);
251 string shortcommand
, cmd
, para
, sub2
;
253 // First lowercase everything
254 for(size_t i
=0;i
<fullcommand
.length();i
++)
255 fullcommand
[i
] = tolower(fullcommand
[i
]);
257 // Remove a potential .exe
258 size_t slen
= fullcommand
.length();
260 // for slen == 3 this would yield string::npos otherwise
261 if ( slen
> 4 && fullcommand
.rfind(".exe") == slen
- 4 )
262 fullcommand
.erase(slen
-4);
264 // get the program name - Only one subexpression
265 if (!match2s(fullcommand
, "([[:alnum:]_~. +-]+)$",
266 shortcommand
, sub2
)) {
267 Fatal("No basename found in: " + fullcommand
);
270 for (size_t i
=0; i
!= kplen
; ++i
) {
271 std::istringstream
line(ignorepara
[i
]);
273 if (shortcommand
== cmd
)
274 while (line
>> para
) {
275 ignorepara_vec
.push_back(para
);
281 // Check if command/parameter is in exception list.
282 bool is_ignorepara(const string
¶
) {
284 for( vector
<string
>::iterator it
= ignorepara_vec
.begin();
285 it
!= ignorepara_vec
.end(); ++it
) {
286 if ( para
.find(*it
) != string::npos
) {
288 cerr
<< "Found execption para: " << para
<< endl
;
297 // Reformat para to DOSish format
298 int winFormat(string
¶
) {
301 // Instead of ([/[:alnum:]_~. +-]+) use ((/?[[:alnum:]_~. +-]+)+)
303 // find [-][-]X<something>=<path>, sometimes with quotes or "/" at the end
304 if (match2s(para
, "^(-?-?[[:alpha:]][[:alnum:]_.-]*=)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
308 para
.assign(su1
+ su2
);
310 cerr
<< " WinFormat - ([-][-]<something>=<path>)\n"
311 << " " << para
<< endl
;
314 // find -X<something>:<path>, sometimes with quotes or "/" at the end
315 else if (match2s(para
, "^(-[[:alpha:]][[:alnum:]_.]*:)[\'\"]?((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
319 para
.assign(su1
+ su2
);
321 cerr
<< " WinFormat - (-<something>:<path>)\n"
322 << " " << para
<< endl
;
325 // find -X<something>:<NO-path>, and prevents translating of these.
326 else if (match2s(para
, "^(-[[:alpha:]][[:alnum:]_]*:)(.*)$",
330 // para.assign(su1 + su2);
332 cerr
<< " WinFormat - (-<something>:<NO-path>)\n"
333 << " " << para
<< endl
;
336 // See iz35982 for the reason for the special treatment of this switch.
337 // This regex evaluates <something>:///<path>, sometimes with
338 // quotes or "/" at the end
339 else if (match2s(para
, "^([[:alpha:]][[:alnum:]_]*:)[\'\"]?///((/?[[:alnum:]_~. +-]+)+)[\'\"]?$",
343 para
.assign(su1
+ "///" + su2
);
345 rep_char( para
, '\\', '/');
348 cerr
<< " WinFormat - (<something>:///<path>)\n"
349 << " " << para
<< endl
;
352 // find -X<absolute path>, sometimes with quotes or "/" at the end
353 else if (match2s(para
, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)[\'\"]?$",
357 para
.assign(su1
+ su2
);
359 cerr
<< " WinFormat - (-X<absolute path>)\n"
360 << " " << para
<< endl
;
363 // find -FX<path> (MSVC switches for output naming), sometimes with quotes
365 else if (match2s(para
, "^(-F[ARdemopr])[\'\"]?(/[/[:alnum:]_~. +-]+)[\'\"]?$",
369 para
.assign(su1
+ su2
);
371 cerr
<< " WinFormat - (compiler naming (-FX<absolute path>) path)\n"
372 << " " << para
<< endl
;
376 // No parameter found, assume a path
378 // replace the colon in drives with 0x1F"
379 // (Unused ascii US - unit separator)
380 rep_subn( para
, "(^|[;,])[[:alpha:]](:)", 2, 0x1F);
382 // Replace remaining : to ;
383 rep_char( para
, ':', ';');
385 // Replace back US to ':';
386 rep_char( para
, 0x1F, ':');
388 /* Search for posix path ;entry; (The regex accepts valid paths with at
389 * least one /) and replace with DOS path, accept quotes.
390 * since iz28717 we also accept ',' as path seperator. */
394 cerr
<< " WinFormat - full path\n"
395 << " " << para
<< endl
;
399 // Sanity check for -X<abspath>
400 if (match2s(para
, "^(-[[:alpha:]])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
402 Fatal("Not converted -X/... type switch in :" + para
);
404 // Sanity check for [-]X<something>(:|=)<abspath> case
405 if (match2s(para
, "^(-?[[:alpha:]][[:alnum:]_.]+[=:])[\'\"]?((/[[:alnum:]_~. +-]+)+)",
407 Fatal("Not processed [-]X<something>(=|:)/... in :" + para
);
414 // Reformat para to DOSish format
415 void do_atcommand(string
¶
) {
416 string at
, filename
, token
;
418 // Workaround, iz28717, keep number of @'s.
419 match2s(para
, "^(@+)(.*)",at
,filename
);
421 cerr
<< "----------------" << endl
;
422 cerr
<< "Process @-file" << endl
;
423 cerr
<< " :" << at
<< ": before filename :" << filename
<< ":" << endl
;
426 // Read at file into memory
427 std::ifstream
atin(filename
.c_str());
428 list
<string
> newtoken
;
429 while (atin
>> token
) {
430 // Read / transform tokens
432 cerr
<< "@ token :" << token
<< ":" << endl
;
433 if (!is_ignorepara(token
))
436 newtoken
.push_back(token
);
440 // Write token tokens bak to file
441 if ( debug
|| debug_light
)
442 cerr
<< "New @-file parameter:" << endl
;
445 // filename += ".bak";
447 std::ofstream
atout(filename
.c_str());
448 list
<string
>::iterator tok
= newtoken
.begin();
449 while ( tok
!= newtoken
.end() ) {
450 if ( debug
|| debug_light
)
451 cerr
<< ( tok
!= newtoken
.begin() ? " " : "" ) << *tok
;
453 atout
<< ( tok
!= newtoken
.begin() ? " " : "" ) << *tok
;
456 // We want a dos file
457 atout
<< '\r' << endl
;
460 // Transform the filename
462 para
= at
+ filename
;
463 if ( debug
|| debug_light
) {
464 cerr
<< "\nNew @-file name: " << para
<< "\n" << endl
;
468 void myCygpath(string
&path
, int shortname
/* =1 */ )
470 static char convpath
[MAX_PATH
];
471 static char buf
[MAX_PATH
];
474 // Only use cygwin_conv_to_win32_path() on absolute paths as it errors
475 // out if its path doen't exist. Unfortunatelt there are a lot of not
476 // existing relative pathes used as parameters during an OOo build.
477 if( path
.find("/", 0) == 0) {
478 err
= cygwin_conv_to_win32_path( path
.c_str(), convpath
);
481 rep_char( path
, '/', '\\');
482 // see below, we copy convpath back to path, that's stupid
483 path
.copy( convpath
, path
.length());
484 convpath
[path
.length()] = 0;
489 Fatal("converting: " + path
+ " - " + strerror(errno
) );
491 // Only convert to short dos names when space is present
492 if (shortname
&& (path
.find(" ", 0) != string::npos
) ) {
493 DWORD len
= GetShortPathName (convpath
, buf
, MAX_PATH
);
495 Fatal("cannot create short name of " + string(convpath
) );
501 path
.assign(convpath
);
505 void replace_cyg_env( void ) {
506 // Transform certain environment variables
508 cerr
<< "Transforming some environment variables" << endl
;
510 const size_t nvars
= sizeof(transformvars
)/sizeof(string
*);
515 for (size_t i
=0; i
!= nvars
; ++i
) {
516 if ( currvar
= getenv(transformvars
[i
].c_str() ) ) {
517 // Only transform existent vars
519 cerr
<< "Transform variable: " << transformvars
[i
] << "="
521 newvar
.assign(currvar
);
523 if( setenv(transformvars
[i
].c_str(), newvar
.c_str(), 1) )
524 Fatal("setenv failed on " + transformvars
[i
] + "=" + newvar
+
525 " with error: " + strerror(errno
));
527 cerr
<< "To: " << transformvars
[i
] << "="
528 << newvar
<< "\n" << endl
;
534 void Fatal( const string text
) {
536 cerr
<< "Error: " << text
<< endl
;
542 match2s(const string argument
, const char *pattern
, string
&sub1
, string
&sub2
)
547 const int maxsub
= 3; // Only 3 needed, 4 is for debug
548 regmatch_t match
[maxsub
];
550 if (regcomp(&re
, pattern
, REG_EXTENDED
) != 0) {
551 Fatal("regcomp had a problem."); /* report error */
553 status
= regexec(&re
, argument
.c_str(), maxsub
, match
, 0);
556 if (status
== REG_NOMATCH
) {
557 return(0); /* no match */
558 } else if (status
== 0) {
559 string
tstr(argument
.c_str() + match
[0].rm_so
,
560 match
[0].rm_eo
- match
[0].rm_so
);
561 // cout << "Match: " << tstr << endl;
563 sub1
.assign(argument
.c_str() + match
[1].rm_so
, match
[1].rm_eo
- match
[1].rm_so
);
564 // cout << "Match1: " << sub1 << endl;
566 sub2
.assign(argument
.c_str() + match
[2].rm_so
, match
[2].rm_eo
- match
[2].rm_so
);
567 // cout << "Match2: " << sub2 << endl;
569 return(1); /* match found */
571 Fatal("regexec had a problem.");
579 // Replace path entry with myCygpath() version
580 void rep_subn_cyg(string
&argument
)
582 // accept ["']<entry>["']:["']<entry>["']:... to make the
583 // $(WRAPCMD) echo 1 ICON $(EMQ)"$(APP1ICON)$(EMQ)"
584 // work in ?tg_app.mk.
585 // FIXME: Better would be to use a DOSified $(APP1ICON) there and remove
586 // the special " treatment here.
587 const char *pattern
= "(^|[;,])[\'\"]?([[:alnum:]_~. +-]*(/[[:alnum:]_~. +-]+)+/?)[\'\"]?([;,]|$)";
588 const int subexp
= 2;
594 string::size_type oLen
, nLen
;
596 const int maxsub
= subexp
+1; // One more than the maximal subexpression
597 regmatch_t match
[maxsub
];
599 if (regcomp(&re
, pattern
, REG_EXTENDED
) != 0) {
600 Fatal("regcomp had a problem."); /* report error */
602 status
= regexec (&re
, argument
.c_str() + pos
, maxsub
, match
, 0);
603 while (status
== 0) { /* While matches found. */
604 // Classical assert()
605 if (match
[subexp
].rm_eo
== -1) {
606 Fatal("Nonexisting subexpression specified!");
609 oLen
= match
[subexp
].rm_eo
- match
[subexp
].rm_so
;
610 repstr
.assign(argument
.c_str() + pos
+ match
[subexp
].rm_so
, oLen
);
612 // Do not replace with shortpaths
613 myCygpath(repstr
, 0);
614 nLen
= repstr
.length();
617 argument
.replace( pos
+ match
[subexp
].rm_so
, oLen
, repstr
);
619 /* Substring found between match[0].rm_so and match[0].rm_eo. */
620 /* This call to regexec() finds the next match. */
622 pos
+= match
[0].rm_eo
+ nLen
- oLen
;
624 // Either the last match did end in ';' or we are at the end of para.
625 // REG_NOTBOL is not used because we skip over the ';' by using pos.
626 status
= regexec (&re
, argument
.c_str() + pos
, maxsub
, match
, 0);
629 if (status
!= REG_NOMATCH
) {
630 Fatal("regexec had a problem.");
636 // Replace all occurrences of subexpression number "subexp" with "repl"
637 void rep_subn( string
&argument
, const char *pattern
, int subexp
, const char repl
)
642 const int maxsub
= subexp
+1; // One more than the maximal subexpression
643 regmatch_t match
[maxsub
];
645 if (regcomp(&re
, pattern
, REG_EXTENDED
) != 0) {
646 Fatal("regcomp had a problem."); /* report error */
648 status
= regexec (&re
, argument
.c_str() + pos
, maxsub
, match
, 0);
649 while (status
== 0) { /* While matches found. */
650 // Classical assert()
651 if (match
[subexp
].rm_eo
== -1) {
652 Fatal("Nonexisting subexpression specified!");
655 argument
[pos
+ match
[subexp
].rm_so
] = repl
;
657 /* Substring found between match[0].rm_so and match[0].rm_eo. */
658 /* This call to regexec() finds the next match. */
659 pos
+= match
[0].rm_eo
;
660 status
= regexec (&re
, argument
.c_str() + pos
, maxsub
, match
, REG_NOTBOL
);
663 if (status
!= REG_NOMATCH
) {
664 Fatal("regexec had a problem.");
670 // Replace all char1 with char2
671 void rep_char( string
&argument
, const char from
, const char to
)
673 string::size_type loc
= 0;
675 while ( (loc
= argument
.find( from
, loc
)) != string::npos
) {