/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _H_UTILS
#define _H_UTILS
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS 1
#include <direct.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <windows.h>
// VS vs MINGW specific includes
#ifdef USE_VS_CRT
#include <crtdbg.h> // for _ASSERT
#else
#define _ASSERT(x) // undef
#endif
extern bool gIsDebug;
extern bool gIsConsole;
// An array that knows its own size. Not dynamically resizable.
template <class T> class CArray {
T* mPtr;
int mSize;
public:
explicit CArray(int size) {
mSize = size;
mPtr = new T[size];
}
~CArray() {
if (mPtr != NULL) {
delete[] mPtr;
mPtr = NULL;
}
mSize = 0;
}
T& operator[](int i) {
_ASSERT(i >= 0 && i < mSize);
return mPtr[i];
}
int size() const {
return mSize;
}
};
// A simple string class wrapper.
class CString {
protected:
char *mStr;
public:
CString() { mStr = NULL; }
CString(const CString &str) { mStr = NULL; set(str.mStr); }
explicit CString(const char *str) { mStr = NULL; set(str); }
CString(const char *start, size_t length) { mStr = NULL; set(start, length); }
CString& operator=(const CString &str) {
return set(str.cstr());
}
CString& set(const char *str) {
if (str != mStr) {
_free();
if (str != NULL) {
mStr = _strdup(str);
}
}
return *this;
}
CString& set(const char *start, size_t length) {
_free();
if (start != NULL) {
mStr = (char *)malloc(length + 1);
strncpy(mStr, start, length);
mStr[length] = 0;
}
return *this;
}
CString& setv(const char *str, va_list ap) {
_free();
// _vscprintf(str, ap) is only available with the MSVCRT, not MinGW.
// Instead we'll iterate till we have enough space to generate the string.
size_t len = strlen(str) + 1024;
mStr = (char *)malloc(len);
strcpy(mStr, str); // provide a default in case vsnprintf totally fails
for (int guard = 0; guard < 10; guard++) {
int ret = vsnprintf(mStr, len, str, ap);
if (ret == -1) {
// Some implementations don't give the proper size needed
// so double the space and try again.
len *= 2;
} else if (ret >= len) {
len = ret + 1;
} else {
// There was enough space to write.
break;
}
mStr = (char *)realloc((void *)mStr, len);
strcpy(mStr, str); // provide a default in case vsnprintf totally fails
}
return *this;
}
CString& setf(const char *str, ...) {
_free();
va_list ap;
va_start(ap, str);
setv(str, ap);
va_end(ap);
return *this;
}
virtual ~CString() { _free(); }
// Returns the C string owned by this CString. It will be
// invalid as soon as this CString is deleted or out of scope.
const char * cstr() const {
return mStr;
}
bool isEmpty() const {
return mStr == NULL || *mStr == 0;
}
size_t length() const {
return mStr == NULL ? 0 : strlen(mStr);
}
CString& add(const char *str) {
if (mStr == NULL) {
set(str);
} else {
mStr = (char *)realloc((void *)mStr, strlen(mStr) + strlen(str) + 1);
strcat(mStr, str);
}
return *this;
}
CString& add(const char *str, int length) {
if (mStr == NULL) {
set(str, length);
} else {
size_t l1 = strlen(mStr);
mStr = (char *)realloc((void *)mStr, l1 + length + 1);
strncpy(mStr + l1, str, length);
mStr[l1 + length] = 0;
}
return *this;
}
CArray<CString> * split(char sep) const {
if (mStr == NULL) {
return new CArray<CString>(0);
}
const char *last = NULL;
int n = 0;
for (const char *s = mStr; *s; s++) {
if (*s == sep && s != mStr && (last == NULL || s > last+1)) {
n++;
last = s;
}
}
CArray<CString> *result = new CArray<CString>(n);
last = NULL;
n = 0;
for (const char *s = mStr; *s; s++) {
if (*s == sep) {
if (s != mStr && (last == NULL || s > last+1)) {
const char *start = last ? last : mStr;
(*result)[n++].set(start, s-start);
}
last = s+1;
}
}
return result;
}
// Sets the string to the message matching Win32 GetLastError.
// If message is non-null, it is prepended to the last error string.
CString& setLastWin32Error(const char *message) {
DWORD err = GetLastError();
LPSTR errStr;
if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, /* lpSource */
err, /* dwMessageId */
0, /* dwLanguageId */
(LPSTR)&errStr, /* lpBuffer */
0, /* nSize */
NULL) != 0) { /* va_list args */
if (message == NULL) {
setf("[%d] %s", err, errStr);
} else {
setf("%s[%d] %s", message, err, errStr);
}
LocalFree(errStr);
}
return *this;
}
private:
void _free() {
if (mStr != NULL) {
free((void *)mStr);
mStr = NULL;
}
}
};
// A simple path class wrapper.
class CPath : public CString {
public:
CPath() : CString() { }
CPath(const CString &str) : CString(str) { }
CPath(const CPath &str) : CString(str) { }
explicit CPath(const char *str) : CString(str) { }
CPath(const char *start, int length) : CString(start, length) { }
CPath& operator=(const CPath &str) {
set(str.cstr());
return *this;
}
// Appends a path segment, adding a \ as necessary.
CPath& addPath(const CString &s) {
return addPath(s.cstr());
}
// Appends a path segment, adding a \ as necessary.
CPath& addPath(const char *s) {
_ASSERT(s != NULL);
if (s != NULL && s[0] != 0) {
size_t n = length();
if (n > 0 && s[0] != '\\' && mStr[n-1] != '\\') add("\\");
add(s);
}
return *this;
}
// Returns true if file exist and is not a directory.
// There's no garantee we have rights to access it.
bool fileExists() const {
if (mStr == NULL) return false;
DWORD attribs = GetFileAttributesA(mStr);
return attribs != INVALID_FILE_ATTRIBUTES &&
!(attribs & FILE_ATTRIBUTE_DIRECTORY);
}
// Returns true if file exist and is a directory.
// There's no garantee we have rights to access it.
bool dirExists() const {
if (mStr == NULL) return false;
DWORD attribs = GetFileAttributesA(mStr);
return attribs != INVALID_FILE_ATTRIBUTES &&
(attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
// Returns a copy of the directory portion of the path, if any
CPath dirName() const {
CPath result;
if (mStr != NULL) {
char *pos = strrchr(mStr, '\\');
if (pos != NULL) {
result.set(mStr, pos - mStr);
}
}
return result;
}
// Returns a pointer to the baseName part of the path.
// It becomes invalid if the path changes.
const char * baseName() const {
if (mStr != NULL) {
char *pos = strrchr(mStr, '\\');
if (pos != NULL) {
return pos + 1;
}
}
return NULL;
}
// If the path ends with the given searchName, replace in-place by the new name
void replaceName(const char *searchName, const char* newName) {
if (mStr == NULL) return;
size_t n = length();
size_t sn = strlen(searchName);
if (n < sn) return;
// if mStr ends with searchName
if (strcmp(mStr + n - sn, searchName) == 0) {
size_t sn2 = strlen(newName);
if (sn2 > sn) {
mStr = (char *)realloc((void *)mStr, n + sn2 - sn + 1);
}
strcpy(mStr + n - sn, newName);
mStr[n + sn2 - sn] = 0;
}
}
// Returns a copy of this path as a DOS short path in the destination.
// Returns true if the Win32 getShortPathName method worked.
// In case of error, returns false and does not change the destination.
// It's OK to invoke this->toShortPath(this).
bool toShortPath(CPath *dest) {
const char *longPath = mStr;
if (mStr == NULL) return false;
DWORD lenShort = (DWORD)strlen(longPath) + 1; // GetShortPathName deals with DWORDs
char * shortPath = (char *)malloc(lenShort);
DWORD length = GetShortPathName(longPath, shortPath, lenShort);
if (length > lenShort) {
// The buffer wasn't big enough, this is the size to use.
free(shortPath);
lenShort = length;
shortPath = (char *)malloc(length);
length = GetShortPathName(longPath, shortPath, lenShort);
}
if (length != 0) dest->set(shortPath);
free(shortPath);
return length != 0;
}
};
// Displays a message in an ok+info dialog box.
void msgBox(const char* text, ...);
// Displays GetLastError prefixed with a description in an error dialog box
void displayLastError(const char *description, ...);
// Executes the command line. Does not wait for the program to finish.
// The return code is from CreateProcess (0 means failure), not the running app.
int execNoWait(const char *app, const char *params, const char *workDir);
// Executes command, waits for completion and returns exit code.
// As indicated in MSDN for CreateProcess, callers should double-quote the program name
// e.g. cmd="\"c:\program files\myapp.exe\" arg1 arg2";
int execWait(const char *cmd);
bool getModuleDir(CPath *outDir);
#endif /* _WIN32 */
#endif /* _H_UTILS */