OSDN Git Service

Collect distributable package files in user specified directory.
[mingw/mingw-get.git] / src / approot.c
1 /*
2  * approot.c
3  *
4  * $Id$
5  *
6  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7  * Copyright (C) 2009, 2012, MinGW Project
8  *
9  *
10  * Implementation of helper function to identify the root directory
11  * of the mingw-get installation tree, and to map sub-directory path
12  * names relative to this tree.
13  *
14  *
15  * This is free software.  Permission is granted to copy, modify and
16  * redistribute this software, under the provisions of the GNU General
17  * Public License, Version 3, (or, at your option, any later version),
18  * as published by the Free Software Foundation; see the file COPYING
19  * for licensing details.
20  *
21  * Note, in particular, that this software is provided "as is", in the
22  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
25  * MinGW Project, accept liability for any damages, however caused,
26  * arising from the use of this software.
27  *
28  */
29 #define WIN32_LEAN_AND_MEAN
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <windows.h>
34 #include <limits.h>
35 #include <ctype.h>
36
37 #define wcscasecmp _wcsicmp
38
39 wchar_t *AppPathNameW( const wchar_t *relpath )
40 {
41   /* UTF-16LE implementation; NOT thread safe...
42    *
43    * Map "relpath" into the file system hierarchy with logical root
44    * at the prefix where the application suite is installed, such that
45    * it becomes "d:\prefix\relpath".
46    *
47    * If the application's executables are installed in a directory called
48    * "bin" or "sbin", then this final directory is excluded from the mapped
49    * prefix, (i.e. a program installed as "d:\prefix\bin\prog.exe" returns
50    * the mapped path as "d:\prefix\relpath", rather than as the inclusive
51    * path "d:\prefix\bin\relpath").  Additionally, if the absolute path to
52    * the program executable includes a directory called "libexec", at any
53    * level within the path, then the absolute path to the parent of this is
54    * taken as the effective prefix.  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    * Note that, in all cases, the returned path name is normalised, such
60    * that only "\" is used as the directory name separator; this ensures
61    * that it may be safely passed to functions such as LoadLibrary(),
62    * which are known to reject "/" as the separator.
63    *
64    *
65    * Mapped path is returned in this static buffer...
66    */
67   static wchar_t retpath[PATH_MAX], *tail = NULL;
68
69   if( tail == NULL )
70   {
71     /* First time initialisation...
72      *
73      * Fill in the static local buffer, with the appropriate "prefix"
74      * string, marking the point at which "relpath" is to be appended.
75      *
76      * On subsequent calls, we reuse this static buffer, leaving the
77      * "prefix" element unchanged, but simply overwriting the "relpath"
78      * element from any prior call; this is NOT thread safe!
79      */
80     wchar_t bindir[] = L"bin", *bindir_p = bindir;
81     wchar_t *mark, *scan = mark = tail = retpath;
82
83     /* Ascertain the installation path of the calling executable.
84      */
85     int chk = GetModuleFileNameW( NULL, retpath, PATH_MAX );
86
87     /* Validate it; reject any result which doesn't fit in "retpath".
88      */
89     if( (chk == 0) || ((chk == PATH_MAX) && (retpath[--chk] != L'\0')) )
90       return tail = NULL;
91
92     /* Parse it, to locate the end of the effective "prefix" string...
93      */
94     do { if( (*scan == L'/') || (*scan == L'\\') )
95          {
96            /* We found the start of a new path name component directory,
97             * (or maybe the file name, at the end); mark it as a possible
98             * final element of the path name, leaving "tail" pointing to
99             * the previously marked element.
100             */
101            tail = mark;
102            mark = scan;
103            if( scan > tail )
104            {
105              /* The final name element is not empty; temporarily mark
106               * it as "end-of-string", the check if it represents the
107               * "libexec" directory name...
108               */
109              *scan = L'\0';
110              if( wcscasecmp( tail, L"\\libexec" ) == 0 )
111              {
112                /* ...and when it does, we back up to mark its parent
113                 * directory as the last in the "prefix" path name...
114                 */
115                scan = tail;
116                *scan-- = L'\0';
117              }
118              else
119                /* ...otherwise we append a directory separator, before
120                 * cycling around to append the next entity name from the
121                 * module path name string.
122                 */
123                *scan = L'\\';
124            }
125          }
126        } while( (*++scan) != L'\0' );
127
128     if( *(scan = tail) == L'\\' )
129     {
130       /* When we get to here, "mark" should point to the last directory
131        * separator, immediately preceeding the executable file name, while
132        * "tail" should point to the directory separator preceeding the last
133        * sub-directory name in the path; we now check, without regard to
134        * case, if this final sub-directory name is "bin" or "sbin"...
135        */
136       *mark = L'\0';
137       if( towlower( *++scan ) == L's' )
138         /*
139          * Might be "sbin"; skip the initial "s", and check for "bin"...
140          */
141         ++scan;
142
143       if( wcscasecmp( scan, L"bin" ) == 0 )
144         /*
145          * The final sub-directory is either "bin" or "sbin"; prune it...
146          */
147         *tail = L'\0';
148
149       else
150         /* ...but when it doesn't match either of these, just adjust the
151          * "tail" pointer, so we leave the final sub-directory name as
152          * part of the "prefix".
153          */
154         tail = mark;
155     }
156   }
157
158   if( relpath == NULL )
159     /*
160      * No "relpath" argument given; simply truncate, to return only
161      * the effective "prefix" string...
162      */
163     *tail = L'\0';
164
165   else
166   { /* We have a "relpath" argument to append; first ensure that the
167      * prefix ends with a directory name separator...
168      */
169     wchar_t *append = tail;
170     if( (*relpath != L'/') && (*relpath != L'\\') )
171       *append++ = L'\\';
172
173     do { /* ...then append the specified path to the application's root,
174           * again, taking care to use "\" as the separator character...
175           */
176          *append++ = (*relpath == L'/') ? L'\\' : *relpath;
177        } while( *relpath++ && ((append - retpath) < PATH_MAX) );
178
179     if( *--append != L'\0' )
180       /*
181        * Abort, if we didn't properly terminate the return string.
182        */
183       return NULL;
184   }
185   return retpath;
186 }
187
188 /* $RCSfile$: end of file */