1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _H_UTILS
18#define _H_UTILS
19
20#ifdef _WIN32
21
22#define _CRT_SECURE_NO_WARNINGS 1
23
24#include <direct.h>
25#include <stdio.h>
26#include <stdarg.h>
27#include <string.h>
28#include <windows.h>
29
30// VS vs MINGW specific includes
31#ifdef USE_VS_CRT
32    #include <crtdbg.h>     // for _ASSERT
33#else
34    #define _ASSERT(x)      // undef
35#endif
36
37extern bool gIsDebug;
38extern bool gIsConsole;
39
40// An array that knows its own size. Not dynamically resizable.
41template <class T> class CArray {
42    T* mPtr;
43    int mSize;
44public:
45    explicit CArray(int size) {
46        mSize = size;
47        mPtr = new T[size];
48    }
49
50    ~CArray() {
51        if (mPtr != NULL) {
52            delete[] mPtr;
53            mPtr = NULL;
54        }
55        mSize = 0;
56    }
57
58    T& operator[](int i) {
59        _ASSERT(i >= 0 && i < mSize);
60        return mPtr[i];
61    }
62
63    int size() const {
64        return mSize;
65    }
66};
67
68// A simple string class wrapper.
69class CString {
70protected:
71    char *mStr;
72public:
73    CString()                              { mStr = NULL; }
74    CString(const CString &str)            { mStr = NULL; set(str.mStr); }
75    explicit CString(const char *str)      { mStr = NULL; set(str); }
76    CString(const char *start, int length) { mStr = NULL; set(start, length); }
77
78    CString& operator=(const CString &str) {
79        return set(str.cstr());
80    }
81
82    CString& set(const char *str) {
83        if (str != mStr) {
84            _free();
85            if (str != NULL) {
86                mStr = _strdup(str);
87            }
88        }
89        return *this;
90    }
91
92    CString& set(const char *start, int length) {
93        _free();
94        if (start != NULL) {
95            mStr = (char *)malloc(length + 1);
96            strncpy(mStr, start, length);
97            mStr[length] = 0;
98        }
99        return *this;
100    }
101
102    CString& setv(const char *str, va_list ap) {
103        _free();
104        // _vscprintf(str, ap) is only available with the MSVCRT, not MinGW.
105        // Instead we'll iterate till we have enough space to generate the string.
106        int len = strlen(str) + 1024;
107        mStr = (char *)malloc(len);
108        strcpy(mStr, str); // provide a default in case vsnprintf totally fails
109        for (int guard = 0; guard < 10; guard++) {
110            int ret = vsnprintf(mStr, len, str, ap);
111            if (ret == -1) {
112                // Some implementations don't give the proper size needed
113                // so double the space and try again.
114                len *= 2;
115            } else if (ret >= len) {
116                len = ret + 1;
117            } else {
118                // There was enough space to write.
119                break;
120            }
121            mStr = (char *)realloc((void *)mStr, len);
122            strcpy(mStr, str); // provide a default in case vsnprintf totally fails
123        }
124        return *this;
125    }
126
127    CString& setf(const char *str, ...) {
128        _free();
129        va_list ap;
130        va_start(ap, str);
131        setv(str, ap);
132        va_end(ap);
133        return *this;
134    }
135
136    virtual ~CString() { _free(); }
137
138    // Returns the C string owned by this CString. It will be
139    // invalid as soon as this CString is deleted or out of scope.
140    const char * cstr() const {
141        return mStr;
142    }
143
144    bool isEmpty() const {
145        return mStr == NULL || *mStr == 0;
146    }
147
148    int length() const {
149        return mStr == NULL ? 0 : strlen(mStr);
150    }
151
152    CString& add(const char *str) {
153        if (mStr == NULL) {
154            set(str);
155        } else {
156            mStr = (char *)realloc((void *)mStr, strlen(mStr) + strlen(str) + 1);
157            strcat(mStr, str);
158        }
159        return *this;
160    }
161
162    CString& add(const char *str, int length) {
163        if (mStr == NULL) {
164            set(str, length);
165        } else {
166            int   l1 = strlen(mStr);
167            mStr = (char *)realloc((void *)mStr, l1 + length + 1);
168            strncpy(mStr + l1, str, length);
169            mStr[l1 + length] = 0;
170        }
171        return *this;
172    }
173
174    CArray<CString> * split(char sep) const {
175        if (mStr == NULL) {
176            return new CArray<CString>(0);
177        }
178        const char *last = NULL;
179        int n = 0;
180        for (const char *s = mStr; *s; s++) {
181            if (*s == sep && s != mStr && (last == NULL || s > last+1)) {
182                n++;
183                last = s;
184            }
185        }
186
187        CArray<CString> *result = new CArray<CString>(n);
188        last = NULL;
189        n = 0;
190        for (const char *s = mStr; *s; s++) {
191            if (*s == sep) {
192                if (s != mStr && (last == NULL || s > last+1)) {
193                    const char *start = last ? last : mStr;
194                    (*result)[n++].set(start, s-start);
195                }
196                last = s+1;
197            }
198        }
199
200        return result;
201    }
202
203    // Sets the string to the message matching Win32 GetLastError.
204    // If message is non-null, it is prepended to the last error string.
205    CString& setLastWin32Error(const char *message) {
206        DWORD err = GetLastError();
207        LPSTR errStr;
208        if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */
209                          FORMAT_MESSAGE_FROM_SYSTEM,
210                          NULL,                             /* lpSource */
211                          err,                              /* dwMessageId */
212                          0,                                /* dwLanguageId */
213                          (LPSTR)&errStr,                   /* lpBuffer */
214                          0,                                /* nSize */
215                          NULL) != 0) {                     /* va_list args */
216            if (message == NULL) {
217                setf("[%d] %s", err, errStr);
218            } else {
219                setf("%s[%d] %s", message, err, errStr);
220            }
221            LocalFree(errStr);
222        }
223        return *this;
224    }
225
226private:
227    void _free() {
228        if (mStr != NULL) {
229            free((void *)mStr);
230            mStr = NULL;
231        }
232    }
233
234};
235
236// A simple path class wrapper.
237class CPath : public CString {
238public:
239    CPath()                              : CString()    { }
240    CPath(const CString &str)            : CString(str) { }
241    CPath(const CPath &str)              : CString(str) { }
242    explicit CPath(const char *str)      : CString(str) { }
243    CPath(const char *start, int length) : CString(start, length) { }
244
245    CPath& operator=(const CPath &str) {
246        set(str.cstr());
247        return *this;
248    }
249
250    // Appends a path segment, adding a \ as necessary.
251    CPath& addPath(const CString &s) {
252        return addPath(s.cstr());
253    }
254
255    // Appends a path segment, adding a \ as necessary.
256    CPath& addPath(const char *s) {
257        _ASSERT(s != NULL);
258        if (s != NULL && s[0] != 0) {
259            int n = length();
260            if (n > 0 && s[0] != '\\' && mStr[n-1] != '\\') add("\\");
261            add(s);
262        }
263        return *this;
264    }
265
266    // Returns true if file exist and is not a directory.
267    // There's no garantee we have rights to access it.
268    bool fileExists() const {
269        if (mStr == NULL) return false;
270        DWORD attribs = GetFileAttributesA(mStr);
271        return attribs != INVALID_FILE_ATTRIBUTES &&
272             !(attribs & FILE_ATTRIBUTE_DIRECTORY);
273    }
274
275    // Returns true if file exist and is a directory.
276    // There's no garantee we have rights to access it.
277    bool dirExists() const {
278        if (mStr == NULL) return false;
279        DWORD attribs = GetFileAttributesA(mStr);
280        return attribs != INVALID_FILE_ATTRIBUTES &&
281              (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
282    }
283
284    // Returns a copy of the directory portion of the path, if any
285    CPath dirName() const {
286        CPath result;
287        if (mStr != NULL) {
288            char *pos = strrchr(mStr, '\\');
289            if (pos != NULL) {
290                result.set(mStr, pos - mStr);
291            }
292        }
293        return result;
294    }
295
296    // Returns a pointer to the baseName part of the path.
297    // It becomes invalid if the path changes.
298    const char * baseName() const {
299        if (mStr != NULL) {
300            char *pos = strrchr(mStr, '\\');
301            if (pos != NULL) {
302                return pos + 1;
303            }
304        }
305        return NULL;
306    }
307
308    // If the path ends with the given searchName, replace in-place by the new name
309    void replaceName(const char *searchName, const char* newName) {
310        if (mStr == NULL) return;
311        int n = length();
312        int sn = strlen(searchName);
313        if (n < sn) return;
314        // if mStr ends with searchName
315        if (strcmp(mStr + n - sn, searchName) == 0) {
316            int sn2 = strlen(newName);
317            if (sn2 > sn) {
318                mStr = (char *)realloc((void *)mStr, n + sn2 - sn + 1);
319            }
320            strcpy(mStr + n - sn, newName);
321            mStr[n + sn2 - sn] = 0;
322        }
323    }
324
325    // Returns a copy of this path as a DOS short path in the destination.
326    // Returns true if the Win32 getShortPathName method worked.
327    // In case of error, returns false and does not change the destination.
328    // It's OK to invoke this->toShortPath(this).
329    bool toShortPath(CPath *dest) {
330        const char *longPath = mStr;
331        if (mStr == NULL) return false;
332
333        DWORD lenShort = strlen(longPath) + 1;
334        char * shortPath = (char *)malloc(lenShort);
335
336        DWORD length = GetShortPathName(longPath, shortPath, lenShort);
337        if (length > lenShort) {
338            // The buffer wasn't big enough, this is the size to use.
339            free(shortPath);
340            lenShort = length;
341            shortPath = (char *)malloc(length);
342            length = GetShortPathName(longPath, shortPath, lenShort);
343        }
344
345        if (length != 0) dest->set(shortPath);
346
347        free(shortPath);
348        return length != 0;
349    }
350};
351
352// Displays a message in an ok+info dialog box.
353void msgBox(const char* text, ...);
354
355// Displays GetLastError prefixed with a description in an error dialog box
356void displayLastError(const char *description, ...);
357
358// Executes the command line. Does not wait for the program to finish.
359// The return code is from CreateProcess (0 means failure), not the running app.
360int execNoWait(const char *app, const char *params, const char *workDir);
361
362// Executes command, waits for completion and returns exit code.
363// As indicated in MSDN for CreateProcess, callers should double-quote the program name
364// e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2";
365int execWait(const char *cmd);
366
367bool getModuleDir(CPath *outDir);
368
369// Disables the FS redirection done by WOW64.
370// Because this runs as a 32-bit app, Windows automagically remaps some
371// folder under the hood (e.g. "Programs Files(x86)" is mapped as "Program Files").
372// This prevents the app from correctly searching for java.exe in these folders.
373// The registry is also remapped.
374PVOID disableWow64FsRedirection();
375
376// Reverts the redirection disabled in disableWow64FsRedirection.
377void revertWow64FsRedirection(PVOID oldWow64Value);
378
379#endif /* _WIN32 */
380#endif /* _H_UTILS */
381