More work on getting dfhack building & compiling on Mac OS X
parent
44c3afc306
commit
1dd4cc5667
@ -0,0 +1,773 @@
|
|||||||
|
/*
|
||||||
|
https://github.com/peterix/dfhack
|
||||||
|
|
||||||
|
A thread-safe logging console with a line editor.
|
||||||
|
|
||||||
|
Based on linenoise:
|
||||||
|
linenoise -- guerrilla line editing library against the idea that a
|
||||||
|
line editing lib needs to be 20,000 lines of C code.
|
||||||
|
|
||||||
|
You can find the latest source code at:
|
||||||
|
|
||||||
|
http://github.com/antirez/linenoise
|
||||||
|
|
||||||
|
Does a number of crazy assumptions that happen to be true in 99.9999% of
|
||||||
|
the 2010 UNIX computers around.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||||
|
Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||||
|
Copyright (c) 2011, Petr Mrázek <peterix@gmail.com>
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
// George Vulov for MacOSX
|
||||||
|
#ifndef __LINUX__
|
||||||
|
#define TEMP_FAILURE_RETRY(expr) \
|
||||||
|
({ long int _res; \
|
||||||
|
do _res = (long int) (expr); \
|
||||||
|
while (_res == -1L && errno == EINTR); \
|
||||||
|
_res; })
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Console.h"
|
||||||
|
#include "Hooks.h"
|
||||||
|
using namespace DFHack;
|
||||||
|
|
||||||
|
#include "tinythread.h"
|
||||||
|
using namespace tthread;
|
||||||
|
|
||||||
|
static int isUnsupportedTerm(void)
|
||||||
|
{
|
||||||
|
static const char *unsupported_term[] = {"dumb","cons25",NULL};
|
||||||
|
char *term = getenv("TERM");
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (term == NULL) return 0;
|
||||||
|
for (j = 0; unsupported_term[j]; j++)
|
||||||
|
if (!strcasecmp(term,unsupported_term[j])) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * ANSI_CLS = "\033[2J";
|
||||||
|
const char * ANSI_BLACK = "\033[22;30m";
|
||||||
|
const char * ANSI_RED = "\033[22;31m";
|
||||||
|
const char * ANSI_GREEN = "\033[22;32m";
|
||||||
|
const char * ANSI_BROWN = "\033[22;33m";
|
||||||
|
const char * ANSI_BLUE = "\033[22;34m";
|
||||||
|
const char * ANSI_MAGENTA = "\033[22;35m";
|
||||||
|
const char * ANSI_CYAN = "\033[22;36m";
|
||||||
|
const char * ANSI_GREY = "\033[22;37m";
|
||||||
|
const char * ANSI_DARKGREY = "\033[01;30m";
|
||||||
|
const char * ANSI_LIGHTRED = "\033[01;31m";
|
||||||
|
const char * ANSI_LIGHTGREEN = "\033[01;32m";
|
||||||
|
const char * ANSI_YELLOW = "\033[01;33m";
|
||||||
|
const char * ANSI_LIGHTBLUE = "\033[01;34m";
|
||||||
|
const char * ANSI_LIGHTMAGENTA = "\033[01;35m";
|
||||||
|
const char * ANSI_LIGHTCYAN = "\033[01;36m";
|
||||||
|
const char * ANSI_WHITE = "\033[01;37m";
|
||||||
|
const char * RESETCOLOR = "\033[0m";
|
||||||
|
|
||||||
|
const char * getANSIColor(const int c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case -1: return RESETCOLOR; // HACK! :P
|
||||||
|
case 0 : return ANSI_BLACK;
|
||||||
|
case 1 : return ANSI_BLUE; // non-ANSI
|
||||||
|
case 2 : return ANSI_GREEN;
|
||||||
|
case 3 : return ANSI_CYAN; // non-ANSI
|
||||||
|
case 4 : return ANSI_RED; // non-ANSI
|
||||||
|
case 5 : return ANSI_MAGENTA;
|
||||||
|
case 6 : return ANSI_BROWN;
|
||||||
|
case 7 : return ANSI_GREY;
|
||||||
|
case 8 : return ANSI_DARKGREY;
|
||||||
|
case 9 : return ANSI_LIGHTBLUE; // non-ANSI
|
||||||
|
case 10: return ANSI_LIGHTGREEN;
|
||||||
|
case 11: return ANSI_LIGHTCYAN; // non-ANSI;
|
||||||
|
case 12: return ANSI_LIGHTRED; // non-ANSI;
|
||||||
|
case 13: return ANSI_LIGHTMAGENTA;
|
||||||
|
case 14: return ANSI_YELLOW; // non-ANSI
|
||||||
|
case 15: return ANSI_WHITE;
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace DFHack
|
||||||
|
{
|
||||||
|
class Private
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Private()
|
||||||
|
{
|
||||||
|
dfout_C = NULL;
|
||||||
|
rawmode = false;
|
||||||
|
in_batch = false;
|
||||||
|
supported_terminal = false;
|
||||||
|
state = con_unclaimed;
|
||||||
|
};
|
||||||
|
virtual ~Private()
|
||||||
|
{
|
||||||
|
//sync();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
bool read_char(unsigned char & out)
|
||||||
|
{
|
||||||
|
FD_ZERO(&descriptor_set);
|
||||||
|
FD_SET(STDIN_FILENO, &descriptor_set);
|
||||||
|
FD_SET(exit_pipe[0], &descriptor_set);
|
||||||
|
int ret = TEMP_FAILURE_RETRY(
|
||||||
|
select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL)
|
||||||
|
);
|
||||||
|
if(ret == -1)
|
||||||
|
return false;
|
||||||
|
if (FD_ISSET(exit_pipe[0], &descriptor_set))
|
||||||
|
return false;
|
||||||
|
if (FD_ISSET(STDIN_FILENO, &descriptor_set))
|
||||||
|
{
|
||||||
|
// read byte from stdin
|
||||||
|
ret = TEMP_FAILURE_RETRY(
|
||||||
|
read(STDIN_FILENO, &out, 1)
|
||||||
|
);
|
||||||
|
if(ret == -1)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void print(const char *data)
|
||||||
|
{
|
||||||
|
fputs(data, dfout_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_text(color_ostream::color_value clr, const std::string &chunk)
|
||||||
|
{
|
||||||
|
if(!in_batch && state == con_lineedit)
|
||||||
|
{
|
||||||
|
disable_raw();
|
||||||
|
fprintf(dfout_C,"\x1b[1G");
|
||||||
|
fprintf(dfout_C,"\x1b[0K");
|
||||||
|
|
||||||
|
color(clr);
|
||||||
|
print(chunk.c_str());
|
||||||
|
|
||||||
|
reset_color();
|
||||||
|
enable_raw();
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
color(clr);
|
||||||
|
print(chunk.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_batch()
|
||||||
|
{
|
||||||
|
assert(!in_batch);
|
||||||
|
|
||||||
|
in_batch = true;
|
||||||
|
|
||||||
|
if (state == con_lineedit)
|
||||||
|
{
|
||||||
|
disable_raw();
|
||||||
|
fprintf(dfout_C,"\x1b[1G");
|
||||||
|
fprintf(dfout_C,"\x1b[0K");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_batch()
|
||||||
|
{
|
||||||
|
assert(in_batch);
|
||||||
|
|
||||||
|
flush();
|
||||||
|
|
||||||
|
in_batch = false;
|
||||||
|
|
||||||
|
if (state == con_lineedit)
|
||||||
|
{
|
||||||
|
reset_color();
|
||||||
|
enable_raw();
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
if (!rawmode)
|
||||||
|
fflush(dfout_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the console, along with its scrollback
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
if(rawmode)
|
||||||
|
{
|
||||||
|
const char * clr = "\033c\033[3J\033[H";
|
||||||
|
::write(STDIN_FILENO,clr,strlen(clr));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print("\033c\033[3J\033[H");
|
||||||
|
fflush(dfout_C);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Position cursor at x,y. 1,1 = top left corner
|
||||||
|
void gotoxy(int x, int y)
|
||||||
|
{
|
||||||
|
char tmp[64];
|
||||||
|
sprintf(tmp,"\033[%d;%dH", y,x);
|
||||||
|
print(tmp);
|
||||||
|
}
|
||||||
|
/// Set color (ANSI color number)
|
||||||
|
void color(Console::color_value index)
|
||||||
|
{
|
||||||
|
if(!rawmode)
|
||||||
|
fprintf(dfout_C,getANSIColor(index));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char * colstr = getANSIColor(index);
|
||||||
|
int lstr = strlen(colstr);
|
||||||
|
::write(STDIN_FILENO,colstr,lstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Reset color to default
|
||||||
|
void reset_color(void)
|
||||||
|
{
|
||||||
|
color(Console::COLOR_RESET);
|
||||||
|
if(!rawmode)
|
||||||
|
fflush(dfout_C);
|
||||||
|
}
|
||||||
|
/// Enable or disable the caret/cursor
|
||||||
|
void cursor(bool enable = true)
|
||||||
|
{
|
||||||
|
if(enable)
|
||||||
|
print("\033[?25h");
|
||||||
|
else
|
||||||
|
print("\033[?25l");
|
||||||
|
}
|
||||||
|
/// Waits given number of milliseconds before continuing.
|
||||||
|
void msleep(unsigned int msec);
|
||||||
|
/// get the current number of columns
|
||||||
|
int get_columns(void)
|
||||||
|
{
|
||||||
|
winsize ws;
|
||||||
|
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 80;
|
||||||
|
return ws.ws_col;
|
||||||
|
}
|
||||||
|
/// get the current number of rows
|
||||||
|
int get_rows(void)
|
||||||
|
{
|
||||||
|
winsize ws;
|
||||||
|
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 25;
|
||||||
|
return ws.ws_row;
|
||||||
|
}
|
||||||
|
/// beep. maybe?
|
||||||
|
//void beep (void);
|
||||||
|
/// A simple line edit (raw mode)
|
||||||
|
int lineedit(const std::string& prompt, std::string& output, recursive_mutex * lock, CommandHistory & ch)
|
||||||
|
{
|
||||||
|
output.clear();
|
||||||
|
reset_color();
|
||||||
|
this->prompt = prompt;
|
||||||
|
if (!supported_terminal)
|
||||||
|
{
|
||||||
|
print(prompt.c_str());
|
||||||
|
fflush(dfout_C);
|
||||||
|
// FIXME: what do we do here???
|
||||||
|
//SDL_recursive_mutexV(lock);
|
||||||
|
std::getline(std::cin, output);
|
||||||
|
//SDL_recursive_mutexP(lock);
|
||||||
|
return output.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
if (enable_raw() == -1) return 0;
|
||||||
|
if(state == con_lineedit)
|
||||||
|
return -1;
|
||||||
|
state = con_lineedit;
|
||||||
|
count = prompt_loop(lock,ch);
|
||||||
|
state = con_unclaimed;
|
||||||
|
disable_raw();
|
||||||
|
print("\n");
|
||||||
|
if(count != -1)
|
||||||
|
{
|
||||||
|
output = raw_buffer;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int enable_raw()
|
||||||
|
{
|
||||||
|
struct termios raw;
|
||||||
|
|
||||||
|
if (!supported_terminal)
|
||||||
|
return -1;
|
||||||
|
if (tcgetattr(STDIN_FILENO,&orig_termios) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
raw = orig_termios; //modify the original mode
|
||||||
|
// input modes: no break, no CR to NL, no parity check, no strip char,
|
||||||
|
// no start/stop output control.
|
||||||
|
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||||
|
// output modes - disable post processing
|
||||||
|
raw.c_oflag &= ~(OPOST);
|
||||||
|
// control modes - set 8 bit chars
|
||||||
|
raw.c_cflag |= (CS8);
|
||||||
|
// local modes - choing off, canonical off, no extended functions,
|
||||||
|
// no signal chars (^Z,^C)
|
||||||
|
#ifdef CONSOLE_NO_CATCH
|
||||||
|
raw.c_lflag &= ~( ECHO | ICANON | IEXTEN );
|
||||||
|
#else
|
||||||
|
raw.c_lflag &= ~( ECHO | ICANON | IEXTEN | ISIG );
|
||||||
|
#endif
|
||||||
|
// control chars - set return condition: min number of bytes and timer.
|
||||||
|
// We want read to return every single byte, without timeout.
|
||||||
|
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;// 1 byte, no timer
|
||||||
|
// put terminal in raw mode after flushing
|
||||||
|
if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw) < 0)
|
||||||
|
return -1;
|
||||||
|
rawmode = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable_raw()
|
||||||
|
{
|
||||||
|
/* Don't even check the return value as it's too late. */
|
||||||
|
if (rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&orig_termios) != -1)
|
||||||
|
rawmode = 0;
|
||||||
|
}
|
||||||
|
void prompt_refresh()
|
||||||
|
{
|
||||||
|
char seq[64];
|
||||||
|
int cols = get_columns();
|
||||||
|
int plen = prompt.size();
|
||||||
|
const char * buf = raw_buffer.c_str();
|
||||||
|
int len = raw_buffer.size();
|
||||||
|
int cooked_cursor = raw_cursor;
|
||||||
|
// Use math! This is silly.
|
||||||
|
while((plen+cooked_cursor) >= cols)
|
||||||
|
{
|
||||||
|
buf++;
|
||||||
|
len--;
|
||||||
|
cooked_cursor--;
|
||||||
|
}
|
||||||
|
while (plen+len > cols)
|
||||||
|
{
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
/* Cursor to left edge */
|
||||||
|
snprintf(seq,64,"\x1b[1G");
|
||||||
|
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
|
||||||
|
/* Write the prompt and the current buffer content */
|
||||||
|
if (::write(STDIN_FILENO,prompt.c_str(),plen) == -1) return;
|
||||||
|
if (::write(STDIN_FILENO,buf,len) == -1) return;
|
||||||
|
/* Erase to right */
|
||||||
|
snprintf(seq,64,"\x1b[0K");
|
||||||
|
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
|
||||||
|
/* Move cursor to original position. */
|
||||||
|
snprintf(seq,64,"\x1b[1G\x1b[%dC", (int)(cooked_cursor+plen));
|
||||||
|
if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prompt_loop(recursive_mutex * lock, CommandHistory & history)
|
||||||
|
{
|
||||||
|
int fd = STDIN_FILENO;
|
||||||
|
size_t plen = prompt.size();
|
||||||
|
int history_index = 0;
|
||||||
|
raw_buffer.clear();
|
||||||
|
raw_cursor = 0;
|
||||||
|
/* The latest history entry is always our current buffer, that
|
||||||
|
* initially is just an empty string. */
|
||||||
|
const std::string empty;
|
||||||
|
history.add(empty);
|
||||||
|
if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1;
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
unsigned char c;
|
||||||
|
int isok;
|
||||||
|
unsigned char seq[2], seq2;
|
||||||
|
lock->unlock();
|
||||||
|
if(!read_char(c))
|
||||||
|
{
|
||||||
|
lock->lock();
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
lock->lock();
|
||||||
|
/* Only autocomplete when the callback is set. It returns < 0 when
|
||||||
|
* there was an error reading from fd. Otherwise it will return the
|
||||||
|
* character that should be handled next. */
|
||||||
|
if (c == 9)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if( completionCallback != NULL) {
|
||||||
|
c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
|
||||||
|
// Return on errors
|
||||||
|
if (c < 0) return len;
|
||||||
|
// Read next character when 0
|
||||||
|
if (c == 0) continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ignore tab
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// just ignore tabs
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case 13: // enter
|
||||||
|
history.remove();
|
||||||
|
return raw_buffer.size();
|
||||||
|
case 3: // ctrl-c
|
||||||
|
errno = EAGAIN;
|
||||||
|
return -1;
|
||||||
|
case 127: // backspace
|
||||||
|
case 8: // ctrl-h
|
||||||
|
if (raw_cursor > 0 && raw_buffer.size() > 0)
|
||||||
|
{
|
||||||
|
raw_buffer.erase(raw_cursor-1,1);
|
||||||
|
raw_cursor--;
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 27: // escape sequence
|
||||||
|
lock->unlock();
|
||||||
|
if(!read_char(seq[0]) || !read_char(seq[1]))
|
||||||
|
{
|
||||||
|
lock->lock();
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
lock->lock();
|
||||||
|
if(seq[0] == '[')
|
||||||
|
{
|
||||||
|
if (seq[1] == 'D')
|
||||||
|
{
|
||||||
|
left_arrow:
|
||||||
|
if (raw_cursor > 0)
|
||||||
|
{
|
||||||
|
raw_cursor--;
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( seq[1] == 'C')
|
||||||
|
{
|
||||||
|
right_arrow:
|
||||||
|
/* right arrow */
|
||||||
|
if (size_t(raw_cursor) != raw_buffer.size())
|
||||||
|
{
|
||||||
|
raw_cursor++;
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (seq[1] == 'A' || seq[1] == 'B')
|
||||||
|
{
|
||||||
|
/* up and down arrow: history */
|
||||||
|
if (history.size() > 1)
|
||||||
|
{
|
||||||
|
/* Update the current history entry before to
|
||||||
|
* overwrite it with tne next one. */
|
||||||
|
history[history_index] = raw_buffer;
|
||||||
|
/* Show the new entry */
|
||||||
|
history_index += (seq[1] == 'A') ? 1 : -1;
|
||||||
|
if (history_index < 0)
|
||||||
|
{
|
||||||
|
history_index = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (size_t(history_index) >= history.size())
|
||||||
|
{
|
||||||
|
history_index = history.size()-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
raw_buffer = history[history_index];
|
||||||
|
raw_cursor = raw_buffer.size();
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(seq[1] == 'H')
|
||||||
|
{
|
||||||
|
// home
|
||||||
|
raw_cursor = 0;
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
else if(seq[1] == 'F')
|
||||||
|
{
|
||||||
|
// end
|
||||||
|
raw_cursor = raw_buffer.size();
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
else if (seq[1] > '0' && seq[1] < '7')
|
||||||
|
{
|
||||||
|
// extended escape
|
||||||
|
lock->unlock();
|
||||||
|
if(!read_char(seq2))
|
||||||
|
{
|
||||||
|
lock->lock();
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
lock->lock();
|
||||||
|
if (seq[1] == '3' && seq2 == '~' )
|
||||||
|
{
|
||||||
|
// delete
|
||||||
|
if (raw_buffer.size() > 0 && size_t(raw_cursor) < raw_buffer.size())
|
||||||
|
{
|
||||||
|
raw_buffer.erase(raw_cursor,1);
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (raw_buffer.size() == size_t(raw_cursor))
|
||||||
|
{
|
||||||
|
raw_buffer.append(1,c);
|
||||||
|
raw_cursor++;
|
||||||
|
if (plen+raw_buffer.size() < size_t(get_columns()))
|
||||||
|
{
|
||||||
|
/* Avoid a full update of the line in the
|
||||||
|
* trivial case. */
|
||||||
|
if (::write(fd,&c,1) == -1) return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
raw_buffer.insert(raw_cursor,1,c);
|
||||||
|
raw_cursor++;
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 21: // Ctrl+u, delete the whole line.
|
||||||
|
raw_buffer.clear();
|
||||||
|
raw_cursor = 0;
|
||||||
|
prompt_refresh();
|
||||||
|
break;
|
||||||
|
case 11: // Ctrl+k, delete from current to end of line.
|
||||||
|
raw_buffer.erase(raw_cursor);
|
||||||
|
prompt_refresh();
|
||||||
|
break;
|
||||||
|
case 1: // Ctrl+a, go to the start of the line
|
||||||
|
raw_cursor = 0;
|
||||||
|
prompt_refresh();
|
||||||
|
break;
|
||||||
|
case 5: // ctrl+e, go to the end of the line
|
||||||
|
raw_cursor = raw_buffer.size();
|
||||||
|
prompt_refresh();
|
||||||
|
break;
|
||||||
|
case 12: // ctrl+l, clear screen
|
||||||
|
clear();
|
||||||
|
prompt_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raw_buffer.size();
|
||||||
|
}
|
||||||
|
FILE * dfout_C;
|
||||||
|
bool supported_terminal;
|
||||||
|
// state variables
|
||||||
|
bool rawmode; // is raw mode active?
|
||||||
|
termios orig_termios; // saved/restored by raw mode
|
||||||
|
// current state
|
||||||
|
enum console_state
|
||||||
|
{
|
||||||
|
con_unclaimed,
|
||||||
|
con_lineedit
|
||||||
|
} state;
|
||||||
|
bool in_batch;
|
||||||
|
std::string prompt; // current prompt string
|
||||||
|
std::string raw_buffer; // current raw mode buffer
|
||||||
|
int raw_cursor; // cursor position in the buffer
|
||||||
|
// thread exit mechanism
|
||||||
|
int exit_pipe[2];
|
||||||
|
fd_set descriptor_set;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Console::Console()
|
||||||
|
{
|
||||||
|
d = 0;
|
||||||
|
inited = false;
|
||||||
|
// we can't create the mutex at this time. the SDL functions aren't hooked yet.
|
||||||
|
wlock = new recursive_mutex();
|
||||||
|
}
|
||||||
|
Console::~Console()
|
||||||
|
{
|
||||||
|
if(inited)
|
||||||
|
shutdown();
|
||||||
|
if(wlock)
|
||||||
|
delete wlock;
|
||||||
|
if(d)
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::init(bool sharing)
|
||||||
|
{
|
||||||
|
if(sharing)
|
||||||
|
{
|
||||||
|
inited = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
freopen("stdout.log", "w", stdout);
|
||||||
|
d = new Private();
|
||||||
|
// make our own weird streams so our IO isn't redirected
|
||||||
|
d->dfout_C = fopen("/dev/tty", "w");
|
||||||
|
std::cin.tie(this);
|
||||||
|
clear();
|
||||||
|
d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO);
|
||||||
|
// init the exit mechanism
|
||||||
|
pipe(d->exit_pipe);
|
||||||
|
FD_ZERO(&d->descriptor_set);
|
||||||
|
FD_SET(STDIN_FILENO, &d->descriptor_set);
|
||||||
|
FD_SET(d->exit_pipe[0], &d->descriptor_set);
|
||||||
|
inited = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::shutdown(void)
|
||||||
|
{
|
||||||
|
if(!d)
|
||||||
|
return true;
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
if(d->rawmode)
|
||||||
|
d->disable_raw();
|
||||||
|
d->print("\n");
|
||||||
|
inited = false;
|
||||||
|
// kill the thing
|
||||||
|
close(d->exit_pipe[1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::begin_batch()
|
||||||
|
{
|
||||||
|
//color_ostream::begin_batch();
|
||||||
|
|
||||||
|
wlock->lock();
|
||||||
|
|
||||||
|
if (inited)
|
||||||
|
d->begin_batch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::end_batch()
|
||||||
|
{
|
||||||
|
if (inited)
|
||||||
|
d->end_batch();
|
||||||
|
|
||||||
|
wlock->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::flush_proxy()
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
if (inited)
|
||||||
|
d->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::add_text(color_value color, const std::string &text)
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
if (inited)
|
||||||
|
d->print_text(color, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Console::get_columns(void)
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
int ret = -1;
|
||||||
|
if(inited)
|
||||||
|
ret = d->get_columns();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Console::get_rows(void)
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
int ret = -1;
|
||||||
|
if(inited)
|
||||||
|
ret = d->get_rows();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::clear()
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
if(inited)
|
||||||
|
d->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::gotoxy(int x, int y)
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
if(inited)
|
||||||
|
d->gotoxy(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::cursor(bool enable)
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
if(inited)
|
||||||
|
d->cursor(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Console::lineedit(const std::string & prompt, std::string & output, CommandHistory & ch)
|
||||||
|
{
|
||||||
|
lock_guard <recursive_mutex> g(*wlock);
|
||||||
|
int ret = -2;
|
||||||
|
if(inited)
|
||||||
|
ret = d->lineedit(prompt,output,wlock,ch);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::msleep (unsigned int msec)
|
||||||
|
{
|
||||||
|
if (msec > 1000) sleep(msec/1000000);
|
||||||
|
usleep((msec % 1000000) * 1000);
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
https://github.com/peterix/dfhack
|
||||||
|
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any
|
||||||
|
damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any
|
||||||
|
purpose, including commercial applications, and to alter it and
|
||||||
|
redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must
|
||||||
|
not claim that you wrote the original software. If you use this
|
||||||
|
software in a product, an acknowledgment in the product documentation
|
||||||
|
would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and
|
||||||
|
must not be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
/*typedef struct interpose_s
|
||||||
|
{
|
||||||
|
void *new_func;
|
||||||
|
void *orig_func;
|
||||||
|
} interpose_t;*/
|
||||||
|
|
||||||
|
#include "DFHack.h"
|
||||||
|
#include "Core.h"
|
||||||
|
#include "Hooks.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "MacPool.h"
|
||||||
|
|
||||||
|
/*static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) =
|
||||||
|
{
|
||||||
|
{ (void *)DFH_SDL_Init, (void *)SDL_Init },
|
||||||
|
{ (void *)DFH_SDL_PollEvent, (void *)SDL_PollEvent },
|
||||||
|
{ (void *)DFH_SDL_Quit, (void *)SDL_Quit },
|
||||||
|
{ (void *)DFH_SDL_NumJoysticks, (void *)SDL_NumJoysticks },
|
||||||
|
|
||||||
|
};*/
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* SDL part starts here *
|
||||||
|
*******************************************************************************/
|
||||||
|
// hook - called for each game tick (or more often)
|
||||||
|
DFhackCExport int SDL_NumJoysticks(void)
|
||||||
|
{
|
||||||
|
DFHack::Core & c = DFHack::Core::getInstance();
|
||||||
|
return c.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// hook - called at program exit
|
||||||
|
static void (*_SDL_Quit)(void) = 0;
|
||||||
|
DFhackCExport void SDL_Quit(void)
|
||||||
|
{
|
||||||
|
DFHack::Core & c = DFHack::Core::getInstance();
|
||||||
|
c.Shutdown();
|
||||||
|
/*if(_SDL_Quit)
|
||||||
|
{
|
||||||
|
_SDL_Quit();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// destroy_pool();
|
||||||
|
|
||||||
|
_SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by DF to check input events
|
||||||
|
static int (*_SDL_PollEvent)(SDL_Event* event) = 0;
|
||||||
|
DFhackCExport int SDL_PollEvent(SDL_Event* event)
|
||||||
|
{
|
||||||
|
pollevent_again:
|
||||||
|
// if SDL returns 0 here, it means there are no more events. return 0
|
||||||
|
int orig_return = _SDL_PollEvent(event);
|
||||||
|
if(!orig_return)
|
||||||
|
return 0;
|
||||||
|
// otherwise we have an event to filter
|
||||||
|
else if( event != 0 )
|
||||||
|
{
|
||||||
|
DFHack::Core & c = DFHack::Core::getInstance();
|
||||||
|
// if we consume the event, ask SDL for more.
|
||||||
|
if(!c.DFH_SDL_Event(event))
|
||||||
|
goto pollevent_again;
|
||||||
|
}
|
||||||
|
return orig_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WINDOW;
|
||||||
|
DFhackCExport int wgetch(WINDOW *win)
|
||||||
|
{
|
||||||
|
static int (*_wgetch)(WINDOW * win) = (int (*)( WINDOW * )) dlsym(RTLD_NEXT, "wgetch");
|
||||||
|
if(!_wgetch)
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
DFHack::Core & c = DFHack::Core::getInstance();
|
||||||
|
wgetch_again:
|
||||||
|
int in = _wgetch(win);
|
||||||
|
int out;
|
||||||
|
if(c.ncurses_wgetch(in, out))
|
||||||
|
{
|
||||||
|
// not consumed, give to DF
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// consumed, repeat
|
||||||
|
goto wgetch_again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hook - called at program start, initialize some stuffs we'll use later
|
||||||
|
static int (*_SDL_Init)(uint32_t flags) = 0;
|
||||||
|
DFhackCExport int SDL_Init(uint32_t flags)
|
||||||
|
{
|
||||||
|
// reroute stderr
|
||||||
|
fprintf(stderr,"dfhack: attempting to hook in\n");
|
||||||
|
//freopen("stderr.log", "w", stderr);
|
||||||
|
// we don't reroute stdout until we figure out if this should be done at all
|
||||||
|
// See: Console-linux.cpp
|
||||||
|
|
||||||
|
// create_pool();
|
||||||
|
|
||||||
|
// find real functions
|
||||||
|
fprintf(stderr,"dfhack: saving real SDL functions\n");
|
||||||
|
_SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init");
|
||||||
|
_SDL_Quit = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_Quit");
|
||||||
|
_SDL_PollEvent = (int (*)(SDL_Event*))dlsym(RTLD_NEXT,"SDL_PollEvent");
|
||||||
|
|
||||||
|
fprintf(stderr,"dfhack: saved real SDL functions\n");
|
||||||
|
// check if we got them
|
||||||
|
if(_SDL_Init && _SDL_Quit && _SDL_PollEvent)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"dfhack: hooking successful\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// bail, this would be a disaster otherwise
|
||||||
|
fprintf(stderr,"dfhack: something went horribly wrong\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
DFHack::Core & c = DFHack::Core::getInstance();
|
||||||
|
c.Init();
|
||||||
|
|
||||||
|
int ret = _SDL_Init(flags);
|
||||||
|
return ret;
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "DFHack.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
#include "Hooks.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Plugin loading functions
|
||||||
|
*/
|
||||||
|
namespace DFHack
|
||||||
|
{
|
||||||
|
DFLibrary * OpenPlugin (const char * filename)
|
||||||
|
{
|
||||||
|
dlerror();
|
||||||
|
DFLibrary * ret = (DFLibrary *) dlopen(filename, RTLD_NOW);
|
||||||
|
if(!ret)
|
||||||
|
{
|
||||||
|
std::cerr << dlerror() << std::endl;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void * LookupPlugin (DFLibrary * plugin ,const char * function)
|
||||||
|
{
|
||||||
|
return (DFLibrary *) dlsym((void *)plugin, function);
|
||||||
|
}
|
||||||
|
void ClosePlugin (DFLibrary * plugin)
|
||||||
|
{
|
||||||
|
dlclose((void *) plugin);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
https://github.com/peterix/dfhack
|
||||||
|
Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com)
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any
|
||||||
|
damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any
|
||||||
|
purpose, including commercial applications, and to alter it and
|
||||||
|
redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must
|
||||||
|
not claim that you wrote the original software. If you use this
|
||||||
|
software in a product, an acknowledgment in the product documentation
|
||||||
|
would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and
|
||||||
|
must not be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Internal.h"
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <md5wrapper.h>
|
||||||
|
#include "MemAccess.h"
|
||||||
|
#include "VersionInfoFactory.h"
|
||||||
|
#include "VersionInfo.h"
|
||||||
|
#include "Error.h"
|
||||||
|
#include <string.h>
|
||||||
|
using namespace DFHack;
|
||||||
|
|
||||||
|
Process::Process(VersionInfoFactory * known_versions)
|
||||||
|
{
|
||||||
|
int target_result;
|
||||||
|
|
||||||
|
char path[1024];
|
||||||
|
char *real_path;
|
||||||
|
uint32_t size = sizeof(path);
|
||||||
|
if (_NSGetExecutablePath(path, &size) == 0) {
|
||||||
|
real_path = realpath(path, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
identified = false;
|
||||||
|
my_descriptor = 0;
|
||||||
|
|
||||||
|
md5wrapper md5;
|
||||||
|
uint32_t length;
|
||||||
|
uint8_t first_kb [1024];
|
||||||
|
memset(first_kb, 0, sizeof(first_kb));
|
||||||
|
// get hash of the running DF process
|
||||||
|
string hash = md5.getHashFromFile(real_path, length, (char *) first_kb);
|
||||||
|
// create linux process, add it to the vector
|
||||||
|
VersionInfo * vinfo = known_versions->getVersionInfoByMD5(hash);
|
||||||
|
if(vinfo)
|
||||||
|
{
|
||||||
|
my_descriptor = new VersionInfo(*vinfo);
|
||||||
|
identified = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char * wd = getcwd(NULL, 0);
|
||||||
|
cerr << "Unable to retrieve version information.\n";
|
||||||
|
cerr << "File: " << real_path << endl;
|
||||||
|
cerr << "MD5: " << hash << endl;
|
||||||
|
cerr << "working dir: " << wd << endl;
|
||||||
|
cerr << "length:" << length << endl;
|
||||||
|
cerr << "1KB hexdump follows:" << endl;
|
||||||
|
for(int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||||
|
first_kb[i*16],
|
||||||
|
first_kb[i*16+1],
|
||||||
|
first_kb[i*16+2],
|
||||||
|
first_kb[i*16+3],
|
||||||
|
first_kb[i*16+4],
|
||||||
|
first_kb[i*16+5],
|
||||||
|
first_kb[i*16+6],
|
||||||
|
first_kb[i*16+7],
|
||||||
|
first_kb[i*16+8],
|
||||||
|
first_kb[i*16+9],
|
||||||
|
first_kb[i*16+10],
|
||||||
|
first_kb[i*16+11],
|
||||||
|
first_kb[i*16+12],
|
||||||
|
first_kb[i*16+13],
|
||||||
|
first_kb[i*16+14],
|
||||||
|
first_kb[i*16+15]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
free(wd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process::~Process()
|
||||||
|
{
|
||||||
|
// destroy our copy of the memory descriptor
|
||||||
|
delete my_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Process::doReadClassName (void * vptr)
|
||||||
|
{
|
||||||
|
//FIXME: BAD!!!!!
|
||||||
|
char * typeinfo = Process::readPtr(((char *)vptr - 0x4));
|
||||||
|
char * typestring = Process::readPtr(typeinfo + 0x4);
|
||||||
|
string raw = readCString(typestring);
|
||||||
|
size_t start = raw.find_first_of("abcdefghijklmnopqrstuvwxyz");// trim numbers
|
||||||
|
size_t end = raw.length();
|
||||||
|
return raw.substr(start,end-start);
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIXME: cross-reference with ELF segment entries?
|
||||||
|
void Process::getMemRanges( vector<t_memrange> & ranges )
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
char permissions[5]; // r/-, w/-, x/-, p/s, 0
|
||||||
|
|
||||||
|
FILE *mapFile = ::fopen("/proc/self/maps", "r");
|
||||||
|
size_t start, end, offset, device1, device2, node;
|
||||||
|
|
||||||
|
while (fgets(buffer, 1024, mapFile))
|
||||||
|
{
|
||||||
|
t_memrange temp;
|
||||||
|
temp.name[0] = 0;
|
||||||
|
sscanf(buffer, "%zx-%zx %s %zx %2zx:%2zx %zu %[^\n]",
|
||||||
|
&start,
|
||||||
|
&end,
|
||||||
|
(char*)&permissions,
|
||||||
|
&offset, &device1, &device2, &node,
|
||||||
|
(char*)temp.name);
|
||||||
|
temp.start = (void *) start;
|
||||||
|
temp.end = (void *) end;
|
||||||
|
temp.read = permissions[0] == 'r';
|
||||||
|
temp.write = permissions[1] == 'w';
|
||||||
|
temp.execute = permissions[2] == 'x';
|
||||||
|
temp.shared = permissions[3] == 's';
|
||||||
|
temp.valid = true;
|
||||||
|
ranges.push_back(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Process::getBase()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getdir (string dir, vector<string> &files)
|
||||||
|
{
|
||||||
|
DIR *dp;
|
||||||
|
struct dirent *dirp;
|
||||||
|
if((dp = opendir(dir.c_str())) == NULL)
|
||||||
|
{
|
||||||
|
cout << "Error(" << errno << ") opening " << dir << endl;
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
while ((dirp = readdir(dp)) != NULL) {
|
||||||
|
files.push_back(string(dirp->d_name));
|
||||||
|
}
|
||||||
|
closedir(dp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::getThreadIDs(vector<uint32_t> & threads )
|
||||||
|
{
|
||||||
|
stringstream ss;
|
||||||
|
vector<string> subdirs;
|
||||||
|
if(getdir("/proc/self/task/",subdirs) != 0)
|
||||||
|
{
|
||||||
|
//FIXME: needs exceptions. this is a fatal error
|
||||||
|
cerr << "unable to enumerate threads. This is BAD!" << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
threads.clear();
|
||||||
|
for(size_t i = 0; i < subdirs.size();i++)
|
||||||
|
{
|
||||||
|
uint32_t tid;
|
||||||
|
if(sscanf(subdirs[i].c_str(),"%d", &tid))
|
||||||
|
{
|
||||||
|
threads.push_back(tid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Process::getPath()
|
||||||
|
{
|
||||||
|
char path[1024];
|
||||||
|
char *real_path;
|
||||||
|
uint32_t size = sizeof(path);
|
||||||
|
if (_NSGetExecutablePath(path, &size) == 0) {
|
||||||
|
real_path = realpath(path, NULL);
|
||||||
|
}
|
||||||
|
std::string path_string(real_path);
|
||||||
|
int last_slash = path_string.find_last_of("/");
|
||||||
|
std::string directory = path_string.substr(0,last_slash);
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Process::getPID()
|
||||||
|
{
|
||||||
|
return getpid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int protect=0;
|
||||||
|
if(trgrange.read)protect|=PROT_READ;
|
||||||
|
if(trgrange.write)protect|=PROT_WRITE;
|
||||||
|
if(trgrange.execute)protect|=PROT_EXEC;
|
||||||
|
result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect);
|
||||||
|
|
||||||
|
return result==0;
|
||||||
|
}
|
Loading…
Reference in New Issue