OSDN Git Service

8c28b12912269307b02c1a202dc708497c7b4b77
[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, MinGW 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 #define MINGW_GET_DLL  L"libexec/mingw-get/mingw-get-0.dll"
31 #define MINGW_GET_GUI  L"libexec/mingw-get/gui.exe"
32
33 #include <windows.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <libgen.h>
37 #include <process.h>
38 #include <getopt.h>
39
40 #define EXIT_FATAL  EXIT_FAILURE + 1
41
42 wchar_t *AppPathNameW( const wchar_t *relpath )
43 {
44   /* UTF-16LE implementation; NOT thread safe...
45    *
46    * Map "relpath" into the file system hierarchy with logical root
47    * at the prefix where the application suite is installed, such that
48    * it becomes "d:\prefix\relpath".
49    *
50    * If the application's executables are installed in a directory called
51    * "bin" or "sbin", then this final directory is excluded from the mapped
52    * prefix, (i.e. a program installed as "d:\prefix\bin\prog.exe" returns
53    * the mapped path as "d:\prefix\relpath", rather than as the inclusive
54    * path "d:\prefix\bin\relpath"); in any other case, the prefix is taken
55    * as the path to the directory in which the program file is installed,
56    * (i.e. a program installed as "d:\prefix\foo\prog.exe" returns the
57    * mapped path as "d:\prefix\foo\relpath".
58    *
59    * Mapped path is returned in this static buffer...
60    */
61   static wchar_t retpath[MAX_PATH], *tail = NULL;
62
63   if( tail == NULL )
64   {
65     /* First time initialisation...
66      *
67      * Fill in the static local buffer, with the appropriate "prefix"
68      * string, marking the point at which "relpath" is to be appended.
69      *
70      * On subsequent calls, we reuse this static buffer, leaving the
71      * "prefix" element unchanged, but simply overwriting the "relpath"
72      * element from any prior call; this is NOT thread safe!
73      */
74     wchar_t prev = L'\\';
75     wchar_t bindir[] = L"bin", *bindir_p = bindir;
76     wchar_t *mark, *scan = mark = tail = retpath;
77
78     /* Ascertain the installation path of the calling executable.
79      */
80     int chk = GetModuleFileNameW( NULL, retpath, MAX_PATH );
81
82     /* Validate it; reject any result which doesn't fit in "retpath".
83      */
84     if( (chk == 0) || ((chk == MAX_PATH) && (retpath[--chk] != L'\0')) )
85       return tail = NULL;
86
87     /* Parse it, to locate the end of the effective "prefix" string...
88      */
89     do { if( *scan == L'/' )
90            /*
91             * This is a sanity check; it should not be necessary, but...
92             *
93             * Enforce use of "\" rather than "/" as path component separator;
94             * ( "LoadLibrary" may be broken, since it seems to care! )
95             */
96            *scan = L'\\';
97
98          if( *scan && (prev == L'\\') )
99          {
100            /* We found the start a new path name component directory,
101             * (or maybe the file name, at the end); mark it as a possible
102             * final element of the path name, leaving "tail" pointing to
103             * the previously marked element.
104             */
105            tail = mark;
106            mark = scan;
107          }
108        } while( (prev = *scan++) != L'\0' );
109
110     /* When we get to here, "mark" should point to the executable file name,
111      * at the end of the path name string, while "tail" should point to the
112      * last directory in the installation path; we now check, without regard
113      * to case, if this final directory name is "bin" or "sbin"...
114      */
115     if( (*(scan = tail) == L's') || (*scan == L'S') )
116       /*
117        * Might be "sbin"; skip the initial "s", and check for "bin"...
118        */
119       ++scan;
120
121     while( *bindir_p && ((*scan++ | L'\x20') == *bindir_p++) )
122       /*
123        * ...could still match "bin"...
124        */ ;
125     if( *bindir_p || (*scan != L'\\') )
126       /*
127        * No, it didn't match; adjust "tail", so we leave the final
128        * directory name as part of "prefix".
129        */
130       tail = mark;
131   }
132
133   if( relpath == NULL )
134     /*
135      * No "relpath" argument given; simply truncate, to return only
136      * the effective "prefix" string...
137      */
138     *tail = L'\0';
139
140   else
141   { wchar_t *append = tail;
142     do { /*
143           * Append the specified path to the application's root,
144           * again, taking care to use "\" as the separator character...
145           */
146          *append++ = (*relpath == L'/') ? L'\\' : *relpath;
147        } while( *relpath++ && ((append - retpath) < MAX_PATH) );
148
149     if( *--append != L'\0' )
150       /*
151        * Abort, if we didn't properly terminate the return string.
152        */
153       return NULL;
154   }
155   return retpath;
156 }
157
158 extern const char *version_identification;
159
160 int main( int argc, char **argv )
161 {
162   wchar_t *approot;             /* where this application is installed */
163
164   if( argc > 1 )
165   {
166     /* The user specified arguments on the command line...
167      * Interpret any which specify processing options for this application,
168      * (these are all specified in GNU `long only' style).
169      */
170     struct option options[] =
171     {
172       /* Option Name              Argument Category    Store To   Return Value
173        * ----------------------   ------------------   --------   ------------
174        */
175       { "version",                no_argument,         NULL,      'V'          },
176
177       /* This list must be terminated by a null definition...
178        */
179       { NULL, 0, NULL, 0 }
180     };
181
182     int opt, offset;
183     while( (opt = getopt_long_only( argc, argv, "V", options, &offset )) != -1 )
184       switch( opt )
185       {
186         case 'V':
187           /* This is a request to display the version of the application;
188            * emit the requisite informational message, and quit.
189            */
190           printf( version_identification );
191           return 0;
192
193         default:
194           /* User specified an invalid or unsupported option...
195            */
196           if( opt != '?' )
197             fprintf( stderr, "%s: option '-%s' not yet supported\n",
198                 basename( *argv ), options[offset].name
199               );
200           return EXIT_FAILURE;
201       }
202   }
203
204   /* Establish the installation path for the mingw-get application...
205    */
206   if( (approot = AppPathNameW( NULL )) != NULL )
207   {
208     /* ...and set up the APPROOT environment variable to refer to
209      * the associated installation prefix...
210      */
211     char approot_setup[1 + snprintf( NULL, 0, "APPROOT=%S", approot )];
212     snprintf( approot_setup, sizeof( approot_setup ), "APPROOT=%S", approot );
213     putenv( approot_setup );
214   }
215
216   if( --argc )
217   {
218     /* The user specified arguments on the command line...
219      * we load the supporting DLL into the current process context,
220      * then, remaining in command line mode, we jump to its main
221      * command line processing routine...
222      */
223     typedef int (*dll_entry)( int, char ** );
224     HMODULE my_dll = LoadLibraryW( AppPathNameW( MINGW_GET_DLL ) );
225     dll_entry climain = (dll_entry)(GetProcAddress( my_dll, "climain" ));
226     if( climain == NULL )
227     {
228       /* ...bailing out, on failure to load the DLL.
229        */
230       fprintf( stderr, "%s: %S: shared library load failed\n", 
231           basename( *argv ), MINGW_GET_DLL
232         );
233       return EXIT_FATAL;
234     }
235     return climain( argc, argv );
236   }
237
238   else
239   { /* No arguments were specified on the command line...
240      * we interpret this as a request to start up in GUI mode...
241      */ 
242     wchar_t *libexec_path = AppPathNameW( MINGW_GET_GUI );
243     char gui_program[1 + snprintf( NULL, 0, "%S", libexec_path )];
244     snprintf( gui_program, sizeof( gui_program ), "%S", libexec_path );
245     int status = execv( gui_program, (const char* const*)(argv) );
246
247     /* If we get to here, then the GUI could not be started...
248      * Issue a diagnostic message, before abnormal termination.
249      */
250     fprintf( stderr, "%s: %S: unable to start application; status = %d\n",
251         basename( *argv ), MINGW_GET_GUI, status
252       );
253     return EXIT_FATAL;
254   }
255 }
256
257 /* $RCSfile$: end of file */