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 ...]\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 "Use 'mingw-get list' to identify possible package names\n"
265 "and the components associated with each.\n\n";
267 #define IMPLEMENT_INITIATION_RITES PHASE_ONE_RITES
270 static __inline__ __attribute__((__always_inline__))
271 char **cli_setargv( HMODULE my_dll, struct pkgopts *opts, char **argv )
273 /* A local wrapper function to facilitate passing pre-parsed
274 * command line options while performing the "climain()" call
275 * into mingw-get-0.dll
277 * Note that this requires a version of mingw-get-0.dll which
278 * provides the "cli_setopts()" hook...
280 typedef void (*dll_hook)(struct pkgopts *);
281 dll_hook cli_setopts = (dll_hook)(GetProcAddress( my_dll, "cli_setopts" ));
282 if( cli_setopts != NULL )
284 * ...which allows us to pass the pre-parsed options.
288 /* In any case, we always return the argument vector which is
289 * to be passed in the "climain()" call itself.
294 /* FIXME: We may ultimately choose to factor the following atoi() replacement
295 * into a separate, free-standing module. For now we keep it here, as a static
296 * function, but we keep the required #include adjacent.
300 static int xatoi( const char *input )
302 /* A replacement for the standard atoi() function; this implementation
303 * supports conversion of octal or hexadecimal representations, in addition
304 * to the decimal representation required by standard atoi().
306 * We begin by initialising the result accumulator to zero...
310 /* ...then, provided we have an input string to interpret...
314 /* ...we proceed with interpretation and accumulation of the result,
315 * noting that we may have to handle an initial minus sign, (but we
316 * don't know yet, so assume that not for now).
319 while( isspace( *input ) )
321 * Ignore leading white space.
327 * An initial minus sign requires negation
328 * of the accumulated result...
332 else if( *input == '+' )
334 * ...while an initial plus sign is redundant,
335 * and may simply be ignored.
341 /* An initial zero signifies either hexadecimal
342 * or octal representation...
344 if( (*++input | '\x20') == 'x' )
346 * ...with following 'x' or 'X' indicating
347 * the hexadecimal case.
349 while( isxdigit( *++input ) )
351 /* Interpret sequence of hexadecimal digits...
353 result = (result << 4) + *input;
356 * ...with ASCII to binary conversion for
357 * lower case digits 'a'..'f',...
361 else if( *input > '9' )
363 * ...ASCII to binary conversion for
364 * upper case digits 'A'..'F',...
369 /* ...or ASCII to decimal conversion,
374 else while( (*input >= '0') && (*input < '8') )
376 * Interpret sequence of octal digits...
378 result = (result << 3) + *input++ - '0';
381 else while( isdigit( *input ) )
383 * Interpret sequence of decimal digits...
385 result = (result << 1) + (result << 3) + *input++ - '0';
389 * We had an initial minus sign, so we must
390 * return the negated result...
394 /* ...otherwise, we simply return the accumulated result
399 #define atmost( lim, val ) ((lim) < (val)) ? (lim) : (val)
401 int main( int argc, char **argv )
403 /* Provide storage for interpretation of any parsed command line options.
404 * Note that we could also initialise them here, but then we would need to
405 * give attention to the number of initialisers required; to save us that
406 * concern we will defer to an initialisation loop, below.
408 struct pkgopts parsed_options;
410 /* Make a note of this program's name, and where it's installed.
413 progname = basename( *argv );
417 /* The user specified arguments on the command line...
418 * Interpret any which specify processing options for this application,
419 * (these are all specified in GNU `long only' style).
422 struct option options[] =
424 /* Option Name Argument Category Store To Return Value
425 * -------------- ------------------ -------- ------------------
427 { "version", no_argument, NULL, 'V' },
428 { "help", no_argument, NULL, 'h' },
429 { "verbose", optional_argument, NULL, OPTION_VERBOSE },
431 { "recursive", no_argument, &optref, OPTION_RECURSIVE },
432 { "reinstall", no_argument, &optref, OPTION_REINSTALL },
433 { "download-only", no_argument, &optref, OPTION_DNLOAD_ONLY },
434 { "print-uris", no_argument, &optref, OPTION_PRINT_URIS },
436 { "all-related", no_argument, &optref, OPTION_ALL_RELATED },
438 { "desktop", optional_argument, &optref, OPTION_DESKTOP },
439 { "start-menu", optional_argument, &optref, OPTION_START_MENU },
441 # if DEBUG_ENABLED( DEBUG_TRACE_DYNAMIC )
442 /* The "--trace" option is supported only when dynamic tracing
443 * debugging support has been compiled in.
445 { "trace", required_argument, &optref, OPTION_TRACE },
448 /* This list must be terminated by a null definition...
454 for( opt = OPTION_FLAGS; opt < OPTION_TABLE_SIZE; ++opt )
456 * Ensure that all entries within the options table are initialised
457 * to zero, (equivalent to NULL for pointer entries)...
459 parsed_options.flags[opt].numeric = 0;
461 while( (opt = getopt_long_only( argc, argv, "vVh", options, &offset )) != -1 )
463 * Parse any user specified options from the command line...
468 /* This is a request to display the version of the application;
469 * emit the requisite informational message, and quit.
471 printf( version_identification );
475 /* This is a request to display help text and quit.
481 /* This is a request to set an explicit verbosity level,
482 * (minimum zero, maximum three), or if no explicit argument
483 * is specified, to increment verbosity as does "-v".
487 /* This is the case where an explicit level was specified...
489 parsed_options.flags[OPTION_FLAGS].numeric =
490 (parsed_options.flags[OPTION_FLAGS].numeric & ~OPTION_VERBOSE)
491 | atmost( OPTION_VERBOSE_MAX, xatoi( optarg ));
496 /* This is a request to increment the verbosity level
497 * from its initial zero setting, to a maximum of three.
499 if( (parsed_options.flags[OPTION_FLAGS].numeric & OPTION_VERBOSE) < 3 )
500 ++parsed_options.flags[OPTION_FLAGS].numeric;
504 switch( optref & OPTION_STORAGE_CLASS )
506 /* This represents a generic option specification,
507 * allowing for storage of a option argument of the
508 * specified class into a specified option slot...
512 case OPTION_STORE_STRING:
513 /* This is a simple store of a option argument
514 * which represents a character string.
516 mark_option_as_set( parsed_options, optref );
517 parsed_options.flags[optref & 0xfff].string = optarg;
520 case OPTION_STORE_NUMBER:
521 /* This is also a simple store of the argument value,
522 * in this case interpreted as a number.
524 mark_option_as_set( parsed_options, optref );
525 parsed_options.flags[optref & 0xfff].numeric = xatoi( optarg );
528 case OPTION_MERGE_NUMBER:
529 /* In this case, we combine the value of the argument,
530 * again interpreted as a number, with the original value
531 * stored in the option slot, forming the bitwise logical
532 * .OR. of the pair of values.
534 mark_option_as_set( parsed_options, optref );
535 parsed_options.flags[optref & 0xfff].numeric |= xatoi( optarg );
539 /* This is a mask and store operation for a specified
540 * bit-field within the first pair of flags slots; in
541 * this case, the optref value itself specifies a 12-bit
542 * value, a 12-bit combining mask, and an alignment shift
543 * count between 0 and 52, in 4-bit increments.
545 if( (shift = (optref & OPTION_SHIFT_MASK) >> 22) < 53 )
547 uint64_t value = optref & 0xfff;
548 uint64_t mask = (optref & 0xfff000) >> 12;
549 *(uint64_t *)(parsed_options.flags) &= ~(mask << shift);
550 *(uint64_t *)(parsed_options.flags) |= value << shift;
556 /* User specified an invalid or unsupported option...
559 fprintf( stderr, "%s: option '-%s' not yet supported\n",
560 progname, options[offset].name
566 /* Establish the installation path for the mingw-get application...
568 if( (approot = AppPathNameW( NULL )) != NULL )
570 /* ...and set up the APPROOT environment variable to refer to
571 * the associated installation prefix...
573 char approot_setup[1 + snprintf( NULL, 0, "APPROOT=%S", approot )];
574 snprintf( approot_setup, sizeof( approot_setup ), "APPROOT=%S", approot );
575 putenv( approot_setup );
580 /* The user specified arguments on the command line...
581 * we load the supporting DLL into the current process context,
582 * then, remaining in command line mode, we jump to its main
583 * command line processing routine...
586 char *argv_base = *argv;
587 typedef int (*dll_entry)( int, char ** );
588 HMODULE my_dll = LoadLibraryW( AppPathNameW( MINGW_GET_DLL ) );
589 dll_entry climain = (dll_entry)(GetProcAddress( my_dll, "climain" ));
590 if( climain == NULL )
592 /* ...bailing out, on failure to load the DLL.
594 fprintf( stderr, "%s: %S: shared library load failed\n",
595 progname, MINGW_GET_DLL
600 /* Adjust argc and argv to discount parsed options...
605 * ...while preserving the original argv[0] reference within
606 * the first remaining argument to be passed to climain().
610 /* We want only one mingw-get process accessing the XML database
611 * at any time; attempt to acquire an exclusive access lock...
613 if( (lock = pkgInitRites( progname )) >= 0 )
615 /* ...and proceed, only if successful.
616 * A non-zero return value indicates that a fatal error occurred.
618 int rc = climain( argc, cli_setargv( my_dll, &parsed_options, argv ) );
620 /* We must release the mingw-get DLL code, BEFORE we invoke
621 * last rites processing, (otherwise the last rites clean-up
622 * handler exhibits abnormal behaviour when it is exec'd).
624 FreeLibrary( my_dll );
626 return pkgLastRites( lock, progname );
629 (void) pkgLastRites( lock, progname );
633 /* If we get to here, then we failed to acquire a lock;
640 { /* No arguments were specified on the command line...
641 * we interpret this as a request to start up in GUI mode...
643 wchar_t *libexec_path = AppPathNameW( MINGW_GET_GUI );
644 char gui_program[1 + snprintf( NULL, 0, "%S", libexec_path )];
645 snprintf( gui_program, sizeof( gui_program ), "%S", libexec_path );
646 int status = execv( gui_program, (const char* const*)(argv) );
648 /* If we get to here, then the GUI could not be started...
649 * Issue a diagnostic message, before abnormal termination.
652 "%s: %S: unable to start GUI; helper program not installed\n",
653 progname, MINGW_GET_GUI
659 /* $RCSfile$: end of file */