d3e6ade96f
I needed to `m_origPathInfo.reset()` in `RequestURI::clear`
909 linhas
23 KiB
C++
909 linhas
23 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "util.h"
|
|
#include "hphp/util/base.h"
|
|
#include "hphp/util/lock.h"
|
|
#include "hphp/util/logger.h"
|
|
#include "hphp/util/exception.h"
|
|
#include "hphp/util/network.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
#include <execinfo.h>
|
|
#include <cxxabi.h>
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static Mutex s_file_mutex;
|
|
|
|
void Util::split(char delimiter, const char *s, vector<string> &out,
|
|
bool ignoreEmpty /* = false */) {
|
|
assert(s);
|
|
|
|
const char *start = s;
|
|
const char *p = s;
|
|
for (; *p; p++) {
|
|
if (*p == delimiter) {
|
|
if (!ignoreEmpty || p > start) {
|
|
out.push_back(string(start, p - start));
|
|
}
|
|
start = p + 1;
|
|
}
|
|
}
|
|
if (!ignoreEmpty || p > start) {
|
|
out.push_back(string(start, p - start));
|
|
}
|
|
}
|
|
|
|
void Util::replaceAll(string &s, const char *from, const char *to) {
|
|
assert(from && *from);
|
|
assert(to);
|
|
|
|
string::size_type lenFrom = strlen(from);
|
|
string::size_type lenTo = strlen(to);
|
|
for (string::size_type pos = s.find(from);
|
|
pos != string::npos;
|
|
pos = s.find(from, pos + lenTo)) {
|
|
s.replace(pos, lenFrom, to);
|
|
}
|
|
}
|
|
|
|
std::string Util::toLower(const std::string &s) {
|
|
unsigned int len = s.size();
|
|
string ret;
|
|
if (len) {
|
|
ret.reserve(len);
|
|
for (unsigned int i = 0; i < len; i++) {
|
|
ret += tolower(s[i]);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string Util::toUpper(const std::string &s) {
|
|
unsigned int len = s.size();
|
|
string ret;
|
|
ret.reserve(len);
|
|
for (unsigned int i = 0; i < len; i++) {
|
|
ret += toupper(s[i]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string Util::getIdentifier(const std::string &fileName) {
|
|
string ret = "hphp_" + fileName;
|
|
replaceAll(ret, "/", "__");
|
|
replaceAll(ret, ".", "__");
|
|
replaceAll(ret, "-", "__");
|
|
return ret;
|
|
}
|
|
|
|
bool Util::mkdir(const std::string &path, int mode /* = 0777 */) {
|
|
if (path.empty()) {
|
|
return false;
|
|
}
|
|
size_t pos = path.rfind('/');
|
|
if (pos != string::npos) {
|
|
// quick test whole path exists
|
|
if (access(path.substr(0, pos).c_str(), F_OK) >= 0) {
|
|
return true;
|
|
}
|
|
for (pos = path.find('/'); pos != string::npos;
|
|
pos = path.find('/', pos + 1)) {
|
|
string subpath = path.substr(0, pos);
|
|
if (subpath.empty()) continue;
|
|
if (access(subpath.c_str(), F_OK) < 0 &&
|
|
::mkdir(subpath.c_str(), mode) < 0) {
|
|
Logger::Error("unable to mkdir %s", subpath.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool same(const char *file1, const char *file2) {
|
|
FILE *f1 = fopen(file1, "r");
|
|
if (f1 == nullptr) {
|
|
Logger::Error("unable to read %s", file1);
|
|
return false;
|
|
}
|
|
FILE *f2 = fopen(file2, "r");
|
|
if (f2 == nullptr) {
|
|
fclose(f1);
|
|
Logger::Error("unable to read %s", file2);
|
|
return false;
|
|
}
|
|
|
|
bool ret = false;
|
|
char buf1[8192];
|
|
char buf2[sizeof(buf1)];
|
|
int n1;
|
|
while ((n1 = fread(buf1, 1, sizeof(buf1), f1))) {
|
|
int toread = n1;
|
|
int pos = 0;
|
|
while (toread) {
|
|
int n2 = fread(buf2 + pos, 1, toread, f2);
|
|
if (n2 <= 0) {
|
|
goto exit_false;
|
|
}
|
|
toread -= n2;
|
|
pos += n2;
|
|
}
|
|
if (memcmp(buf1, buf2, n1) != 0) {
|
|
goto exit_false;
|
|
}
|
|
}
|
|
if (fread(buf2, 1, 1, f2) == 0) {
|
|
ret = true;
|
|
}
|
|
exit_false:
|
|
fclose(f2);
|
|
fclose(f1);
|
|
return ret;
|
|
}
|
|
|
|
void Util::syncdir(const std::string &dest_, const std::string &src_,
|
|
bool keepSrc /* = false */) {
|
|
if (src_.empty() || dest_.empty()) return;
|
|
|
|
string src = src_;
|
|
if (src[src.size() - 1] != '/') src += '/';
|
|
string dest = dest_;
|
|
if (dest[dest.size() - 1] != '/') dest += '/';
|
|
|
|
DIR *ddest = opendir(dest.c_str());
|
|
if (ddest == nullptr) {
|
|
Logger::Error("syncdir: unable to open dest %s", dest.c_str());
|
|
return;
|
|
}
|
|
|
|
DIR *dsrc = opendir(src.c_str());
|
|
if (dsrc == nullptr) {
|
|
closedir(ddest);
|
|
Logger::Error("syncdir: unable to open src %s", src.c_str());
|
|
return;
|
|
}
|
|
|
|
dirent *e;
|
|
|
|
std::set<string> todelete;
|
|
while ((e = readdir(ddest))) {
|
|
if (strcmp(e->d_name, ".") == 0 ||
|
|
strcmp(e->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
string fsrc = src + e->d_name;
|
|
string fdest = dest + e->d_name;
|
|
|
|
// delete files/directories that are only in dest
|
|
if (access(fsrc.c_str(), F_OK) < 0) {
|
|
size_t pos = fdest.rfind('.');
|
|
if (pos != string::npos) {
|
|
string ext = fdest.substr(pos + 1);
|
|
// do not delete intermediate files
|
|
if (ext == "o" || ext == "d") {
|
|
continue;
|
|
}
|
|
}
|
|
todelete.insert(fdest);
|
|
continue;
|
|
}
|
|
|
|
// delete mismatched types so to copy over new ones
|
|
struct stat sb1, sb2;
|
|
stat(fsrc.c_str(), &sb1);
|
|
stat(fdest.c_str(), &sb2);
|
|
if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT)) {
|
|
todelete.insert(fdest.c_str());
|
|
continue;
|
|
}
|
|
|
|
// updates
|
|
if ((sb1.st_mode & S_IFMT) == S_IFDIR) {
|
|
syncdir(fdest, fsrc);
|
|
} else if (sb1.st_size != sb2.st_size ||
|
|
!same(fsrc.c_str(), fdest.c_str())) {
|
|
todelete.insert(fdest);
|
|
}
|
|
}
|
|
|
|
// delete the ones to delete
|
|
if (!todelete.empty()) {
|
|
for (std::set<string>::const_iterator iter = todelete.begin();
|
|
iter != todelete.end(); ++iter) {
|
|
Logger::Info("sync: deleting %s", iter->c_str());
|
|
boost::filesystem::remove_all(*iter);
|
|
}
|
|
}
|
|
|
|
// insert new ones
|
|
while ((e = readdir(dsrc))) {
|
|
string fdest = dest + e->d_name;
|
|
if (access(fdest.c_str(), F_OK) < 0) {
|
|
Logger::Info("sync: updating %s", fdest.c_str());
|
|
if (keepSrc) {
|
|
ssystem((string("cp -R ") + src + e->d_name + " " + dest).c_str());
|
|
} else {
|
|
rename((src + e->d_name).c_str(), (dest + e->d_name).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dsrc);
|
|
closedir(ddest);
|
|
}
|
|
|
|
int Util::copy(const char *srcfile, const char *dstfile) {
|
|
int srcFd = open(srcfile, O_RDONLY);
|
|
if (srcFd == -1) return -1;
|
|
int dstFd = open(dstfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (dstFd == -1) return -1;
|
|
|
|
while (1) {
|
|
char buf[8192];
|
|
bool err = false;
|
|
ssize_t rbytes = read(srcFd, buf, sizeof(buf));
|
|
ssize_t wbytes;
|
|
if (rbytes == 0) break;
|
|
if (rbytes == -1) {
|
|
err = true;
|
|
Logger::Error("read failed: %s", safe_strerror(errno).c_str());
|
|
} else if ((wbytes = write(dstFd, buf, rbytes)) != rbytes) {
|
|
err = true;
|
|
Logger::Error("write failed: %d, %s", wbytes,
|
|
safe_strerror(errno).c_str());
|
|
}
|
|
if (err) {
|
|
close(srcFd);
|
|
close(dstFd);
|
|
return -1;
|
|
}
|
|
}
|
|
close(srcFd);
|
|
close(dstFd);
|
|
return 0;
|
|
}
|
|
|
|
static int force_sync(int fd) {
|
|
#if defined(__FreeBSD__) || defined(__APPLE__)
|
|
return fsync(fd);
|
|
#else
|
|
return fdatasync(fd);
|
|
#endif
|
|
}
|
|
|
|
int Util::drop_cache(int fd, off_t len /* = 0 */) {
|
|
#if defined(__FreeBSD__) || defined(__APPLE__)
|
|
return 0;
|
|
#else
|
|
return posix_fadvise(fd, 0, len, POSIX_FADV_DONTNEED);
|
|
#endif
|
|
}
|
|
|
|
int Util::drop_cache(FILE *f, off_t len /* = 0 */) {
|
|
return drop_cache(fileno(f), len);
|
|
}
|
|
|
|
int Util::directCopy(const char *srcfile, const char *dstfile) {
|
|
int srcFd = open(srcfile, O_RDONLY);
|
|
if (srcFd == -1) return -1;
|
|
int dstFd = open(dstfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (dstFd == -1) return -1;
|
|
|
|
#if defined(__APPLE__)
|
|
fcntl(srcFd, F_NOCACHE, 1);
|
|
fcntl(dstFd, F_NOCACHE, 1);
|
|
#endif
|
|
|
|
while (1) {
|
|
char buf[1 << 20];
|
|
bool err = false;
|
|
ssize_t rbytes = read(srcFd, buf, sizeof(buf));
|
|
ssize_t wbytes;
|
|
if (rbytes == 0) break;
|
|
|
|
if (rbytes == -1) {
|
|
err = true;
|
|
Logger::Error("read failed: %s", safe_strerror(errno).c_str());
|
|
} else if (force_sync(srcFd) == -1) {
|
|
err = true;
|
|
Logger::Error("read sync failed: %s",
|
|
safe_strerror(errno).c_str());
|
|
} else if (drop_cache(srcFd) == -1) {
|
|
err = true;
|
|
Logger::Error("read cache drop failed: %s",
|
|
safe_strerror(errno).c_str());
|
|
} else if ((wbytes = write(dstFd, buf, rbytes)) != rbytes) {
|
|
err = true;
|
|
Logger::Error("write failed: %d, %s", wbytes,
|
|
safe_strerror(errno).c_str());
|
|
} else if (force_sync(dstFd) == -1) {
|
|
err = true;
|
|
Logger::Error("write sync failed: %s",
|
|
safe_strerror(errno).c_str());
|
|
} else if (drop_cache(dstFd) == -1) {
|
|
err = true;
|
|
Logger::Error("write cache drop failed: %s",
|
|
safe_strerror(errno).c_str());
|
|
}
|
|
if (err) {
|
|
close(srcFd);
|
|
close(dstFd);
|
|
return -1;
|
|
}
|
|
}
|
|
close(srcFd);
|
|
close(dstFd);
|
|
return 0;
|
|
}
|
|
|
|
int Util::rename(const char *oldname, const char *newname) {
|
|
int ret = ::rename(oldname, newname);
|
|
if (ret == 0) return 0;
|
|
if (errno != EXDEV) return -1;
|
|
|
|
copy(oldname, newname);
|
|
unlink(oldname);
|
|
return 0;
|
|
}
|
|
|
|
int Util::directRename(const char *oldname, const char *newname) {
|
|
int ret = ::rename(oldname, newname);
|
|
if (ret == 0) return 0;
|
|
if (errno != EXDEV) return -1;
|
|
|
|
directCopy(oldname, newname);
|
|
unlink(oldname);
|
|
return 0;
|
|
}
|
|
|
|
int Util::ssystem(const char* command) {
|
|
int ret = system(command);
|
|
if (ret == -1) {
|
|
Logger::Error("system(\"%s\"): %s", command, safe_strerror(errno).c_str());
|
|
} else if (ret != 0) {
|
|
Logger::Error("command failed: \"%s\"", command);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string Util::safe_strerror(int errnum) {
|
|
char buf[1024];
|
|
#ifdef __GLIBC__
|
|
return strerror_r(errnum, buf, sizeof(buf));
|
|
#else
|
|
strerror_r(errnum, buf, sizeof(buf));
|
|
return buf;
|
|
#endif
|
|
}
|
|
|
|
size_t Util::dirname_helper(char *path, int len) {
|
|
if (len == 0) {
|
|
/* Illegal use of this function */
|
|
return 0;
|
|
}
|
|
|
|
/* Strip trailing slashes */
|
|
register char *end = path + len - 1;
|
|
while (end >= path && *end == '/') {
|
|
end--;
|
|
}
|
|
if (end < path) {
|
|
/* The path only contained slashes */
|
|
path[0] = '/';
|
|
path[1] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/* Strip filename */
|
|
while (end >= path && *end != '/') {
|
|
end--;
|
|
}
|
|
if (end < path) {
|
|
/* No slash found, therefore return '.' */
|
|
path[0] = '.';
|
|
path[1] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/* Strip slashes which came before the file name */
|
|
while (end >= path && *end == '/') {
|
|
end--;
|
|
}
|
|
if (end < path) {
|
|
path[0] = '/';
|
|
path[1] = '\0';
|
|
return 1;
|
|
}
|
|
*(end+1) = '\0';
|
|
|
|
return end + 1 - path;
|
|
}
|
|
|
|
std::string Util::safe_dirname(const char *path, int len) {
|
|
char* tmp_path = (char*)malloc(len+1);
|
|
memcpy(tmp_path, path, len);
|
|
tmp_path[len] = '\0';
|
|
size_t newLen = dirname_helper(tmp_path, len);
|
|
std::string ret;
|
|
ret.assign(tmp_path, newLen);
|
|
free((void *) tmp_path);
|
|
return ret;
|
|
}
|
|
|
|
std::string Util::safe_dirname(const char *path) {
|
|
int len = strlen(path);
|
|
return safe_dirname(path, len);
|
|
}
|
|
|
|
std::string Util::safe_dirname(const std::string& path) {
|
|
return safe_dirname(path.c_str(), path.size());
|
|
}
|
|
|
|
std::string Util::relativePath(const std::string fromDir,
|
|
const std::string toFile) {
|
|
|
|
size_t maxlen = (fromDir.size() + toFile.size()) * 3;
|
|
|
|
// Sanity checks
|
|
if (fromDir[0] != '/' || toFile[0] != '/' ||
|
|
fromDir[fromDir.size() - 1] != '/') {
|
|
return "";
|
|
}
|
|
|
|
// Maybe we're lucky and this is an easy case
|
|
int from_len = fromDir.size();
|
|
if (strncmp(toFile.c_str(), fromDir.c_str(), from_len) == 0) {
|
|
return toFile.substr(from_len);
|
|
}
|
|
|
|
char* path = (char*) malloc(maxlen);
|
|
int path_len = 0;
|
|
|
|
const char* from_dir = fromDir.c_str();
|
|
const char* to_file = toFile.c_str();
|
|
|
|
const char* from_start = from_dir + 1;
|
|
const char* to_start = to_file + 1;
|
|
|
|
while (true) {
|
|
int seg_len = 0;
|
|
char cur = from_start[0];
|
|
while (cur && cur != '/') {
|
|
++seg_len;
|
|
cur = from_start[seg_len];
|
|
}
|
|
|
|
if (memcmp(from_start, to_start, seg_len + 1)) {
|
|
break;
|
|
}
|
|
from_start += seg_len + 1;
|
|
to_start += seg_len + 1;
|
|
}
|
|
|
|
// Now to build the path
|
|
char cur = *from_start;
|
|
char* path_end = path;
|
|
while (cur) {
|
|
if (cur == '/') {
|
|
strcpy(path_end, "../");
|
|
path_len += 3;
|
|
maxlen -= 3;
|
|
path_end += 3;
|
|
}
|
|
++from_start;
|
|
cur = *from_start;
|
|
}
|
|
|
|
if (from_start[-1] != '/') {
|
|
strcpy(path_end, "../");
|
|
path_len += 3;
|
|
maxlen -= 3;
|
|
path_end += 3;
|
|
}
|
|
|
|
strncpy(path_end, to_start, maxlen - 1);
|
|
|
|
string p(path);
|
|
free((void*) path);
|
|
|
|
return p;
|
|
}
|
|
|
|
std::string Util::canonicalize(const std::string &path) {
|
|
const char *r = canonicalize(path.c_str(), path.size());
|
|
string res(r);
|
|
free((void*)r);
|
|
return res;
|
|
}
|
|
|
|
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You 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.
|
|
*/
|
|
|
|
const char *Util::canonicalize(const char *addpath, size_t addlen,
|
|
bool collapse_slashes /* = true */) {
|
|
assert(strlen(addpath) == addlen);
|
|
// 4 for slashes at start, after root, and at end, plus trailing
|
|
// null
|
|
size_t maxlen = addlen + 4;
|
|
size_t pathlen = 0; // is the length of the result path
|
|
size_t seglen; // is the end of the current segment
|
|
|
|
/* Treat null as an empty path.
|
|
*/
|
|
if (!addpath)
|
|
addpath = "";
|
|
|
|
char *path = (char *)malloc(maxlen);
|
|
|
|
if (addpath[0] == '/' && collapse_slashes) {
|
|
/* Ignore the given root path, strip off leading
|
|
* '/'s to a single leading '/' from the addpath,
|
|
* and leave addpath at the first non-'/' character.
|
|
*/
|
|
while (addpath[0] == '/')
|
|
++addpath;
|
|
path[0] = '/';
|
|
pathlen = 1;
|
|
}
|
|
|
|
while (*addpath) {
|
|
/* Parse each segment, find the closing '/'
|
|
*/
|
|
const char *next = addpath;
|
|
while (*next && (*next != '/')) {
|
|
++next;
|
|
}
|
|
seglen = next - addpath;
|
|
|
|
if (seglen == 0) {
|
|
/* / */
|
|
if (!collapse_slashes) {
|
|
path[pathlen++] = '/';
|
|
}
|
|
} else if (seglen == 1 && addpath[0] == '.') {
|
|
/* ./ */
|
|
} else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') {
|
|
/* backpath (../) */
|
|
if (pathlen == 1 && path[0] == '/') {
|
|
} else if (pathlen == 0
|
|
|| (pathlen == 3
|
|
&& !memcmp(path + pathlen - 3, "../", 3))
|
|
|| (pathlen > 3
|
|
&& !memcmp(path + pathlen - 4, "/../", 4))) {
|
|
/* Append another backpath, including
|
|
* trailing slash if present.
|
|
*/
|
|
memcpy(path + pathlen, "../", *next ? 3 : 2);
|
|
pathlen += *next ? 3 : 2;
|
|
} else {
|
|
/* otherwise crop the prior segment */
|
|
do {
|
|
--pathlen;
|
|
} while (pathlen && path[pathlen - 1] != '/');
|
|
}
|
|
} else {
|
|
/* An actual segment, append it to the destination path */
|
|
if (*next) {
|
|
seglen++;
|
|
}
|
|
memcpy(path + pathlen, addpath, seglen);
|
|
pathlen += seglen;
|
|
}
|
|
|
|
/* Skip over trailing slash to the next segment */
|
|
if (*next) {
|
|
++next;
|
|
}
|
|
|
|
addpath = next;
|
|
}
|
|
path[pathlen] = '\0';
|
|
return path;
|
|
}
|
|
|
|
std::string Util::normalizeDir(const std::string &dirname) {
|
|
string ret = Util::canonicalize(dirname);
|
|
if (!ret.empty() && ret[ret.length() - 1] != '/') {
|
|
ret += '/';
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string Util::escapeStringForCPP(const char *input, int len,
|
|
bool* binary /* = NULL */) {
|
|
if (binary) *binary = false;
|
|
string ret;
|
|
ret.reserve((len << 1) + 2);
|
|
for (int i = 0; i < len; i++) {
|
|
unsigned char ch = input[i];
|
|
switch (ch) {
|
|
case '\n': ret += "\\n"; break;
|
|
case '\r': ret += "\\r"; break;
|
|
case '\t': ret += "\\t"; break;
|
|
case '\a': ret += "\\a"; break;
|
|
case '\b': ret += "\\b"; break;
|
|
case '\f': ret += "\\f"; break;
|
|
case '\v': ret += "\\v"; break;
|
|
case '\0': ret += "\\000"; if (binary) *binary = true; break;
|
|
case '\"': ret += "\\\""; break;
|
|
case '\\': ret += "\\\\"; break;
|
|
case '?': ret += "\\?"; break; // avoiding trigraph errors
|
|
default:
|
|
if (isprint(ch)) {
|
|
ret += ch;
|
|
} else {
|
|
// output in octal notation
|
|
char buf[10];
|
|
snprintf(buf, sizeof(buf), "\\%03o", ch);
|
|
ret += buf;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string Util::escapeStringForPHP(const char *input, int len) {
|
|
string output;
|
|
output.reserve((len << 1) + 2);
|
|
output = "'";
|
|
for (int i = 0; i < len; i++) {
|
|
unsigned char ch = input[i];
|
|
switch (ch) {
|
|
case '\n': output += "'.\"\\n\".'"; break;
|
|
case '\r': output += "'.\"\\r\".'"; break;
|
|
case '\t': output += "'.\"\\t\".'"; break;
|
|
case '\'': output += "'.\"'\".'"; break;
|
|
case '\\': output += "'.\"\\\\\".'"; break;
|
|
default:
|
|
output += ch;
|
|
break;
|
|
}
|
|
}
|
|
output += "'";
|
|
replaceAll(output, ".''.", ".");
|
|
replaceAll(output, "''.", "");
|
|
replaceAll(output, ".''", "");
|
|
replaceAll(output, "\".\"", "");
|
|
return output;
|
|
}
|
|
|
|
const void *Util::buffer_duplicate(const void *src, int size) {
|
|
char *s = (char *)malloc(size + 1); // '\0' in the end
|
|
memcpy(s, src, size);
|
|
s[size] = '\0';
|
|
return s;
|
|
}
|
|
|
|
const void *Util::buffer_append(const void *buf1, int size1,
|
|
const void *buf2, int size2) {
|
|
char *s = (char *)realloc(const_cast<void *>(buf1), size1 + size2 + 1);
|
|
memcpy((char *)s + size1, buf2, size2);
|
|
s[size1 + size2] = '\0';
|
|
return s;
|
|
}
|
|
|
|
void Util::string_printf(std::string &msg, const char *fmt, ...) {
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
string_vsnprintf(msg, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void Util::string_vsnprintf(std::string &msg, const char *fmt, va_list ap) {
|
|
int i = 0;
|
|
for (int len = 1024; msg.empty(); len <<= 1) {
|
|
va_list v;
|
|
va_copy(v, ap);
|
|
|
|
char *buf = (char*)malloc(len);
|
|
if (vsnprintf(buf, len, fmt, v) < len) {
|
|
msg = buf;
|
|
}
|
|
free(buf);
|
|
|
|
va_end(v);
|
|
if (++i > 10) break;
|
|
}
|
|
}
|
|
|
|
void Util::find(std::vector<std::string> &out,
|
|
const std::string &root, const char *path, bool php,
|
|
const std::set<std::string> *excludeDirs /* = NULL */,
|
|
const std::set<std::string> *excludeFiles /* = NULL */) {
|
|
if (!path) path = "";
|
|
if (*path == '/') path++;
|
|
|
|
string spath = path;
|
|
if (spath.length() && spath[spath.length() - 1] != '/') {
|
|
spath += '/';
|
|
}
|
|
if (excludeDirs && excludeDirs->find(spath) != excludeDirs->end()) {
|
|
return;
|
|
}
|
|
|
|
string fullPath = root + path;
|
|
if (fullPath.empty()) {
|
|
return;
|
|
}
|
|
DIR *dir = opendir(fullPath.c_str());
|
|
if (dir == nullptr) {
|
|
Logger::Error("Util::find(): unable to open directory %s",
|
|
fullPath.c_str());
|
|
return;
|
|
}
|
|
if (fullPath[fullPath.length() - 1] != '/') {
|
|
fullPath += '/';
|
|
}
|
|
|
|
dirent *e;
|
|
while ((e = readdir(dir))) {
|
|
char *ename = e->d_name;
|
|
|
|
// skipping . .. hidden files
|
|
if (ename[0] == '.' || !*ename) {
|
|
continue;
|
|
}
|
|
string fe = fullPath + ename;
|
|
struct stat se;
|
|
if (stat(fe.c_str(), &se)) {
|
|
Logger::Error("Util::find(): unable to stat %s", fe.c_str());
|
|
continue;
|
|
}
|
|
|
|
if ((se.st_mode & S_IFMT) == S_IFDIR) {
|
|
string subdir = spath + ename;
|
|
find(out, root, subdir.c_str(), php, excludeDirs, excludeFiles);
|
|
continue;
|
|
}
|
|
|
|
// skipping "tags" files
|
|
if (strcmp(ename, "tags") == 0) {
|
|
continue;
|
|
}
|
|
|
|
// skipping emacs leftovers
|
|
char last = ename[strlen(ename) - 1];
|
|
if (last == '~' || last == '#') {
|
|
continue;
|
|
}
|
|
|
|
bool isPHP = false;
|
|
const char *p = strrchr(ename, '.');
|
|
if (p) {
|
|
isPHP = (strncmp(p + 1, "php", 3) == 0);
|
|
} else {
|
|
try {
|
|
string line;
|
|
std::ifstream fin(fe.c_str());
|
|
if (std::getline(fin, line)) {
|
|
if (line[0] == '#' && line[1] == '!' &&
|
|
line.find("php") != string::npos) {
|
|
isPHP = true;
|
|
}
|
|
}
|
|
} catch (...) {
|
|
Logger::Error("Util::find(): unable to read %s", fe.c_str());
|
|
}
|
|
}
|
|
|
|
if (isPHP == php &&
|
|
(!excludeFiles ||
|
|
excludeFiles->find(spath + ename) == excludeFiles->end())) {
|
|
out.push_back(fe);
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|
|
|
|
std::string Util::format_pattern(const std::string &pattern,
|
|
bool prefixSlash) {
|
|
if (pattern.empty()) return pattern;
|
|
|
|
std::string ret = "#";
|
|
for (unsigned int i = 0; i < pattern.size(); i++) {
|
|
char ch = pattern[i];
|
|
|
|
// apache rewrite rules don't require initial slash
|
|
if (prefixSlash && i == 0 && ch == '^') {
|
|
char ch1 = pattern[1];
|
|
if (ch1 != '/' && ch1 != '(') {
|
|
ret += "^/";
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (ch == '#') {
|
|
ret += "\\#";
|
|
} else {
|
|
ret += ch;
|
|
}
|
|
}
|
|
ret += '#';
|
|
return ret;
|
|
}
|
|
|
|
char* Util::getNativeFunctionName(void* codeAddr) {
|
|
void* buf[1] = {codeAddr};
|
|
char** symbols = backtrace_symbols(buf, 1);
|
|
char* functionName = nullptr;
|
|
if (symbols != nullptr) {
|
|
//
|
|
// the output from backtrace_symbols looks like this:
|
|
// ../path/hhvm/hhvm(_ZN4HPHP2VM6Transl17interpOneIterInitEv+0) [0x17cebe9]
|
|
//
|
|
// we first want to extract the mangled name from it to get this:
|
|
// _ZN4HPHP2VM6Transl17interpOneIterInitEv
|
|
//
|
|
// and then pass this to abi::__cxa_demangle to get the demanged name:
|
|
// HPHP::Transl::interpOneIterInit()
|
|
//
|
|
// Sometimes, though, backtrace_symbols can't find the function name
|
|
// and ends up giving us a blank managled name, like this:
|
|
// ../path/hhvm/hhvm() [0x17e4d01]
|
|
// or this: [0x7fffca800130]
|
|
//
|
|
char* start = strchr(*symbols, '(');
|
|
if (start) {
|
|
start++;
|
|
char* end = strchr(start, '+');
|
|
if (end != nullptr) {
|
|
size_t len = end-start;
|
|
functionName = new char[len+1];
|
|
strncpy(functionName, start, len);
|
|
functionName[len] = '\0';
|
|
int status;
|
|
char* demangledName = abi::__cxa_demangle(functionName, 0, 0, &status);
|
|
if (status == 0) {
|
|
delete []functionName;
|
|
functionName = demangledName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(symbols);
|
|
if (functionName == nullptr) {
|
|
#define MAX_ADDR_HEX_LEN 40
|
|
functionName = new char[MAX_ADDR_HEX_LEN + 3];
|
|
sprintf(functionName, "%40p", codeAddr);
|
|
}
|
|
return functionName;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|