blob: d8d7fb3c034a816ee4f6f6d3bfb4e67daa9e926d [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// 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.
#include "util.h"
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
#include <sys/time.h>
#endif
#include <vector>
#ifdef _WIN32
#include <direct.h> // _mkdir
#endif
#include "edit_distance.h"
#include "metrics.h"
void Fatal(const char* msg, ...) {
va_list ap;
fprintf(stderr, "ninja: FATAL: ");
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fprintf(stderr, "\n");
#ifdef _WIN32
// On Windows, some tools may inject extra threads.
// exit() may block on locks held by those threads, so forcibly exit.
fflush(stderr);
fflush(stdout);
ExitProcess(1);
#else
exit(1);
#endif
}
void Warning(const char* msg, ...) {
va_list ap;
fprintf(stderr, "ninja: WARNING: ");
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fprintf(stderr, "\n");
}
void Error(const char* msg, ...) {
va_list ap;
fprintf(stderr, "ninja: ERROR: ");
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fprintf(stderr, "\n");
}
bool CanonicalizePath(string* path, string* err) {
METRIC_RECORD("canonicalize str");
int len = path->size();
char* str = 0;
if (len > 0)
str = &(*path)[0];
if (!CanonicalizePath(str, &len, err))
return false;
path->resize(len);
return true;
}
bool CanonicalizePath(char* path, int* len, string* err) {
// WARNING: this function is performance-critical; please benchmark
// any changes you make to it.
METRIC_RECORD("canonicalize path");
if (*len == 0) {
*err = "empty path";
return false;
}
const int kMaxPathComponents = 30;
char* components[kMaxPathComponents];
int component_count = 0;
char* start = path;
char* dst = start;
const char* src = start;
const char* end = start + *len;
if (*src == '/') {
++src;
++dst;
}
while (src < end) {
if (*src == '.') {
if (src + 1 == end || src[1] == '/') {
// '.' component; eliminate.
src += 2;
continue;
} else if (src[1] == '.' && (src + 2 == end || src[2] == '/')) {
// '..' component. Back up if possible.
if (component_count > 0) {
dst = components[component_count - 1];
src += 3;
--component_count;
} else {
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
}
continue;
}
}
if (*src == '/') {
src++;
continue;
}
if (component_count == kMaxPathComponents)
Fatal("path has too many components");
components[component_count] = dst;
++component_count;
while (*src != '/' && src != end)
*dst++ = *src++;
*dst++ = *src++; // Copy '/' or final \0 character as well.
}
if (dst == start) {
*err = "path canonicalizes to the empty path";
return false;
}
*len = dst - start - 1;
return true;
}
int MakeDir(const string& path) {
#ifdef _WIN32
return _mkdir(path.c_str());
#else
return mkdir(path.c_str(), 0777);
#endif
}
int ReadFile(const string& path, string* contents, string* err) {
FILE* f = fopen(path.c_str(), "r");
if (!f) {
err->assign(strerror(errno));
return -errno;
}
char buf[64 << 10];
size_t len;
while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
contents->append(buf, len);
}
if (ferror(f)) {
err->assign(strerror(errno)); // XXX errno?
contents->clear();
fclose(f);
return -errno;
}
fclose(f);
return 0;
}
void SetCloseOnExec(int fd) {
#ifndef _WIN32
int flags = fcntl(fd, F_GETFD);
if (flags < 0) {
perror("fcntl(F_GETFD)");
} else {
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
perror("fcntl(F_SETFD)");
}
#else
HANDLE hd = (HANDLE) _get_osfhandle(fd);
if (! SetHandleInformation(hd, HANDLE_FLAG_INHERIT, 0)) {
fprintf(stderr, "SetHandleInformation(): %s", GetLastErrorString().c_str());
}
#endif // ! _WIN32
}
int64_t GetTimeMillis() {
#ifdef _WIN32
// GetTickCount64 is only available on Vista or later.
return GetTickCount();
#else
timeval now;
gettimeofday(&now, NULL);
return ((int64_t)now.tv_sec * 1000) + (now.tv_usec / 1000);
#endif
}
const char* SpellcheckStringV(const string& text,
const vector<const char*>& words) {
const bool kAllowReplacements = true;
const int kMaxValidEditDistance = 3;
int min_distance = kMaxValidEditDistance + 1;
const char* result = NULL;
for (vector<const char*>::const_iterator i = words.begin();
i != words.end(); ++i) {
int distance = EditDistance(*i, text, kAllowReplacements,
kMaxValidEditDistance);
if (distance < min_distance) {
min_distance = distance;
result = *i;
}
}
return result;
}
const char* SpellcheckString(const string& text, ...) {
va_list ap;
va_start(ap, text);
vector<const char*> words;
const char* word;
while ((word = va_arg(ap, const char*)))
words.push_back(word);
return SpellcheckStringV(text, words);
}
#ifdef _WIN32
string GetLastErrorString() {
DWORD err = GetLastError();
char* msg_buf;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(char*)&msg_buf,
0,
NULL);
string msg = msg_buf;
LocalFree(msg_buf);
return msg;
}
#endif
static bool islatinalpha(int c) {
// isalpha() is locale-dependent.
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
string StripAnsiEscapeCodes(const string& in) {
string stripped;
stripped.reserve(in.size());
for (size_t i = 0; i < in.size(); ++i) {
if (in[i] != '\33') {
// Not an escape code.
stripped.push_back(in[i]);
continue;
}
// Only strip CSIs for now.
if (i + 1 >= in.size()) break;
if (in[i + 1] != '[') continue; // Not a CSI.
i += 2;
// Skip everything up to and including the next [a-zA-Z].
while (i < in.size() && !islatinalpha(in[i]))
++i;
}
return stripped;
}
#ifdef _WIN32
static double GetLoadAverage_win32()
{
// TODO(nicolas.despres@gmail.com): Find a way to implement it on Windows.
return -0.0f;
}
#else
static double GetLoadAverage_unix()
{
double loadavg[3] = { 0.0f, 0.0f, 0.0f };
if (getloadavg(loadavg, 3) < 0)
{
// Maybe we should return an error here or the availability of
// getloadavg(3) should be checked when ninja is configured.
return -0.0f;
}
return loadavg[0];
}
#endif // _WIN32
double GetLoadAverage()
{
#ifdef _WIN32
return GetLoadAverage_win32();
#else
return GetLoadAverage_unix();
#endif // _WIN32
}