-
toole1 authored
git-svn-id: https://subversion.cs.illinois.edu/svn/cs225@4587 6fbd10e7-183d-0410-a318-cb416676e4f2
toole1 authoredgit-svn-id: https://subversion.cs.illinois.edu/svn/cs225@4587 6fbd10e7-183d-0410-a318-cb416676e4f2
util.cpp 17.43 KiB
// CS 225 util.h
// Created Spring 2011 by Jack Toole
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
#include "util.h"
extern char ** environ; // man 7 environ
namespace util
{
namespace internal
{
const char * error_prefix = "";
template<typename StrType>
void exit_if_error_output(const char * file, int32_t line, StrType message)
{
if (util::internal::error_prefix != NULL)
cerr << util::internal::error_prefix;
cerr << file << ":" << line << ": " << message;
if (errno != 0)
cerr << ": " << strerror(errno);
cerr << endl;
exit(-1);
}
}
void SET_ERROR_MESSAGE(const char * message)
{
internal::error_prefix = message;
}
// originally from stackoverflow.com user plinth
// http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c
// Modified by Jack Toole
int8_t exec(int redirect_fd, const char * command,
const char * arg1,
const char * arg2,
const char * arg3,
const char * arg4,
const char * arg5,
const char * arg6)
{
int childExitStatus;
pid_t pid;
// For debugging:
#if 0
cerr << "exec(" << command << ' '
<< ((arg1!=NULL)?arg1:"-") << ' '
<< ((arg2!=NULL)?arg2:"-") << ' '
<< ((arg3!=NULL)?arg3:"-") << ' '
<< ((arg4!=NULL)?arg4:"-") << ' '
<< ((arg5!=NULL)?arg5:"-") << ' '
<< ((arg6!=NULL)?arg6:"-") << ' '
<< ')' << endl;
#endif
// avoid self destruction errors from closing then trying to duplicate output
// you can't redirect to what's already there
if (redirect_fd == STDOUT_FILENO || redirect_fd == STDERR_FILENO)
redirect_fd = STDOUT_FILENO;
// Save timer values :)
// These are preserved across the parent, but not inherited by the child
// let's change that
struct itimerval remaining_real;
struct itimerval remaining_virtual;
struct itimerval remaining_prof;
bool supports_virtual = true;
bool supports_prof = true;
EXIT_IF_ERROR(getitimer(ITIMER_REAL, &remaining_real));
if (getitimer(ITIMER_VIRTUAL, &remaining_virtual) != 0)
{
if (errno == EINVAL)
{
supports_virtual = false;
errno = 0;
}
else
internal::exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_VIRTUAL) failed");
}
if (getitimer(ITIMER_PROF, &remaining_prof) != 0)
{
if (errno == EINVAL)
{
supports_prof = false;
errno = 0;
}
else
internal::exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_PROF) failed");
}
pid = fork();
if (pid == 0) /* child */
{
// Restore timers
EXIT_IF_ERROR(setitimer(ITIMER_REAL, &remaining_real, NULL));
if (supports_virtual) EXIT_IF_ERROR(setitimer(ITIMER_VIRTUAL, &remaining_virtual, NULL));
if (supports_prof) EXIT_IF_ERROR(setitimer(ITIMER_PROF, &remaining_prof, NULL));
if (redirect_fd == -1)
{
int devnull_fd = open("/dev/null", O_WRONLY | O_NONBLOCK);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup2(devnull_fd, STDOUT_FILENO);
dup2(devnull_fd, STDERR_FILENO);
close(devnull_fd);
}
else if (redirect_fd != STDOUT_FILENO)
{
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup2(redirect_fd, STDOUT_FILENO);
dup2(redirect_fd, STDERR_FILENO);
}
// Sanitize the environment
#if 1 //!! hack!
char path[] = "PATH=/bin/:/usr/bin:/usr/local/bin";
char redirect_glibc[] = "LIBC_FATAL_STDERR_=1";
char * newenv[] = { path, redirect_glibc, NULL };
//char * newenv[] = { path, NULL };
environ = newenv;
#endif
// Swap out child process image with the command, searching
// in the specified path
execlp(command, command, arg1, arg2, arg3, arg4, arg5, arg6, NULL);
// An error occured
cerr << "exec(" << '\"' << command << '\"';
if (arg1 != NULL) cerr << ", \"" << arg1 << "\"";
if (arg2 != NULL) cerr << ", \"" << arg2 << "\"";
if (arg3 != NULL) cerr << ", \"" << arg3 << "\"";
if (arg4 != NULL) cerr << ", \"" << arg4 << "\"";
if (arg5 != NULL) cerr << ", \"" << arg5 << "\"";
if (arg6 != NULL) cerr << ", \"" << arg6 << "\"";
cerr << ") failed: " << strerror(errno) << endl;
exit(-1);
}
else if (pid < 0)
{
/* error - couldn't start process - you decide how to handle */
return -1;
}
else
{
/* parent - wait for child - this has all error handling, you
* could just call wait() as long as you are only expecting to
* have one child process at a time.
*/
pid_t ws = waitpid( pid, &childExitStatus, 0);
if (ws == -1)
{ /* error - handle as you wish */
//cout << "exec error: " << __LINE__ << endl;
return -1;
}
if (WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
{
int8_t status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
/* handle non-zero as you wish */
return status;
}
else if (WIFSIGNALED(childExitStatus)) /* killed */
{
kill(getpid(), WTERMSIG(childExitStatus));
return -1;
}
else if (WIFSTOPPED(childExitStatus)) /* stopped */
{
//cout << "exec error: " << __LINE__ << endl;
return -1;
}
else
return -1;
}
}
int chdir(const string & dir)
{
return ::chdir(dir.c_str());
}
void assertExists(const string & path, int exit_code /* = -1 */)
{
if (!exists(path))
{
cerr << "Error: " << path << " does not exist." << endl;
exit(exit_code);
}
}
bool exists(const string & path)
{
// Try stat-ing it
struct stat st;
if (stat(path.c_str(), &st) != 0) return false;
// Check for read permission
if ((st.st_mode & S_IRUSR) == 0) return false;
// Check for correct file/directory nature
if (path[path.length()-1] != '/') return S_ISREG(st.st_mode);
// Otherwise we want a directory
if ((st.st_mode & S_IXUSR) == 0) return false;
return S_ISDIR(st.st_mode);
}
mode_t permissions(const string & path)
{
// Try stat-ing it
struct stat st;
if (stat(path.c_str(), &st) != 0) return -1;
// Check for read permission
if ((st.st_mode & S_IRUSR) == 0) return -1;
return (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
}
void forceRemoveDir(string dir)
{
size_t len = dir.length();
if (dir[len-1] == '/') dir[len-1] = '\0';
EXIT_IF_ERROR(exec("rm","-rf",dir.c_str()) != 0);
if (dir[len-1] == '\0') dir[len-1] = '/';
}
string getcwdstr()
{
int len = 256;
char * buffer = new char[len];
char * ret = getcwd(&buffer[0], len - 1);
while (ret == NULL && errno == ERANGE)
{
len *= 2;
delete buffer;
buffer = new char[len];
ret = getcwd(&buffer[0], len - 1);
}
EXIT_IF_ERROR(ret == NULL);
string cwdstr(buffer);
delete buffer;
return cwdstr;
}
void copyFile(const string & source, const string & dest)
{
assertExists(source);
vector<string> folders = tokenize(dest, '/');
string currdir = "";
for (size_t i = 0; i < folders.size() - 1; i++)
{
currdir += folders[i] + '/';
if (!exists(currdir))
exec("mkdir", currdir.c_str());
}
exec("cp", source.c_str(), dest.c_str());
}
void copyFiles(const string & sourceFolder, const string & destFolder, const vector<string> & files)
{
assertExists(destFolder);
for (size_t i = 0; i < files.size(); i++)
{
string sourceFile = sourceFolder + files[i];
assertExists(sourceFile);
copyFile(sourceFile, destFolder);
}
}
void protectFiles(const string & folder, const vector<string> & files)
{
#if 0 // (debug)
for (size_t i = 0; i < files.size(); i++)
{
string file = folder + files[i];
assertExists(file);
if (chmod(file.c_str(), S_IRUSR) != 0)
{
perror("chmod failed");
exit(-1);
}
}
#endif
}
void protectDir(const string & dir)
{
// (debug) EXIT_IF_ERROR(exec("/bin/chmod", "-R", "ugoa-w", dir.c_str()) != 0);
}
// Originally from Simon Biber
// http://bytes.com/topic/c/answers/545614-list-files-current-directory
vector<string> get_files_in_dir(const string & dir)
{
EXIT_IF_ERROR(dir == "" || dir[dir.length()-1] != '/', "Directory name must end in a '/'");
vector<string> files;
DIR * dirp = opendir(dir.c_str());
if (dirp == NULL) return files;
struct dirent * ent = readdir(dirp);
while (ent != NULL)
{
string file = ent->d_name;
if (file != "." && file != "..")
files.push_back(dir + file);
ent = readdir(dirp);
}
closedir(dirp);
return files;
}
bool is_symlink(const string & file)
{
// Try lstat-ing it
struct stat st;
if (lstat(file.c_str(), &st) != 0) return -1;
// Check for read permission
if ((st.st_mode & S_IRUSR) == 0) return false;
// & with symlink bit
return (S_ISLNK(st.st_mode)) != 0;
}
string get_symlink_target(const string & symlink)
{
const size_t buf_size = 4096;
char buf[buf_size+1]; // TODO (toole1): hack-y value
ssize_t len = readlink(symlink.c_str(), buf, buf_size);
EXIT_IF_ERROR(len < 0 || static_cast<size_t>(len) == buf_size, "Error getting target of symlink " + symlink);
buf[len] = '\0';
return string(buf);
}
void linkDirs(const string & sourceFolder, const string & destFolder, const vector<string> & dirs)
{
assertExists(destFolder);
for (size_t i = 0; i < dirs.size(); i++)
{
string source = sourceFolder + dirs[i];
string target = destFolder + dirs[i];
// Check for redundant monad/ directory
// This allows the monad/ dir to be safely renamed
if (replaceFirst(source, "/../monad/","/"))
replaceFirst(target, "/monad/","/");
assertExists(destFolder + source + '/');
if (symlink(source.c_str(), target.c_str()) != 0)
{
cerr << "symlink failed: " << target << ": ";
perror(NULL);
exit(-1);
}
}
}
bool replaceFirst(string & str, const string & toreplace, const string & with)
{
size_t i = str.find(toreplace);
if (i != string::npos)
{
str.replace(i,toreplace.length(),with);
return true;
}
return false;
}
size_t replaceAll(string & str, const string & toreplace, const string & with)
{
size_t i = str.find(toreplace);
size_t count = 0;
while (i != string::npos)
{
str.replace(i,toreplace.length(),with);
i = str.find(toreplace, i + with.length());
count++;
}
return count;
}
size_t replaceAllInternal(string & str, const string & toreplace, const string & with)
{
size_t i = str.find(toreplace);
size_t count = 0;
while ((i != string::npos) && (i != str.length() - toreplace.length()))
{
str.replace(i,toreplace.length(),with);
i = str.find(toreplace, i + with.length());
count++;
}
return count;
}
size_t findNthLast(const string & str, char c, size_t n)
{
if (str.length() == 0) return string::npos;
size_t i = str.length() - 1;
do
{
if (str[i] == c) n--;
if (n == 0) return i;
} while (i-- != 0);
return string::npos;
}
string read_string_from_FILE(FILE * file, size_t max_length /* = -1 */)
{
vector<char> v;
v.reserve(256);
while (true)
{
int nextchar = fgetc(file);
if (nextchar == '\0' || nextchar == EOF)
break;
if (v.size() < max_length)
v.push_back(nextchar);
}
if (v.size() == max_length)
{
v.push_back('.');
v.push_back('.');
v.push_back('.');
}
v.push_back('\0');
return string(&v[0]);
}
void write_string_to_FILE(FILE * file, const char * str)
{
fflush(file);
size_t i = 0;
do
{
// cout << (int)str[i] << ' ';
fputc(str[i], file);
// We use a do-while because we want the \0 to be written to the stream
// for sending multiple strings
} while (str[i++] != 0);
// cout << endl;
fflush(file);
}
/**
*
*
**/
string readFile(const string & filename)
{
ifstream file;
file.open(filename.c_str());
if (!file.good())
return "";
stringbuf linebuf;
file.get(linebuf, '\0');
linebuf.pubsync();
return linebuf.str();
}
void readConfig(const string & testsFolder, FileMap & map, const string & discriminator /* = "" */)
{
string file;
if (testsFolder == "" || testsFolder[testsFolder.length()-1] == '/')
file = testsFolder + "config.ini";
else
file = testsFolder;
readFileGeneric(file, &map, NULL, discriminator);
}
void readFile(const string & file, vector<string> & lines)
{
readFileGeneric(file, NULL, &lines);
}
void readFileGeneric(const string & filename, FileMap * map, vector<string> * lines, const string & discriminator /* = "" */)
{
ifstream infile;
istream * fileptr;
if (filename == "/dev/stdin")
fileptr = &cin;
else
{
fileptr = &infile;
infile.open(filename.c_str(), fstream::in);
}
istream & file = *fileptr;
vector<string> * section = NULL;
if (map != NULL) section = &(*map)[""];
else section = lines;
while ((file.good() && file.peek() == '\n') || file.peek() == '\r')
file.get(); // get '\n'
while (file.good())
{
// Read a line - A lot of code, I know, right?
stringbuf linebuf;
file.get(linebuf);
while ((file.good() && file.peek() == '\n') || file.peek() == '\r')
file.get(); // get '\n'
if (linebuf.in_avail() == 0) continue;
linebuf.pubsync();
string line = linebuf.str();
int len = line.length();
if (line[len-1] == '\r')
line.replace(--len,1,"");
if (len == 0 || line[0] == ';') continue; // skip comments
if (map != NULL)
{
// Update the section
if (line[0] == '[' && line[len-1] == ']')
{
section = &(*map)[line.substr(1, len - 2)];
continue;
}
else if (line[0] == '[' || line[len-1] == ']')
{
cout << "config.ini: Format error: " << line << endl;
exit(-1);
}
}
// Or add the line/file to the section
size_t delim_pos = line.find_first_of("?:");
if (delim_pos == string::npos || map == NULL)
section->push_back(line);
else if ((line[delim_pos] == ':' && (delim_pos == 0 || discriminator == "")) ||
line.compare(0, delim_pos, discriminator) == 0)
section->push_back(line.substr(delim_pos+1, line.size()-delim_pos-1));
}
if (filename != "/dev/stdin")
infile.close();
}
char * processOptions(int argc, char ** argv, OptionsMap & opts, vector<string> & args)
{
for (int arg_i = 1; arg_i < argc; arg_i++)
{
char * currarg = argv[arg_i];
if (strncmp(currarg, "--", 2) == 0) //long option
{
bool value, invert;
size_t string_i;
if (strncasecmp(currarg+2, "no", 2) == 0)
{
invert = true;
string_i = 4;
}
else
{
invert = false;
string_i = 2;
}
size_t equals_i = string_i;
while (currarg[equals_i] != '\0' && currarg[equals_i] != '=')
equals_i++;
if (currarg[equals_i] == '=')
currarg[equals_i++] = '\0';
OptionsMap::iterator option = opts.find(currarg+string_i);
if (option == opts.end())
{
cerr << "Unknown option: " << currarg << endl;
return currarg;
}
char valuechar = tolower(currarg[equals_i]);
if (valuechar == 'o') valuechar = tolower(currarg[equals_i+1]) + 1;
switch (valuechar)
{
case '\0' : value = true; break;
case 'f'+1 : value = false; break; //off: see 'o' above
case 'n' : value = false; break;
case 'n'+1 : value = true; break; //on: contrast 'n': see 'o' above
case 't' : value = true; break;
case 'y' : value = true; break;
default:
cerr << "Unknown option value: " << currarg << endl;
return currarg;
}
(*option).second = value ^ invert;
} // "--"
else if (currarg[0] == '-') //string of single char options
{
for (size_t c = 1; currarg[c] != '\0'; c++)
{
OptionsMap::iterator option = opts.find(string(1,currarg[c]));
if (option == opts.end())
{
cerr << "Unknown option: -" << currarg[c] << endl;
currarg[1] = currarg[c];
currarg[2] = '\0';
return currarg;
}
(*option).second = true;
}
}
else //positional argument
args.push_back(currarg);
}
return NULL;
}
void makeLower(string & str)
{
for (size_t i = 0; i < str.length(); i++)
{
str[i] = tolower(str[i]);
}
}
string toLower(const string & str)
{
string s(str);
makeLower(s);
return s;
}
/**
* A wrapper function which writes a buffer to a file.
**/
ssize_t writeBytesToFile(signed int fileDescriptor, const char * buffer, unsigned int bufferLength) {
return writen(fileDescriptor, buffer, bufferLength);
}
// From Steven's Unix Net Programming
// http://www.kohala.com/start/
/* Write "n" bytes to a descriptor. */
ssize_t writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const int8_t * ptr;
ptr = static_cast<const int8_t*>(vptr);
nleft = n;
while (nleft > 0) {
if ( (nwritten = ::write(fd, ptr, nleft)) <= 0) {
if (errno == EINTR)
nwritten = 0; /* and call write() again */
else
return -1; /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return n;
}
// From Steven's Unix Net Programming
// http://www.kohala.com/start/
/* Read "n" bytes from a descriptor. */
ssize_t readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
int8_t * ptr;
ptr = static_cast<int8_t*>(vptr);
nleft = n;
while (nleft > 0) {
if ( (nread = ::read(fd, ptr, nleft)) < 0) {
if (errno == EINTR)
nread = 0; /* and call read() again */
else
return -1;
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return n - nleft; /* return >= 0 */
}
void rename_main(const string & file, const string & newname)
{
if (newname[0] != '_')
{
cerr << "INTERNAL ERROR: " __FILE__ << ":" << __LINE__
<< "newname argument to rename_main must begin with an underscore to ensure replacement safety" << endl;
exit(-2);
}
assertExists(file);
exec( "sed", "-i",
( "s/int[\\r\\n \\t][\\r\\n \\t]*main(.*)/int " + newname +
"(int argc, char ** argv)/" ).c_str(),
file.c_str() );
}
vector<string> tokenize(const string & str, char delim)
{
vector<string> args;
size_t start = 0;
size_t end;
for (end = str.find(delim); end != string::npos; end = str.find(delim, start))
{
args.push_back(str.substr(start, end - start));
start = end+1;
}
args.push_back(str.substr(start, str.size() - start));
return args;
}
} // namespace util