6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2010, 2011, 2012, MinGW Project
10 * Initiation stub for command line invocation of mingw-get
13 * This is free software. Permission is granted to copy, modify and
14 * redistribute this software, under the provisions of the GNU General
15 * Public License, Version 3, (or, at your option, any later version),
16 * as published by the Free Software Foundation; see the file COPYING
17 * for licensing details.
19 * Note, in particular, that this software is provided "as is", in the
20 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
21 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
22 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
23 * MinGW Project, accept liability for any damages, however caused,
24 * arising from the use of this software.
27 #define _WIN32_WINNT 0x500
28 #define WIN32_LEAN_AND_MEAN
41 #define EXIT_FATAL EXIT_FAILURE + 1
43 static const char *progname;
45 wchar_t *AppPathNameW( const wchar_t *relpath )
47 /* UTF-16LE implementation; NOT thread safe...
49 * Map "relpath" into the file system hierarchy with logical root
50 * at the prefix where the application suite is installed, such that
51 * it becomes "d:\prefix\relpath".
53 * If the application's executables are installed in a directory called
54 * "bin" or "sbin", then this final directory is excluded from the mapped
55 * prefix, (i.e. a program installed as "d:\prefix\bin\prog.exe" returns
56 * the mapped path as "d:\prefix\relpath", rather than as the inclusive
57 * path "d:\prefix\bin\relpath"); in any other case, the prefix is taken
58 * as the path to the directory in which the program file is installed,
59 * (i.e. a program installed as "d:\prefix\foo\prog.exe" returns the
60 * mapped path as "d:\prefix\foo\relpath".
62 * Mapped path is returned in this static buffer...
64 static wchar_t retpath[MAX_PATH], *tail = NULL;
68 /* First time initialisation...
70 * Fill in the static local buffer, with the appropriate "prefix"
71 * string, marking the point at which "relpath" is to be appended.
73 * On subsequent calls, we reuse this static buffer, leaving the
74 * "prefix" element unchanged, but simply overwriting the "relpath"
75 * element from any prior call; this is NOT thread safe!
78 wchar_t bindir[] = L"bin", *bindir_p = bindir;
79 wchar_t *mark, *scan = mark = tail = retpath;
81 /* Ascertain the installation path of the calling executable.
83 int chk = GetModuleFileNameW( NULL, retpath, MAX_PATH );
85 /* Validate it; reject any result which doesn't fit in "retpath".
87 if( (chk == 0) || ((chk == MAX_PATH) && (retpath[--chk] != L'\0')) )
90 /* Parse it, to locate the end of the effective "prefix" string...
92 do { if( *scan == L'/' )
94 * This is a sanity check; it should not be necessary, but...
96 * Enforce use of "\" rather than "/" as path component separator;
97 * ( "LoadLibrary" may be broken, since it seems to care! )
101 if( *scan && (prev == L'\\') )
103 /* We found the start a new path name component directory,
104 * (or maybe the file name, at the end); mark it as a possible
105 * final element of the path name, leaving "tail" pointing to
106 * the previously marked element.
111 } while( (prev = *scan++) != L'\0' );
113 /* When we get to here, "mark" should point to the executable file name,
114 * at the end of the path name string, while "tail" should point to the
115 * last directory in the installation path; we now check, without regard
116 * to case, if this final directory name is "bin" or "sbin"...
118 if( (*(scan = tail) == L's') || (*scan == L'S') )
120 * Might be "sbin"; skip the initial "s", and check for "bin"...
124 while( *bindir_p && ((*scan++ | L'\x20') == *bindir_p++) )
126 * ...could still match "bin"...
128 if( *bindir_p || (*scan != L'\\') )
130 * No, it didn't match; adjust "tail", so we leave the final
131 * directory name as part of "prefix".
136 if( relpath == NULL )
138 * No "relpath" argument given; simply truncate, to return only
139 * the effective "prefix" string...
144 { wchar_t *append = tail;
146 * Append the specified path to the application's root,
147 * again, taking care to use "\" as the separator character...
149 *append++ = (*relpath == L'/') ? L'\\' : *relpath;
150 } while( *relpath++ && ((append - retpath) < MAX_PATH) );
152 if( *--append != L'\0' )
154 * Abort, if we didn't properly terminate the return string.
161 extern const char *version_identification;
163 static const char *help_text =
164 "Manage MinGW and MSYS installations (command line user interface).\n\n"
167 " mingw-get [OPTIONS] ACTION [package-spec[version-bounds] ...]\n\n"
169 " mingw-get update\n"
170 " mingw-get [OPTIONS] {install | upgrade | remove} package-spec ...\n"
171 " mingw-get [OPTIONS] {show | list} [package-spec ...]\n\n"
174 " --help, -h Show this help text\n"
176 " --version, -V Show version and licence information\n"
178 " --verbose, -v Increase verbosity of diagnostic or\n"
179 " progress reporting output; repeat up\n"
180 " to three times for maximum verbosity\n"
181 " --verbose=N Set verbosity level to N; (0 <= N <= 3)\n"
184 /* The "--trace" option is available only when dynamic tracing
185 * debugging support is compiled in; don't advertise it otherwise.
187 #if DEBUG_ENABLED( DEBUG_TRACE_DYNAMIC )
188 " --trace=N Enable tracing feature N; (debugging aid)\n"
192 /* The following are always available...
194 " --reinstall When performing an install or upgrade\n"
195 " operation, reinstall any named package\n"
196 " for which the most recent release is\n"
197 " already installed\n"
199 " --recursive Extend the scope of \"install --reinstall\"\n"
200 " or of \"upgrade\", such that the operation\n"
201 " is applied recursively to all prerequisites\n"
202 " of all packages named on the command line\n"
204 " --download-only Download the package archive files which\n"
205 " would be required to complete the specified\n"
206 " install, upgrade, or source operation, but\n"
207 " do not unpack them, or otherwise proceed\n"
208 " to complete the operation\n"
210 " --print-uris Display the repository URIs from which\n"
211 " package archive files would be retrieved\n"
212 " prior to performing the specified install,\n"
213 " upgrade, or source operation, but do not\n"
214 " download any package file, or otherwise\n"
215 " proceed with the operation\n"
217 " --all-related When performing source or licence operations,\n"
218 " causes mingw-get to retrieve, and optionally to\n"
219 " unpack the source or licence archives for all\n"
220 " runtime prerequisites of, and in addition to,\n"
221 " the nominated package\n"
223 " --desktop[=all-users]\n"
224 " Enable the creation of desktop shortcuts, for\n"
225 " packages which provide the capability via pre-\n"
226 " or post-install scripts; the optional 'all-users'\n"
227 " qualifier requests that all such shortcuts are\n"
228 " to be made available to all users; without it\n"
229 " shortcuts will be created for current user only\n"
231 " Note that specification of this option does not\n"
232 " guarantee that shortcuts will be created; the\n"
233 " onus lies with individual package maintainers\n"
234 " to provide scripting to support this capability\n"
236 " --start-menu[=all-users]\n"
237 " Enable the creation of start menu shortcuts, for\n"
238 " packages which provide the capability via pre-\n"
239 " or post-install scripts; the optional 'all-users'\n"
240 " qualifier requests that all such shortcuts are\n"
241 " to be made available to all users; without it\n"
242 " shortcuts will be created for current user only\n"
244 " Note that specification of this option does not\n"
245 " guarantee that shortcuts will be created; the\n"
246 " onus lies with individual package maintainers\n"
247 " to provide scripting to support this capability\n"
250 " update Update local copy of repository catalogues\n"
251 " list, show List and show details of available packages\n"
252 " source Download and optionally unpack package sources\n"
253 " licence Download and optionally unpack licence packages,\n"
254 " handling them as if they are source packages\n"
255 " install Install new packages\n"
256 " upgrade Upgrade previously installed packages\n"
257 " remove Remove previously installed packages\n\n"
259 "Package Specifications:\n"
260 " [subsystem-]name[-component]:\n"
261 " msys-bash-doc The 'doc' component of the bash package for MSYS\n"
262 " mingw32-gdb All components of the gdb package for MinGW\n\n"
264 "Version Bounds (for install or upgrade actions):\n"
265 " {>|>=|=|<=|<}major[.minor[.rev]][-subsystem-major[.minor[.rev]]]:\n"
266 " \"gcc=4.5.*\" Latest available release of GCC version 4.5.x\n"
267 " \"gcc<4.6\" Alternative representation for GCC version 4.5.x\n\n"
269 "Use 'mingw-get list' to identify possible package names, and the\n"
270 "components associated with each.\n\n"
272 "Quote package names with attached version bounds specifications, to\n"
273 "avoid possible misinterpretation of shell operators. Do NOT insert\n"
274 "white space at any point within any \"package-spec[version-bounds]\"\n"
275 "specification string.\n\n"
278 #define IMPLEMENT_INITIATION_RITES PHASE_ONE_RITES
281 static __inline__ __attribute__((__always_inline__))
282 char **cli_setargv( HMODULE my_dll, struct pkgopts *opts, char **argv )
284 /* A local wrapper function to facilitate passing pre-parsed
285 * command line options while performing the "climain()" call
286 * into mingw-get-0.dll
288 * Note that this requires a version of mingw-get-0.dll which
289 * provides the "cli_setopts()" hook...
291 typedef void (*dll_hook)(struct pkgopts *);
292 dll_hook cli_setopts = (dll_hook)(GetProcAddress( my_dll, "cli_setopts" ));
293 if( cli_setopts != NULL )
295 * ...which allows us to pass the pre-parsed options.
299 /* In any case, we always return the argument vector which is
300 * to be passed in the "climain()" call itself.
305 /* FIXME: We may ultimately choose to factor the following atoi() replacement
306 * into a separate, free-standing module. For now we keep it here, as a static
307 * function, but we keep the required #include adjacent.
311 static int xatoi( const char *input )
313 /* A replacement for the standard atoi() function; this implementation
314 * supports conversion of octal or hexadecimal representations, in addition
315 * to the decimal representation required by standard atoi().
317 * We begin by initialising the result accumulator to zero...
321 /* ...then, provided we have an input string to interpret...
325 /* ...we proceed with interpretation and accumulation of the result,
326 * noting that we may have to handle an initial minus sign, (but we
327 * don't know yet, so assume that not for now).
330 while( isspace( *input ) )
332 * Ignore leading white space.
338 * An initial minus sign requires negation
339 * of the accumulated result...
343 else if( *input == '+' )
345 * ...while an initial plus sign is redundant,
346 * and may simply be ignored.
352 /* An initial zero signifies either hexadecimal
353 * or octal representation...
355 if( (*++input | '\x20') == 'x' )
357 * ...with following 'x' or 'X' indicating
358 * the hexadecimal case.
360 while( isxdigit( *++input ) )
362 /* Interpret sequence of hexadecimal digits...
364 result = (result << 4) + *input;
367 * ...with ASCII to binary conversion for
368 * lower case digits 'a'..'f',...
372 else if( *input > '9' )
374 * ...ASCII to binary conversion for
375 * upper case digits 'A'..'F',...
380 /* ...or ASCII to decimal conversion,
385 else while( (*input >= '0') && (*input < '8') )
387 * Interpret sequence of octal digits...
389 result = (result << 3) + *input++ - '0';
392 else while( isdigit( *input ) )
394 * Interpret sequence of decimal digits...
396 result = (result << 1) + (result << 3) + *input++ - '0';
400 * We had an initial minus sign, so we must
401 * return the negated result...
405 /* ...otherwise, we simply return the accumulated result
410 #define atmost( lim, val ) ((lim) < (val)) ? (lim) : (val)
412 int main( int argc, char **argv )
414 /* Provide storage for interpretation of any parsed command line options.
415 * Note that we could also initialise them here, but then we would need to
416 * give attention to the number of initialisers required; to save us that
417 * concern we will defer to an initialisation loop, below.
419 struct pkgopts parsed_options;
421 /* Make a note of this program's name, and where it's installed.
424 progname = basename( *argv );
428 /* The user specified arguments on the command line...
429 * Interpret any which specify processing options for this application,
430 * (these are all specified in GNU `long only' style).
433 struct option options[] =
435 /* Option Name Argument Category Store To Return Value
436 * -------------- ------------------ -------- ------------------
438 { "version", no_argument, NULL, 'V' },
439 { "help", no_argument, NULL, 'h' },
440 { "verbose", optional_argument, NULL, OPTION_VERBOSE },
442 { "recursive", no_argument, &optref, OPTION_RECURSIVE },
443 { "reinstall", no_argument, &optref, OPTION_REINSTALL },
444 { "download-only", no_argument, &optref, OPTION_DNLOAD_ONLY },
445 { "print-uris", no_argument, &optref, OPTION_PRINT_URIS },
447 { "all-related", no_argument, &optref, OPTION_ALL_RELATED },
449 { "desktop", optional_argument, &optref, OPTION_DESKTOP },
450 { "start-menu", optional_argument, &optref, OPTION_START_MENU },
452 # if DEBUG_ENABLED( DEBUG_TRACE_DYNAMIC )
453 /* The "--trace" option is supported only when dynamic tracing
454 * debugging support has been compiled in.
456 { "trace", required_argument, &optref, OPTION_TRACE },
459 /* This list must be terminated by a null definition...
465 for( opt = OPTION_FLAGS; opt < OPTION_TABLE_SIZE; ++opt )
467 * Ensure that all entries within the options table are initialised
468 * to zero, (equivalent to NULL for pointer entries)...
470 parsed_options.flags[opt].numeric = 0;
472 while( (opt = getopt_long_only( argc, argv, "vVh", options, &offset )) != -1 )
474 * Parse any user specified options from the command line...
479 /* This is a request to display the version of the application;
480 * emit the requisite informational message, and quit.
482 printf( version_identification );
486 /* This is a request to display help text and quit.
492 /* This is a request to set an explicit verbosity level,
493 * (minimum zero, maximum three), or if no explicit argument
494 * is specified, to increment verbosity as does "-v".
498 /* This is the case where an explicit level was specified...
500 parsed_options.flags[OPTION_FLAGS].numeric =
501 (parsed_options.flags[OPTION_FLAGS].numeric & ~OPTION_VERBOSE)
502 | atmost( OPTION_VERBOSE_MAX, xatoi( optarg ));
507 /* This is a request to increment the verbosity level
508 * from its initial zero setting, to a maximum of three.
510 if( (parsed_options.flags[OPTION_FLAGS].numeric & OPTION_VERBOSE) < 3 )
511 ++parsed_options.flags[OPTION_FLAGS].numeric;
515 switch( optref & OPTION_STORAGE_CLASS )
517 /* This represents a generic option specification,
518 * allowing for storage of a option argument of the
519 * specified class into a specified option slot...
523 case OPTION_STORE_STRING:
524 /* This is a simple store of a option argument
525 * which represents a character string.
527 mark_option_as_set( parsed_options, optref );
528 parsed_options.flags[optref & 0xfff].string = optarg;
531 case OPTION_STORE_NUMBER:
532 /* This is also a simple store of the argument value,
533 * in this case interpreted as a number.
535 mark_option_as_set( parsed_options, optref );
536 parsed_options.flags[optref & 0xfff].numeric = xatoi( optarg );
539 case OPTION_MERGE_NUMBER:
540 /* In this case, we combine the value of the argument,
541 * again interpreted as a number, with the original value
542 * stored in the option slot, forming the bitwise logical
543 * .OR. of the pair of values.
545 mark_option_as_set( parsed_options, optref );
546 parsed_options.flags[optref & 0xfff].numeric |= xatoi( optarg );
550 /* This is a mask and store operation for a specified
551 * bit-field within the first pair of flags slots; in
552 * this case, the optref value itself specifies a 12-bit
553 * value, a 12-bit combining mask, and an alignment shift
554 * count between 0 and 52, in 4-bit increments.
556 if( (shift = (optref & OPTION_SHIFT_MASK) >> 22) < 53 )
558 uint64_t value = optref & 0xfff;
559 uint64_t mask = (optref & 0xfff000) >> 12;
560 *(uint64_t *)(parsed_options.flags) &= ~(mask << shift);
561 *(uint64_t *)(parsed_options.flags) |= value << shift;
567 /* User specified an invalid or unsupported option...
570 fprintf( stderr, "%s: option '-%s' not yet supported\n",
571 progname, options[offset].name
577 /* Establish the installation path for the mingw-get application...
579 if( (approot = AppPathNameW( NULL )) != NULL )
581 /* ...and set up the APPROOT environment variable to refer to
582 * the associated installation prefix...
584 char approot_setup[1 + snprintf( NULL, 0, "APPROOT=%S", approot )];
585 snprintf( approot_setup, sizeof( approot_setup ), "APPROOT=%S", approot );
586 putenv( approot_setup );
591 /* The user specified arguments on the command line...
592 * we load the supporting DLL into the current process context,
593 * then, remaining in command line mode, we jump to its main
594 * command line processing routine...
597 char *argv_base = *argv;
598 typedef int (*dll_entry)( int, char ** );
599 HMODULE my_dll = LoadLibraryW( AppPathNameW( MINGW_GET_DLL ) );
600 dll_entry climain = (dll_entry)(GetProcAddress( my_dll, "climain" ));
601 if( climain == NULL )
603 /* ...bailing out, on failure to load the DLL.
605 fprintf( stderr, "%s: %S: shared library load failed\n",
606 progname, MINGW_GET_DLL
611 /* Adjust argc and argv to discount parsed options...
616 * ...while preserving the original argv[0] reference within
617 * the first remaining argument to be passed to climain().
621 /* We want only one mingw-get process accessing the XML database
622 * at any time; attempt to acquire an exclusive access lock...
624 if( (lock = pkgInitRites( progname )) >= 0 )
626 /* ...and proceed, only if successful.
627 * A non-zero return value indicates that a fatal error occurred.
629 int rc = climain( argc, cli_setargv( my_dll, &parsed_options, argv ) );
631 /* We must release the mingw-get DLL code, BEFORE we invoke
632 * last rites processing, (otherwise the last rites clean-up
633 * handler exhibits abnormal behaviour when it is exec'd).
635 FreeLibrary( my_dll );
637 return pkgLastRites( lock, progname );
640 (void) pkgLastRites( lock, progname );
644 /* If we get to here, then we failed to acquire a lock;
651 { /* No arguments were specified on the command line...
652 * we interpret this as a request to start up in GUI mode...
654 wchar_t *libexec_path = AppPathNameW( MINGW_GET_GUI );
655 char gui_program[1 + snprintf( NULL, 0, "%S", libexec_path )];
656 snprintf( gui_program, sizeof( gui_program ), "%S", libexec_path );
657 int status = execv( gui_program, (const char* const*)(argv) );
659 /* If we get to here, then the GUI could not be started...
660 * Issue a diagnostic message, before abnormal termination.
663 "%s: %S: unable to start GUI; helper program not installed\n",
664 progname, MINGW_GET_GUI
670 /* $RCSfile$: end of file */