summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Mrázek2011-07-13 11:45:30 +0200
committerPetr Mrázek2011-07-13 11:45:30 +0200
commit630b746cfe8455ee89ef8af55013141aa70ccf87 (patch)
tree5117a9bb7fec32452af2e2792cbdcce359395e1a
parent1b011cdf6ce4331d731fd361fd82f367a9484435 (diff)
downloaddfhack-630b746cfe8455ee89ef8af55013141aa70ccf87.tar.gz
dfhack-630b746cfe8455ee89ef8af55013141aa70ccf87.tar.bz2
dfhack-630b746cfe8455ee89ef8af55013141aa70ccf87.tar.xz
Integrate linenoise into Console - Linux part
-rw-r--r--LICENSE44
-rw-r--r--library/CMakeLists.txt3
-rw-r--r--library/Console-linux.cpp493
-rw-r--r--library/Core.cpp51
-rw-r--r--library/PluginManager.cpp7
-rw-r--r--library/depends/libnoise/linenoise.cpp57
-rw-r--r--library/include/dfhack/Console.h32
-rw-r--r--library/include/dfhack/Core.h4
-rw-r--r--plugins/cleanmap.cpp2
-rw-r--r--plugins/prospector.cpp54
-rw-r--r--plugins/reveal.cpp24
-rw-r--r--plugins/vdig.cpp10
-rw-r--r--plugins/weather.cpp24
13 files changed, 684 insertions, 121 deletions
diff --git a/LICENSE b/LICENSE
index 7998532a..68b70dcf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -139,4 +139,46 @@ DAMAGE.
* IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
+ * OTHER DEALINGS IN THE SOFTWARE.
+
+------------------------------------------------------------------
+Parts of dfhack are based on linenoise:
+linenoise.c -- 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>
+
+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.
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 25e4bb2a..803ade58 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -151,6 +151,9 @@ IF(UNIX)
COMMAND ${CMAKE_COMMAND} -E make_directory ${DFHACK_OUTPUT_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${dfhack_SOURCE_DIR}/package/linux/dfhack ${DFHACK_OUTPUT_DIR})
ADD_DEPENDENCIES(dfhack prepare_UNIX)
+ install(PROGRAMS ${dfhack_SOURCE_DIR}/package/linux/dfhack
+ DESTINATION ${DFHACK_LIBRARY_DESTINATION}) #linux: share/dfhack
+
ENDIF()
install(TARGETS dfhack
diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp
index 4313d4d8..6a846828 100644
--- a/library/Console-linux.cpp
+++ b/library/Console-linux.cpp
@@ -22,42 +22,178 @@ must not be misrepresented as being the original software.
distribution.
*/
+/*
+Parts of this code are based on linenoise:
+linenoise.c -- 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>
+
+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 "dfhack/Console.h"
#include <cstdio>
#include <cstdlib>
#include <sstream>
+#include <stdio.h>
+#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>
using namespace DFHack;
-duthomhas::stdiostream dfout;
-FILE * dfout_C = 0;
-duthomhas::stdiobuf * stream_o = 0;
+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+#define LINENOISE_MAX_LINE 4096
+
+static const char *unsupported_term[] = {"dumb","cons25",NULL};
+namespace DFHack
+{
+ class Private
+ {
+ public:
+ Private()
+ {
+ dfout_C = 0;
+ stream_o = 0;
+ rawmode = 0;
+ history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+ history_len = 0;
+ history = NULL;
+ };
+ int get_columns(void)
+ {
+ winsize ws;
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 80;
+ return ws.ws_col;
+ }
+ int get_rows(void)
+ {
+ winsize ws;
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) return 25;
+ return ws.ws_row;
+ }
+ void clear()
+ {
+ const char * clr = "\033c\033[3J\033[H";
+ write(STDIN_FILENO,clr,strlen(clr));
+ }
+
+ int isUnsupportedTerm(void)
+ {
+ 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;
+ }
+
+ FILE * dfout_C;
+ duthomhas::stdiobuf * stream_o;
+ termios orig_termios; /* in order to restore at exit */
+ int rawmode; /* for atexit() function to check if restore is needed*/
+ int history_max_len;
+ int history_len;
+ char **history;
+ };
+}
// FIXME: prime candidate for being a singleton...
Console::Console()
{
+ d = new Private();
+}
+Console::~Console()
+{
+ delete d;
+}
+
+bool Console::init(void)
+{
// make our own weird streams so our IO isn't redirected
- dfout_C = fopen("/dev/tty", "w");
- stream_o = new duthomhas::stdiobuf(dfout_C);
- dfout.rdbuf(stream_o);
- std::cin.tie(&dfout);
+ d->dfout_C = fopen("/dev/tty", "w");
+ d->stream_o = new duthomhas::stdiobuf(d->dfout_C);
+ rdbuf(d->stream_o);
+ std::cin.tie(this);
clear();
- // result is a terminal controlled by the parasitic code!
}
-Console::~Console()
+
+bool Console::shutdown(void)
{
-
+ if(d->rawmode)
+ disable_raw();
+ *this << std::endl;
}
+
+int Console::print( const char* format, ... )
+{
+ va_list args;
+ va_start( args, format );
+ int ret = vfprintf( d->dfout_C, format, args );
+ va_end( args );
+ return ret;
+}
+
+int Console::get_columns(void)
+{
+ return d->get_columns();
+}
+
+int Console::get_rows(void)
+{
+ return d->get_rows();
+}
+
void Console::clear()
{
- dfout << "\033c";
- dfout << "\033[3J\033[H";
+ *this << "\033c";
+ *this << "\033[3J\033[H";
}
+
void Console::gotoxy(int x, int y)
{
std::ostringstream oss;
oss << "\033[" << y << ";" << x << "H";
- dfout << oss.str();
+ *this << oss.str();
}
const char * ANSI_CLS = "\033[2J";
@@ -105,12 +241,12 @@ const char * getANSIColor(const int c)
void Console::color(int index)
{
- dfout << getANSIColor(index);
+ *this << getANSIColor(index);
}
void Console::reset_color( void )
{
- dfout << RESETCOLOR;
+ *this << RESETCOLOR;
}
@@ -118,11 +254,11 @@ void Console::cursor(bool enable)
{
if(enable)
{
- dfout <<"\033[?25h";
+ *this <<"\033[?25h";
}
else
{
- dfout <<"\033[?25l";
+ *this <<"\033[?25l";
}
}
@@ -130,4 +266,327 @@ void Console::msleep (unsigned int msec)
{
if (msec > 1000) sleep(msec/1000000);
usleep((msec % 1000000) * 1000);
+}
+
+int Console::enable_raw()
+{
+ struct termios raw;
+
+ if (!isatty(STDIN_FILENO)) goto fatal;
+ if (tcgetattr(STDIN_FILENO,&d->orig_termios) == -1) goto fatal;
+
+ raw = d->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) */
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ /* 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) goto fatal;
+ d->rawmode = 1;
+ return 0;
+
+fatal:
+ errno = ENOTTY;
+ return -1;
+}
+
+void Console::disable_raw()
+{
+ /* Don't even check the return value as it's too late. */
+ if (d->rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&d->orig_termios) != -1)
+ d->rawmode = 0;
+}
+
+void Console::prompt_refresh( const std::string& prompt, const std::string& buffer, size_t pos)
+{
+ char seq[64];
+ int cols = get_columns();
+ int plen = prompt.size();
+ const char * buf = buffer.c_str();
+ int len = buffer.size();
+ // Use math! This is silly.
+ while((plen+pos) >= cols)
+ {
+ buf++;
+ len--;
+ pos--;
+ }
+ 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)(pos+plen));
+ if (::write(STDIN_FILENO,seq,strlen(seq)) == -1) return;
+}
+
+int Console::prompt_loop(const std::string & prompt, std::string & buffer)
+{
+ int fd = STDIN_FILENO;
+ size_t plen = prompt.size();
+ size_t pos = 0;
+ size_t cols = d->get_columns();
+ int history_index = 0;
+
+ /* The latest history entry is always our current buffer, that
+ * initially is just an empty string. */
+ history_add("");
+
+ if (::write(fd,prompt.c_str(),plen) == -1) return -1;
+ while(1)
+ {
+ char c;
+ int nread;
+ char seq[2], seq2[2];
+
+ nread = ::read(fd,&c,1);
+ if (nread <= 0) return buffer.size();
+
+ /* 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.pop_front();
+ return buffer.size();
+ case 3: /* ctrl-c */
+ errno = EAGAIN;
+ return -1;
+ case 127: /* backspace */
+ case 8: /* ctrl-h */
+ if (pos > 0 && buffer.size() > 0)
+ {
+ buffer.erase(pos-1,1);
+ pos--;
+ prompt_refresh(prompt,buffer,pos);
+ }
+ break;
+ // I fail to see how is this useful to anyone but hardcore emacs users
+ /*
+ case 4: // ctrl-d, remove char at right of cursor
+ if (len > 1 && pos < (len-1)) {
+ memmove(buf+pos,buf+pos+1,len-pos);
+ len--;
+ buf[len] = '\0';
+ prompt_refresh(prompt,buffer,pos);
+ } else if (len == 0) {
+ history_len--;
+ free(history[history_len]);
+ return -1;
+ }
+ break;
+ case 20: // ctrl-t
+ if (pos > 0 && pos < len) {
+ int aux = buf[pos-1];
+ buf[pos-1] = buf[pos];
+ buf[pos] = aux;
+ if (pos != len-1) pos++;
+ prompt_refresh(prompt,buffer,pos);
+ }
+ break;
+ case 2: // ctrl-b
+ goto left_arrow;
+ case 6: // ctrl-f
+ goto right_arrow;
+ case 16: // ctrl-p
+ seq[1] = 65;
+ goto up_down_arrow;
+ case 14: // ctrl-n
+ seq[1] = 66;
+ goto up_down_arrow;
+ break;
+ */
+ case 27: /* escape sequence */
+ if (::read(fd,seq,2) == -1) break;
+ if(seq[0] == '[')
+ {
+ if (seq[1] == 'D')
+ {
+ left_arrow:
+ if (pos > 0)
+ {
+ pos--;
+ prompt_refresh(prompt,buffer,pos);
+ }
+ }
+ else if ( seq[1] == 'C')
+ {
+ right_arrow:
+ /* right arrow */
+ if (pos != buffer.size())
+ {
+ pos++;
+ prompt_refresh(prompt,buffer,pos);
+ }
+ }
+ else if (seq[1] == 'A' || seq[1] == 'B')
+ {
+ up_down_arrow:
+ /* 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] = buffer;
+ /* Show the new entry */
+ history_index += (seq[1] == 'A') ? 1 : -1;
+ if (history_index < 0)
+ {
+ history_index = 0;
+ break;
+ }
+ else if (history_index >= history.size())
+ {
+ history_index = history.size()-1;
+ break;
+ }
+ buffer = history[history_index];
+ pos = buffer.size();
+ prompt_refresh(prompt,buffer,pos);
+ }
+ }
+ else if(seq[1] == 'H')
+ {
+ // home
+ pos = 0;
+ prompt_refresh(prompt,buffer,pos);
+ }
+ else if(seq[1] == 'F')
+ {
+ // end
+ pos = buffer.size();
+ prompt_refresh(prompt,buffer,pos);
+ }
+ else if (seq[1] > '0' && seq[1] < '7')
+ {
+ // extended escape
+ if (::read(fd,seq2,2) == -1) break;
+ if (seq2[0] == '~' && seq[1] == '3')
+ {
+ // delete
+ if (buffer.size() > 0 && pos < buffer.size())
+ {
+ buffer.erase(pos,1);
+ prompt_refresh(prompt,buffer,pos);
+ }
+ }
+ }
+ }
+ break;
+ default:
+ if (buffer.size() == pos)
+ {
+ buffer.append(1,c);
+ pos++;
+ if (plen+buffer.size() < cols)
+ {
+ /* Avoid a full update of the line in the
+ * trivial case. */
+ if (::write(fd,&c,1) == -1) return -1;
+ }
+ else
+ {
+ prompt_refresh(prompt,buffer,pos);
+ }
+ }
+ else
+ {
+ buffer.insert(pos,1,c);
+ pos++;
+ prompt_refresh(prompt,buffer,pos);
+ }
+ break;
+ case 21: // Ctrl+u, delete the whole line.
+ buffer.clear();
+ pos = 0;
+ prompt_refresh(prompt,buffer,pos);
+ break;
+ case 11: // Ctrl+k, delete from current to end of line.
+ buffer.erase(pos);
+ prompt_refresh(prompt,buffer,pos);
+ break;
+ case 1: // Ctrl+a, go to the start of the line
+ pos = 0;
+ prompt_refresh(prompt,buffer,pos);
+ break;
+ case 5: // ctrl+e, go to the end of the line
+ pos = buffer.size();
+ prompt_refresh(prompt,buffer,pos);
+ break;
+ case 12: // ctrl+l, clear screen
+ clear();
+ prompt_refresh(prompt,buffer,pos);
+ }
+ }
+ return buffer.size();
+}
+// push to front, remove from back if we are above maximum. ignore immediate duplicates
+void Console::history_add(const std::string & command)
+{
+ if(history.front() == command)
+ return;
+ history.push_front(command);
+ if(history.size() > 100)
+ history.pop_back();
+}
+
+int Console::lineedit(const std::string & prompt, std::string & output)
+{
+ output.clear();
+ int count;
+
+ if (d->isUnsupportedTerm() || !isatty(STDIN_FILENO))
+ {
+ *this << prompt;
+ flush();
+ std::getline(std::cin, output);
+ return output.size();
+ }
+ else
+ {
+ if (enable_raw() == -1) return 0;
+ count = prompt_loop(prompt, output);
+ disable_raw();
+ *this << std::endl;
+ return output.size();
+ }
} \ No newline at end of file
diff --git a/library/Core.cpp b/library/Core.cpp
index ea8d2608..72e607c5 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -125,46 +125,44 @@ int fIOthread(void * iodata)
PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr;
if(plug_mgr == 0 || core == 0)
{
- dfout << "Something horrible happened to the plugin manager in Core's constructor..." << std::endl;
+ core->con << "Something horrible happened to the plugin manager in Core's constructor..." << std::endl;
return 0;
}
- fprintf(dfout_C,"DFHack is ready. Have a nice day! Type in '?' or 'help' for help.\n");
+ core->con.print("DFHack is ready. Have a nice day! Type in '?' or 'help' for help.\n");
//dfterm << << endl;
int clueless_counter = 0;
while (true)
{
string command = "";
- //dfout <<"[DFHack]# ";
- char * line = linenoise("[DFHack]# ", dfout_C);
+ core->con.lineedit("[DFHack]# ",command);
+ core->con.history_add(command);
+ //core->con <<"[DFHack]# ";
+ //char * line = linenoise("[DFHack]# ", core->con.dfout_C);
// dfout <<"[DFHack]# ";
- if(line)
+ /*
+ if (line)
{
command=line;
linenoiseHistoryAdd(line);
free(line);
- }
+ }*/
//getline(cin, command);
- if (cin.eof())
- {
- command = "q";
- dfout << std::endl; // No newline from the user here!
- }
if(command=="help" || command == "?")
{
- dfout << "Available commands" << endl;
- dfout << "------------------" << endl;
+ core->con << "Available commands" << endl;
+ core->con << "------------------" << endl;
for(int i = 0; i < plug_mgr->size();i++)
{
const Plugin * plug = (plug_mgr->operator[](i));
if(!plug->size())
continue;
- dfout << "Plugin " << plug->getName() << " :" << std::endl;
+ core->con << "Plugin " << plug->getName() << " :" << std::endl;
for (int j = 0; j < plug->size();j++)
{
const PluginCommand & pcmd = (plug->operator[](j));
- dfout << setw(12) << pcmd.name << "| " << pcmd.description << endl;
+ core->con << setw(12) << pcmd.name << "| " << pcmd.description << endl;
}
- dfout << endl;
+ core->con << endl;
}
}
else if( command == "" )
@@ -186,18 +184,18 @@ int fIOthread(void * iodata)
command_result res = plug_mgr->InvokeCommand(first, parts);
if(res == CR_NOT_IMPLEMENTED)
{
- dfout << "Invalid command." << endl;
+ core->con << "Invalid command." << endl;
clueless_counter ++;
}
else if(res == CR_FAILURE)
{
- dfout << "ERROR!" << endl;
+ core->con << "ERROR!" << endl;
}
}
}
if(clueless_counter == 3)
{
- dfout << "Do 'help' or '?' for the list of available commands." << endl;
+ core->con << "Do 'help' or '?' for the list of available commands." << endl;
clueless_counter = 0;
}
}
@@ -206,7 +204,6 @@ int fIOthread(void * iodata)
Core::Core()
{
// init the console. This must be always the first step!
- con = 0;
plug_mgr = 0;
vif = 0;
p = 0;
@@ -228,13 +225,13 @@ Core::Core()
bool Core::Init()
{
// init the console. This must be always the first step!
- con = new Console();
+ con.init();
// find out what we are...
vif = new DFHack::VersionInfoFactory("Memory.xml");
p = new DFHack::Process(vif);
if (!p->isIdentified())
{
- dfout << "Couldn't identify this version of DF." << std::endl;
+ con << "Couldn't identify this version of DF." << std::endl;
errorstate = true;
delete p;
p = NULL;
@@ -246,7 +243,7 @@ bool Core::Init()
AccessMutex = SDL_CreateMutex();
if(!AccessMutex)
{
- dfout << "Mutex creation failed." << std::endl;
+ con << "Mutex creation failed." << std::endl;
errorstate = true;
return false;
}
@@ -255,7 +252,7 @@ bool Core::Init()
plug_mgr = new PluginManager(this);
if(!plug_mgr)
{
- dfout << "Failed to create the Plugin Manager." << std::endl;
+ con << "Failed to create the Plugin Manager." << std::endl;
errorstate = true;
return false;
}
@@ -367,11 +364,7 @@ int Core::Shutdown ( void )
}
allModules.clear();
memset(&(s_mods), 0, sizeof(s_mods));
- dfout << std::endl;
- // kill the console object
- if(con)
- delete con;
- con = 0;
+ con.shutdown();
return -1;
}
diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp
index be57ca1b..77515f89 100644
--- a/library/PluginManager.cpp
+++ b/library/PluginManager.cpp
@@ -47,7 +47,6 @@ static int getdir (string dir, vector<string> &files)
struct dirent *dirp;
if((dp = opendir(dir.c_str())) == NULL)
{
- dfout << "Error(" << errno << ") opening " << dir << endl;
return errno;
}
while ((dirp = readdir(dp)) != NULL) {
@@ -81,20 +80,20 @@ Plugin::Plugin(Core * core, const std::string & file)
DFLibrary * plug = OpenPlugin(file.c_str());
if(!plug)
{
- dfout << "Can't load plugin " << filename << endl;
+ core->con << "Can't load plugin " << filename << endl;
return;
}
const char * (*_PlugName)() =(const char * (*)()) LookupPlugin(plug, "plugin_name");
if(!_PlugName)
{
- dfout << "Plugin " << filename << " has no name." << endl;
+ core->con << "Plugin " << filename << " has no name." << endl;
ClosePlugin(plug);
return;
}
plugin_init = (command_result (*)(Core *, std::vector <PluginCommand> &)) LookupPlugin(plug, "plugin_init");
if(!plugin_init)
{
- dfout << "Plugin " << filename << " has no init function." << endl;
+ core->con << "Plugin " << filename << " has no init function." << endl;
ClosePlugin(plug);
return;
}
diff --git a/library/depends/libnoise/linenoise.cpp b/library/depends/libnoise/linenoise.cpp
index 863590f5..5cbf4825 100644
--- a/library/depends/libnoise/linenoise.cpp
+++ b/library/depends/libnoise/linenoise.cpp
@@ -112,7 +112,8 @@ char **history = NULL;
static void linenoiseAtExit(void);
int linenoiseHistoryAdd(const char *line);
-static int isUnsupportedTerm(void) {
+static int isUnsupportedTerm(void)
+{
char *term = getenv("TERM");
int j;
@@ -286,7 +287,47 @@ void linenoiseClearScreen(void) {
/* nothing to do, just to avoid warning. */
}
}
-
+/*
+static int selread(int fd, char *buf, size_t buflen)
+{
+ int n;
+ int start = 0;
+ fd_set input;
+ struct timeval timeout;
+ // Initialize the input set
+ FD_ZERO(&input);
+ FD_SET(fd, &input);
+
+ repeat_select:
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ n = select(fd+1, &input, NULL, NULL, &timeout);
+ if(n == 0 || n == -1 && errno == EINTR)
+ {
+ // lock mutex
+ // copy end variable
+ // unlock mutex
+ // if end, return -2
+ // else
+ goto repeat_select;
+ }
+ else if (n > 0)
+ {
+ int nread = read(fd,buf + start,buflen);
+ if (nread == -1)
+ return -1; // it's an ugly error
+ else if(nread < buflen)
+ {
+ start += nread;
+ buflen -= nread;
+ goto repeat_select;
+ }
+ else return nread + start;
+ }
+ else
+ return -1;
+}
+*/
static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
size_t plen = strlen(prompt);
size_t pos = 0;
@@ -538,7 +579,8 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt, FILE * out
return count;
}
-char *linenoise(const char *prompt, FILE * out) {
+char *linenoise(const char *prompt, FILE * out)
+{
char buf[LINENOISE_MAX_LINE];
int count;
@@ -575,11 +617,13 @@ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
}
/* Using a circular buffer is smarter, but a bit more complex to handle. */
-int linenoiseHistoryAdd(const char *line) {
+int linenoiseHistoryAdd(const char *line)
+{
char *linecopy;
if (history_max_len == 0) return 0;
- if (history == NULL) {
+ if (history == NULL)
+ {
history = (char**)malloc(sizeof(char*)*history_max_len);
if (history == NULL) return 0;
memset(history,0,(sizeof(char*)*history_max_len));
@@ -596,7 +640,8 @@ int linenoiseHistoryAdd(const char *line) {
return 1;
}
-int linenoiseHistorySetMaxLen(int len) {
+int linenoiseHistorySetMaxLen(int len)
+{
char **newHistory;
if (len < 1) return 0;
diff --git a/library/include/dfhack/Console.h b/library/include/dfhack/Console.h
index faa29c86..de8cfdca 100644
--- a/library/include/dfhack/Console.h
+++ b/library/include/dfhack/Console.h
@@ -25,19 +25,23 @@ distribution.
#pragma once
#include "dfhack/Pragma.h"
#include "dfhack/Export.h"
-#include "dfhack/Core.h"
#include "dfhack/extra/stdiostream.h"
-
-extern DFHACK_EXPORT duthomhas::stdiostream dfout;
-extern DFHACK_EXPORT FILE * dfout_C;
+#include <deque>
namespace DFHack
{
- class DFHACK_EXPORT Console
+ class Private;
+ class DFHACK_EXPORT Console : public duthomhas::stdiostream
{
public:
Console();
~Console();
+ /// initialize the console
+ bool init( void );
+ /// shutdown the console
+ bool shutdown( void );
+ /// Print a formatted string, like printf
+ int print(const char * format, ...);
/// Clear the console, along with its scrollback
void clear();
/// Position cursor at x,y. 1,1 = top left corner
@@ -50,5 +54,23 @@ namespace DFHack
void cursor(bool enable = true);
/// Waits given number of milliseconds before continuing.
void msleep(unsigned int msec);
+ /// get the current number of columns
+ int get_columns(void);
+ /// get the current number of rows
+ int get_rows(void);
+ /// beep. maybe?
+ //void beep (void);
+ /// A simple line edit (raw mode)
+ int lineedit(const std::string& prompt, std::string& output);
+ /// add a command to the history
+ void history_add(const std::string& command);
+ private:
+ int prompt_loop(const std::string & prompt, std::string & buffer);
+ void prompt_refresh( const std::string & prompt, const std::string & buffer, size_t pos);
+ int enable_raw();
+ void disable_raw();
+ std::deque <std::string> history;
+ void history_clear();
+ Private * d;
};
} \ No newline at end of file
diff --git a/library/include/dfhack/Core.h b/library/include/dfhack/Core.h
index 1f9615d9..381d6819 100644
--- a/library/include/dfhack/Core.h
+++ b/library/include/dfhack/Core.h
@@ -31,7 +31,7 @@ distribution.
#include <stack>
#include <map>
#include <stdint.h>
-
+#include "dfhack/Console.h"
namespace DFHack
{
@@ -112,7 +112,7 @@ namespace DFHack
DFHack::Process * p;
DFHack::VersionInfo * vinfo;
- DFHack::Console * con;
+ DFHack::Console con;
private:
Core();
bool Init();
diff --git a/plugins/cleanmap.cpp b/plugins/cleanmap.cpp
index 01cb530e..132b8bd4 100644
--- a/plugins/cleanmap.cpp
+++ b/plugins/cleanmap.cpp
@@ -51,7 +51,7 @@ DFhackCExport command_result cleanmap (Core * c, vector <string> & parameters)
// init the map
if(!Mapz->Start())
{
- dfout << "Can't init map." << std::endl;
+ c->con << "Can't init map." << std::endl;
c->Resume();
return CR_FAILURE;
}
diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp
index 639f1900..9c78264d 100644
--- a/plugins/prospector.cpp
+++ b/plugins/prospector.cpp
@@ -41,7 +41,7 @@ struct compare_pair_second
};
-void printMats(MatMap &mat, std::vector<DFHack::t_matgloss> &materials)
+void printMats(DFHack::Console & con, MatMap &mat, std::vector<DFHack::t_matgloss> &materials)
{
unsigned int total = 0;
MatSorter sorting_vector;
@@ -54,15 +54,15 @@ void printMats(MatMap &mat, std::vector<DFHack::t_matgloss> &materials)
{
if(it->first >= materials.size())
{
- dfout << "Bad index: " << it->first << " out of " << materials.size() << endl;
+ con << "Bad index: " << it->first << " out of " << materials.size() << endl;
continue;
}
DFHack::t_matgloss mat = materials[it->first];
- dfout << std::setw(25) << mat.id << " : " << it->second << std::endl;
+ con << std::setw(25) << mat.id << " : " << it->second << std::endl;
total += it->second;
}
- dfout << ">>> TOTAL = " << total << std::endl << std::endl;
+ con << ">>> TOTAL = " << total << std::endl << std::endl;
}
DFhackCExport command_result prospector (Core * c, vector <string> & parameters);
@@ -98,7 +98,7 @@ DFhackCExport command_result prospector (DFHack::Core * c, vector <string> & par
DFHack::Maps *maps = c->getMaps();
if (!maps->Start())
{
- dfout << "Cannot get map info!" << std::endl;
+ c->con << "Cannot get map info!" << std::endl;
c->Resume();
return CR_FAILURE;
}
@@ -108,13 +108,13 @@ DFhackCExport command_result prospector (DFHack::Core * c, vector <string> & par
DFHack::Materials *mats = c->getMaterials();
if (!mats->ReadInorganicMaterials())
{
- dfout << "Unable to read inorganic material definitons!" << std::endl;
+ c->con << "Unable to read inorganic material definitons!" << std::endl;
c->Resume();
return CR_FAILURE;
}
if (showPlants && !mats->ReadOrganicMaterials())
{
- dfout << "Unable to read organic material definitons; plants won't be listed!" << std::endl;
+ c->con << "Unable to read organic material definitons; plants won't be listed!" << std::endl;
showPlants = false;
}
@@ -134,21 +134,21 @@ DFhackCExport command_result prospector (DFHack::Core * c, vector <string> & par
if (!(showSlade && maps->ReadGlobalFeatures(globalFeatures)))
{
- dfout << "Unable to read global features; slade won't be listed!" << std::endl;
+ c->con << "Unable to read global features; slade won't be listed!" << std::endl;
}
if (!maps->ReadLocalFeatures(localFeatures))
{
- dfout << "Unable to read local features; adamantine "
- << (showTemple ? "and demon temples " : "")
- << "won't be listed!" << std::endl;
+ c->con << "Unable to read local features; adamantine "
+ << (showTemple ? "and demon temples " : "")
+ << "won't be listed!" << std::endl;
}
uint32_t vegCount = 0;
DFHack::Vegetation *veg = c->getVegetation();
if (showPlants && !veg->Start())
{
- dfout << "Unable to read vegetation; plants won't be listed!" << std::endl;
+ c->con << "Unable to read vegetation; plants won't be listed!" << std::endl;
}
for(uint32_t z = 0; z < z_max; z++)
@@ -217,7 +217,7 @@ DFhackCExport command_result prospector (DFHack::Core * c, vector <string> & par
if (!info)
{
- dfout << "Bad type: " << type << std::endl;
+ c->con << "Bad type: " << type << std::endl;
continue;
}
@@ -306,39 +306,39 @@ DFhackCExport command_result prospector (DFHack::Core * c, vector <string> & par
MatMap::const_iterator it;
- dfout << "Base materials:" << std::endl;
+ c->con << "Base materials:" << std::endl;
for (it = baseMats.begin(); it != baseMats.end(); ++it)
{
- dfout << std::setw(25) << DFHack::TileMaterialString[it->first] << " : " << it->second << std::endl;
+ c->con << std::setw(25) << DFHack::TileMaterialString[it->first] << " : " << it->second << std::endl;
}
- dfout << std::endl << "Layer materials:" << std::endl;
- printMats(layerMats, mats->inorganic);
+ c->con << std::endl << "Layer materials:" << std::endl;
+ printMats(c->con, layerMats, mats->inorganic);
- dfout << "Vein materials:" << std::endl;
- printMats(veinMats, mats->inorganic);
+ c->con << "Vein materials:" << std::endl;
+ printMats(c->con, veinMats, mats->inorganic);
if (showPlants)
{
- dfout << "Shrubs:" << std::endl;
- printMats(plantMats, mats->organic);
- dfout << "Wood in trees:" << std::endl;
- printMats(treeMats, mats->organic);
+ c->con << "Shrubs:" << std::endl;
+ printMats(c->con, plantMats, mats->organic);
+ c->con << "Wood in trees:" << std::endl;
+ printMats(c->con, treeMats, mats->organic);
}
if (hasAquifer)
{
- dfout << "Has aquifer" << std::endl;
+ c->con << "Has aquifer" << std::endl;
}
if (hasDemonTemple)
{
- dfout << "Has demon temple" << std::endl;
+ c->con << "Has demon temple" << std::endl;
}
if (hasLair)
{
- dfout << "Has lair" << std::endl;
+ c->con << "Has lair" << std::endl;
}
// Cleanup
@@ -349,6 +349,6 @@ DFhackCExport command_result prospector (DFHack::Core * c, vector <string> & par
mats->Finish();
maps->Finish();
c->Resume();
- dfout << std::endl;
+ c->con << std::endl;
return CR_OK;
}
diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp
index 01cefb0d..474cdee5 100644
--- a/plugins/reveal.cpp
+++ b/plugins/reveal.cpp
@@ -104,7 +104,7 @@ DFhackCExport command_result reveal(DFHack::Core * c, std::vector<std::string> &
if(revealed != NOT_REVEALED)
{
- dfout << "Map is already revealed or this is a different map." << std::endl;
+ c->con << "Map is already revealed or this is a different map." << std::endl;
return CR_FAILURE;
}
@@ -115,20 +115,20 @@ DFhackCExport command_result reveal(DFHack::Core * c, std::vector<std::string> &
World->ReadGameMode(gm);
if(gm.g_mode != GAMEMODE_DWARF)
{
- dfout << "Only in fortress mode." << std::endl;
+ c->con << "Only in fortress mode." << std::endl;
c->Resume();
return CR_FAILURE;
}
if(!Maps->Start())
{
- dfout << "Can't init map." << std::endl;
+ c->con << "Can't init map." << std::endl;
c->Resume();
return CR_FAILURE;
}
if(no_hell && !Maps->StartFeatures())
{
- dfout << "Unable to read local features; can't reveal map safely" << std::endl;
+ c->con << "Unable to read local features; can't reveal map safely" << std::endl;
c->Resume();
return CR_FAILURE;
}
@@ -175,10 +175,10 @@ DFhackCExport command_result reveal(DFHack::Core * c, std::vector<std::string> &
World->SetPauseState(true);
}
c->Resume();
- dfout << "Map revealed." << std::endl;
+ c->con << "Map revealed." << std::endl;
if(!no_hell)
- dfout << "Unpausing can unleash the forces of hell, so it has been temporarily disabled." << std::endl;
- dfout << "Run 'unreveal' to revert to previous state." << std::endl;
+ c->con << "Unpausing can unleash the forces of hell, so it has been temporarily disabled." << std::endl;
+ c->con << "Run 'unreveal' to revert to previous state." << std::endl;
return CR_OK;
}
@@ -187,7 +187,7 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
DFHack::designations40d designations;
if(!revealed)
{
- dfout << "There's nothing to revert!" << std::endl;
+ c->con << "There's nothing to revert!" << std::endl;
return CR_FAILURE;
}
c->Suspend();
@@ -197,14 +197,14 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
World->ReadGameMode(gm);
if(gm.g_mode != GAMEMODE_DWARF)
{
- dfout << "Only in fortress mode." << std::endl;
+ c->con << "Only in fortress mode." << std::endl;
c->Resume();
return CR_FAILURE;
}
Maps = c->getMaps();
if(!Maps->Start())
{
- dfout << "Can't init map." << std::endl;
+ c->con << "Can't init map." << std::endl;
c->Resume();
return CR_FAILURE;
}
@@ -214,7 +214,7 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
Maps->getSize(x_max_b,y_max_b,z_max_b);
if(x_max != x_max_b || y_max != y_max_b || z_max != z_max_b)
{
- dfout << "The map is not of the same size..." << std::endl;
+ c->con << "The map is not of the same size..." << std::endl;
c->Resume();
return CR_FAILURE;
}
@@ -233,7 +233,7 @@ DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string>
// give back memory.
hidesaved.clear();
revealed = NOT_REVEALED;
- dfout << "Map hidden!" << std::endl;
+ c->con << "Map hidden!" << std::endl;
c->Resume();
return CR_OK;
}
diff --git a/plugins/vdig.cpp b/plugins/vdig.cpp
index 934825fb..5fcbed96 100644
--- a/plugins/vdig.cpp
+++ b/plugins/vdig.cpp
@@ -49,7 +49,7 @@ DFhackCExport command_result vdig (Core * c, vector <string> & parameters)
// init the map
if(!Maps->Start())
{
- dfout << "Can't init map. Make sure you have a map loaded in DF." << std::endl;
+ c->con << "Can't init map. Make sure you have a map loaded in DF.\n";
c->Resume();
return CR_FAILURE;
}
@@ -61,14 +61,14 @@ DFhackCExport command_result vdig (Core * c, vector <string> & parameters)
Gui->getCursorCoords(cx,cy,cz);
while(cx == -30000)
{
- dfout << "Cursor is not active. Point the cursor at a vein." << std::endl;
+ c->con << "Cursor is not active. Point the cursor at a vein.\n";
c->Resume();
return CR_FAILURE;
}
DFHack::DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz);
if(xy.x == 0 || xy.x == tx_max - 1 || xy.y == 0 || xy.y == ty_max - 1)
{
- dfout << "I won't dig the borders. That would be cheating!" << std::endl;
+ c->con << "I won't dig the borders. That would be cheating!\n";
c->Resume();
return CR_FAILURE;
}
@@ -78,12 +78,12 @@ DFhackCExport command_result vdig (Core * c, vector <string> & parameters)
int16_t veinmat = MCache->veinMaterialAt(xy);
if( veinmat == -1 )
{
- dfout << "This tile is not a vein." << std::endl;
+ c->con << "This tile is not a vein.\n";
delete MCache;
c->Resume();
return CR_FAILURE;
}
- fprintf(dfout_C,"%d/%d/%d tiletype: %d, veinmat: %d, designation: 0x%x ... DIGGING!\n", cx,cy,cz, tt, veinmat, des.whole);
+ c->con.print("%d/%d/%d tiletype: %d, veinmat: %d, designation: 0x%x ... DIGGING!\n", cx,cy,cz, tt, veinmat, des.whole);
stack <DFHack::DFCoord> flood;
flood.push(xy);
diff --git a/plugins/weather.cpp b/plugins/weather.cpp
index cf2833ab..0e2ed180 100644
--- a/plugins/weather.cpp
+++ b/plugins/weather.cpp
@@ -58,7 +58,7 @@ DFhackCExport command_result weather (Core * c, vector <string> & parameters)
}
if(lock && unlock)
{
- dfout << "Lock or unlock? DECIDE!" << std::endl;
+ c->con << "Lock or unlock? DECIDE!" << std::endl;
return CR_FAILURE;
}
int cnt = 0;
@@ -67,7 +67,7 @@ DFhackCExport command_result weather (Core * c, vector <string> & parameters)
cnt += clear;
if(cnt > 1)
{
- dfout << "Rain, snow or clear sky? DECIDE!" << std::endl;
+ c->con << "Rain, snow or clear sky? DECIDE!" << std::endl;
return CR_FAILURE;
}
bool something = lock || unlock || rain || snow || clear;
@@ -75,14 +75,14 @@ DFhackCExport command_result weather (Core * c, vector <string> & parameters)
DFHack::World * w = c->getWorld();
if(!w->wmap)
{
- dfout << "Weather support seems broken :(" << std::endl;
+ c->con << "Weather support seems broken :(" << std::endl;
c->Resume();
return CR_FAILURE;
}
if(!something)
{
// paint weather map
- dfout << "Weather map (C = clear, R = rain, S = snow):" << std::endl;
+ c->con << "Weather map (C = clear, R = rain, S = snow):" << std::endl;
for(int y = 0; y<5;y++)
{
for(int x = 0; x<5;x++)
@@ -90,20 +90,20 @@ DFhackCExport command_result weather (Core * c, vector <string> & parameters)
switch((*w->wmap)[x][y])
{
case DFHack::CLEAR:
- dfout << "C ";
+ c->con << "C ";
break;
case DFHack::RAINING:
- dfout << "R ";
+ c->con << "R ";
break;
case DFHack::SNOWING:
- dfout << "S ";
+ c->con << "S ";
break;
default:
- dfout << (int) (*w->wmap)[x][y] << " ";
+ c->con << (int) (*w->wmap)[x][y] << " ";
break;
}
}
- dfout << std::endl;
+ c->con << std::endl;
}
}
else
@@ -111,17 +111,17 @@ DFhackCExport command_result weather (Core * c, vector <string> & parameters)
// weather changing action!
if(rain)
{
- dfout << "Here comes the rain." << std::endl;
+ c->con << "Here comes the rain." << std::endl;
w->SetCurrentWeather(RAINING);
}
if(snow)
{
- dfout << "Snow everywhere!" << std::endl;
+ c->con << "Snow everywhere!" << std::endl;
w->SetCurrentWeather(SNOWING);
}
if(clear)
{
- dfout << "Suddenly, sunny weather!" << std::endl;
+ c->con << "Suddenly, sunny weather!" << std::endl;
w->SetCurrentWeather(CLEAR);
}
// FIXME: weather lock needs map ID to work reliably... needs to be implemented.