OSDN Git Service

Simplify numeric argument interpreter function.
[mingw/mingw-get.git] / src / clistub.c
1 /*
2  * clistub.c
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2010, 2011, 2012, MinGW.org Project
8  *
9  *
10  * Initiation stub for command line invocation of mingw-get
11  *
12  *
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.
18  *
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.
25  *
26  */
27 #define _WIN32_WINNT 0x500
28 #define WIN32_LEAN_AND_MEAN
29
30 #include "debug.h"
31 #include "pkgopts.h"
32 #include "approot.h"
33
34 #include <windows.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <libgen.h>
38 #include <process.h>
39 #include <getopt.h>
40
41 #define EXIT_FATAL  EXIT_FAILURE + 1
42
43 static const char *progname;
44 extern const char *version_identification;
45
46 static const char *help_text =
47 "Manage MinGW and MSYS installations (command line user interface).\n\n"
48
49 "Usage:\n"
50 "  mingw-get [OPTIONS] ACTION [package-spec[version-bounds] ...]\n\n"
51
52 "  mingw-get update\n"
53 "  mingw-get [OPTIONS] {install | upgrade | remove} package-spec ...\n"
54 "  mingw-get [OPTIONS] {show | list} [package-spec ...]\n\n"
55
56 "Options:\n"
57 "  --help, -h        Show this help text\n"
58 "\n"
59 "  --version, -V     Show version and licence information\n"
60 "\n"
61 "  --verbose, -v     Increase verbosity of diagnostic or\n"
62 "                    progress reporting output; repeat up\n"
63 "                    to three times for maximum verbosity\n"
64 "  --verbose=N       Set verbosity level to N; (0 <= N <= 3)\n"
65 "\n"
66
67 /* The "--trace" option is available only when dynamic tracing
68  * debugging support is compiled in; don't advertise it otherwise.
69  */
70 #if DEBUG_ENABLED( DEBUG_TRACE_DYNAMIC )
71 "  --trace=N         Enable tracing feature N; (debugging aid)\n"
72 "\n"
73 #endif
74
75 /* The following are always available...
76  */
77 "  --reinstall       When performing an install or upgrade\n"
78 "                    operation, reinstall any named package\n"
79 "                    for which the most recent release is\n"
80 "                    already installed\n"
81 "\n"
82 "  --recursive       Extend the scope of \"install --reinstall\"\n"
83 "                    or of \"upgrade\", such that the operation\n"
84 "                    is applied recursively to all prerequisites\n"
85 "                    of all packages named on the command line\n"
86 "\n"
87 "  --download-only   Download the package archive files which\n"
88 "                    would be required to complete the specified\n"
89 "                    install, upgrade, or source operation, but\n"
90 "                    do not unpack them, or otherwise proceed\n"
91 "                    to complete the operation\n"
92 "\n"
93 "  --print-uris      Display the repository URIs from which\n"
94 "                    package archive files would be retrieved\n"
95 "                    prior to performing the specified install,\n"
96 "                    upgrade, or source operation, but do not\n"
97 "                    download any package file, or otherwise\n"
98 "                    proceed with the operation\n"
99 "\n"
100 "  --all-related     When performing source or licence operations,\n"
101 "                    causes mingw-get to retrieve, and optionally to\n"
102 "                    unpack the source or licence archives for all\n"
103 "                    runtime prerequisites of, and in addition to,\n"
104 "                    the nominated package\n"
105 "\n"
106 "  --desktop[=all-users]\n"
107 "                    Enable the creation of desktop shortcuts, for\n"
108 "                    packages which provide the capability via pre-\n"
109 "                    or post-install scripts; the optional 'all-users'\n"
110 "                    qualifier requests that all such shortcuts are\n"
111 "                    to be made available to all users; without it\n"
112 "                    shortcuts will be created for current user only\n"
113 "\n"
114 "                    Note that specification of this option does not\n"
115 "                    guarantee that shortcuts will be created; the\n"
116 "                    onus lies with individual package maintainers\n"
117 "                    to provide scripting to support this capability\n"
118 "\n"
119 "  --start-menu[=all-users]\n"
120 "                    Enable the creation of start menu shortcuts, for\n"
121 "                    packages which provide the capability via pre-\n"
122 "                    or post-install scripts; the optional 'all-users'\n"
123 "                    qualifier requests that all such shortcuts are\n"
124 "                    to be made available to all users; without it\n"
125 "                    shortcuts will be created for current user only\n"
126 "\n"
127 "                    Note that specification of this option does not\n"
128 "                    guarantee that shortcuts will be created; the\n"
129 "                    onus lies with individual package maintainers\n"
130 "                    to provide scripting to support this capability\n"
131 "\n"
132 "Actions:\n"
133 "  update            Update local copy of repository catalogues\n"
134 "  list, show        List and show details of available packages\n"
135 "  source            Download and optionally unpack package sources\n"
136 "  licence           Download and optionally unpack licence packages,\n"
137 "                    handling them as if they are source packages\n"
138 "  install           Install new packages\n"
139 "  upgrade           Upgrade previously installed packages\n"
140 "  remove            Remove previously installed packages\n\n"
141
142 "Package Specifications:\n"
143 "  [subsystem-]name[-component]:\n"
144 "  msys-bash-doc     The 'doc' component of the bash package for MSYS\n"
145 "  mingw32-gdb       All components of the gdb package for MinGW\n\n"
146
147 "Version Bounds (for install or upgrade actions):\n"
148 "  {>|>=|=|<=|<}major[.minor[.rev]][-subsystem-major[.minor[.rev]]]:\n"
149 "  \"gcc=4.5.*\"       Latest available release of GCC version 4.5.x\n"
150 "  \"gcc<4.6\"         Alternative representation for GCC version 4.5.x\n\n"
151
152 "Use 'mingw-get list' to identify possible package names, and the\n"
153 "components associated with each.\n\n"
154
155 "Quote package names with attached version bounds specifications, to\n"
156 "avoid possible misinterpretation of shell operators.  Do NOT insert\n"
157 "white space at any point within any \"package-spec[version-bounds]\"\n"
158 "specification string.\n\n"
159 ;
160
161 #define  IMPLEMENT_INITIATION_RITES     PHASE_ONE_RITES
162 #include "rites.c"
163
164 static __inline__ __attribute__((__always_inline__))
165 char **cli_setargv( HMODULE my_dll, struct pkgopts *opts, char **argv )
166 {
167   /* A local wrapper function to facilitate passing pre-parsed
168    * command line options while performing the "climain()" call
169    * into mingw-get-0.dll
170    *
171    * Note that this requires a version of mingw-get-0.dll which
172    * provides the "cli_setopts()" hook...
173    */
174   typedef void (*dll_hook)(struct pkgopts *);
175   dll_hook cli_setopts = (dll_hook)(GetProcAddress( my_dll, "cli_setopts" ));
176   if( cli_setopts != NULL )
177     /*
178      * ...which allows us to pass the pre-parsed options.
179      */
180     cli_setopts( opts );
181
182   /* In any case, we always return the argument vector which is
183    * to be passed in the "climain()" call itself.
184    */
185   return argv;
186 }
187
188 static int xatoi( const char *input )
189 {
190   /* A replacement for the standard atoi() function; this implementation
191    * directs the call to strtol(), and so supports conversion of octal or
192    * hexadecimal representations, in addition to the decimal representation
193    * required by standard atoi(), while also protecting against the adverse
194    * effect of passing a NULL input pointer.
195    */
196   return (input != NULL) ? strtol( input, NULL, 0 ) : 0;
197 }
198
199 #define atmost( lim, val )              ((lim) < (val)) ? (lim) : (val)
200
201 /* Disable command line globbing; we don't need it, and case-insensitive
202  * glob matches on files such as INSTALL may interfere with case-sensitive
203  * specification of the mingw-get action keywords.
204  */
205 int _CRT_glob = 0;
206
207 int main( int argc, char **argv )
208 {
209   /* Provide storage for interpretation of any parsed command line options.
210    * Note that we could also initialise them here, but then we would need to
211    * give attention to the number of initialisers required; to save us that
212    * concern we will defer to an initialisation loop, below.
213    */
214   struct pkgopts parsed_options;
215
216   /* Make a note of this program's name, and where it's installed.
217    */
218   wchar_t *approot;
219   progname = basename( *argv );
220
221   if( argc > 1 )
222   {
223     /* The user specified arguments on the command line...
224      * Interpret any which specify processing options for this application,
225      * (these are all specified in GNU `long only' style).
226      */
227     int optref;
228     struct option options[] =
229     {
230       /* Option Name      Argument Category    Store To   Return Value
231        * --------------   ------------------   --------   ------------------
232        */
233       { "version",        no_argument,         NULL,      'V'                },
234       { "help",           no_argument,         NULL,      'h'                },
235       { "verbose",        optional_argument,   NULL,      OPTION_VERBOSE     },
236
237       { "recursive",      no_argument,         &optref,   OPTION_RECURSIVE   },
238       { "reinstall",      no_argument,         &optref,   OPTION_REINSTALL   },
239       { "download-only",  no_argument,         &optref,   OPTION_DNLOAD_ONLY },
240       { "print-uris",     no_argument,         &optref,   OPTION_PRINT_URIS  },
241
242       { "all-related",    no_argument,         &optref,   OPTION_ALL_RELATED },
243
244       { "desktop",        optional_argument,   &optref,   OPTION_DESKTOP     },
245       { "start-menu",     optional_argument,   &optref,   OPTION_START_MENU  },
246
247 #     if DEBUG_ENABLED( DEBUG_TRACE_DYNAMIC )
248         /* The "--trace" option is supported only when dynamic tracing
249          * debugging support has been compiled in.
250          */
251       { "trace",          required_argument,   &optref,   OPTION_TRACE       },
252 #     endif
253
254       /* This list must be terminated by a null definition...
255        */
256       { NULL, 0, NULL, 0 }
257     };
258
259     int opt, offset;
260     for( opt = OPTION_FLAGS; opt < OPTION_TABLE_SIZE; ++opt )
261       /*
262        * Ensure that all entries within the options table are initialised
263        * to zero, (equivalent to NULL for pointer entries)...
264        */
265       parsed_options.flags[opt].numeric = 0;
266
267     while( (opt = getopt_long_only( argc, argv, "vVh", options, &offset )) != -1 )
268       /*
269        * Parse any user specified options from the command line...
270        */
271       switch( opt )
272       {
273         case 'V':
274           /* This is a request to display the version of the application;
275            * emit the requisite informational message, and quit.
276            */
277           printf( version_identification );
278           return EXIT_SUCCESS;
279
280         case 'h':
281           /* This is a request to display help text and quit.
282            */
283           printf( help_text );
284           return EXIT_SUCCESS;
285
286         case OPTION_VERBOSE:
287           /* This is a request to set an explicit verbosity level,
288            * (minimum zero, maximum three), or if no explicit argument
289            * is specified, to increment verbosity as does "-v".
290            */
291           if( optarg != NULL )
292           {
293             /* This is the case where an explicit level was specified...
294              */
295             parsed_options.flags[OPTION_FLAGS].numeric =
296               (parsed_options.flags[OPTION_FLAGS].numeric & ~OPTION_VERBOSE)
297               | atmost( OPTION_VERBOSE_MAX, xatoi( optarg ));
298             break;
299           }
300
301         case 'v':
302           /* This is a request to increment the verbosity level
303            * from its initial zero setting, to a maximum of three.
304            */
305           if( (parsed_options.flags[OPTION_FLAGS].numeric & OPTION_VERBOSE) < 3 )
306             ++parsed_options.flags[OPTION_FLAGS].numeric;
307           break;
308
309         case OPTION_GENERIC:
310           switch( optref & OPTION_STORAGE_CLASS )
311           {
312             /* This represents a generic option specification,
313              * allowing for storage of a option argument of the
314              * specified class into a specified option slot...
315              */
316             unsigned shift;
317
318             case OPTION_STORE_STRING:
319               /* This is a simple store of a option argument
320                * which represents a character string.
321                */
322               mark_option_as_set( parsed_options, optref );
323               parsed_options.flags[optref & 0xfff].string = optarg;
324               break;
325
326             case OPTION_STORE_NUMBER:
327               /* This is also a simple store of the argument value,
328                * in this case interpreted as a number.
329                */
330               mark_option_as_set( parsed_options, optref );
331               parsed_options.flags[optref & 0xfff].numeric = xatoi( optarg );
332               break;
333
334             case OPTION_MERGE_NUMBER:
335               /* In this case, we combine the value of the argument,
336                * again interpreted as a number, with the original value
337                * stored in the option slot, forming the bitwise logical
338                * .OR. of the pair of values.
339                */
340               mark_option_as_set( parsed_options, optref );
341               parsed_options.flags[optref & 0xfff].numeric |= xatoi( optarg );
342               break;
343
344             default:
345               /* This is a mask and store operation for a specified
346                * bit-field within the first pair of flags slots; in
347                * this case, the optref value itself specifies a 12-bit
348                * value, a 12-bit combining mask, and an alignment shift
349                * count between 0 and 52, in 4-bit increments.
350                */
351               if( (shift = (optref & OPTION_SHIFT_MASK) >> 22) < 53 )
352               {
353                 uint64_t value = optref & 0xfff;
354                 uint64_t mask = (optref & 0xfff000) >> 12;
355                 *(uint64_t *)(parsed_options.flags) &= ~(mask << shift);
356                 *(uint64_t *)(parsed_options.flags) |= value << shift;
357               }
358           }
359           break;
360
361         default:
362           /* User specified an invalid or unsupported option...
363            */
364           if( opt != '?' )
365             fprintf( stderr, "%s: option '-%s' not yet supported\n",
366                 progname, options[offset].name
367               );
368           return EXIT_FAILURE;
369       }
370   }
371
372   /* Establish the installation path for the mingw-get application...
373    */
374   if( (approot = AppPathNameW( NULL )) != NULL )
375   {
376     /* ...and set up the APPROOT environment variable to refer to
377      * the associated installation prefix, (ensuring that it ends
378      * with a directory name separator)...
379      */
380     const char *approot_fmt = "APPROOT=%S\\";
381     char approot_setup[1 + snprintf( NULL, 0, approot_fmt, approot )];
382     snprintf( approot_setup, sizeof( approot_setup ), approot_fmt, approot );
383     putenv( approot_setup );
384   }
385
386   if( argc > 1 )
387   {
388     /* The user specified arguments on the command line...
389      * we load the supporting DLL into the current process context,
390      * then, remaining in command line mode, we jump to its main
391      * command line processing routine...
392      */
393     int lock;
394     char *argv_base = *argv;
395     typedef int (*dll_entry)( int, char ** );
396     HMODULE my_dll = LoadLibraryW( AppPathNameW( MINGW_GET_DLL ) );
397     dll_entry climain = (dll_entry)(GetProcAddress( my_dll, "climain" ));
398     if( climain == NULL )
399     {
400       /* ...bailing out, on failure to load the DLL.
401        */
402       fprintf( stderr, "%s: %S: shared library load failed\n", 
403           progname, MINGW_GET_DLL
404         );
405       return EXIT_FATAL;
406     }
407
408     /* Adjust argc and argv to discount parsed options...
409      */
410     argc -= optind;
411     argv += optind - 1;
412     /*
413      * ...while preserving the original argv[0] reference within
414      * the first remaining argument to be passed to climain().
415      */
416     *argv = argv_base;
417
418     /* We want only one mingw-get process accessing the XML database
419      * at any time; attempt to acquire an exclusive access lock...
420      */
421     if( (lock = pkgInitRites( progname )) >= 0 )
422     {
423       /* ...and proceed, only if successful.
424        *  A non-zero return value indicates that a fatal error occurred.
425        */
426       int rc = climain( argc, cli_setargv( my_dll, &parsed_options, argv ) );
427
428       /* We must release the mingw-get DLL code, BEFORE we invoke
429        * last rites processing, (otherwise the last rites clean-up
430        * handler exhibits abnormal behaviour when it is exec'd).
431        */
432       FreeLibrary( my_dll );
433       if (rc == 0)
434         return pkgLastRites( lock, progname );
435       else
436       {
437         (void) pkgLastRites( lock, progname );
438         return EXIT_FATAL;
439       }
440     }
441     /* If we get to here, then we failed to acquire a lock;
442      * we MUST abort!
443      */
444     return EXIT_FATAL;
445   }
446
447   else
448   { /* No arguments were specified on the command line...
449      * we interpret this as a request to start up in GUI mode...
450      */ 
451     wchar_t *libexec_path = AppPathNameW( MINGW_GET_GUI );
452     char gui_program[1 + snprintf( NULL, 0, "%S", libexec_path )];
453     snprintf( gui_program, sizeof( gui_program ), "%S", libexec_path );
454     int status = execv( gui_program, (const char* const*)(argv) );
455
456     /* If we get to here, then the GUI could not be started;
457      * first, try the fall-back GUI diagnostic stub...
458      */
459     libexec_path = AppPathNameW( MINGW_GET_GFB );
460     snprintf( gui_program, sizeof( gui_program ), "%S", libexec_path );
461     status = execv( gui_program, (const char* const*)(argv) );
462
463     /* ...before  issuing a diagnostic message, and quitting
464      * with an abnormal termination code.
465      */
466     fprintf( stderr,
467         "%s: %S: unable to start GUI; helper program not installed\n",
468         progname, MINGW_GET_GUI
469       );
470     return EXIT_FATAL;
471   }
472 }
473
474 /* $RCSfile$: end of file */