summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorRodrigo Arias Mallo <rodarima@gmail.com>2024-12-10 22:30:12 +0100
committerRodrigo Arias Mallo <rodarima@gmail.com>2024-12-10 22:30:12 +0100
commit429d5f88b94ff28416cbfc6420b6389fa284df97 (patch)
treefb6fdaf7731de1ef396f98b748c56f3149801c84 /common
Import RTFL 0.1.1v0.1.1
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am30
-rw-r--r--common/about.cc109
-rw-r--r--common/about.hh30
-rw-r--r--common/fltk_lines.cc140
-rw-r--r--common/fltk_lines.hh52
-rw-r--r--common/lines.cc362
-rw-r--r--common/lines.hh121
-rw-r--r--common/parser.cc250
-rw-r--r--common/parser.hh47
-rw-r--r--common/rtfl_findrepeat.cc534
-rw-r--r--common/rtfl_tee.c249
-rw-r--r--common/tools.cc264
-rw-r--r--common/tools.hh80
13 files changed, 2268 insertions, 0 deletions
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 0000000..b8e1313
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,30 @@
+# Notes about libraries: "librtfl-tools.a" contains everything not
+# depending on FLTK, which can so be used in command line tools;
+# "librtfl-common.a" depens on FLTK, and also on the former.
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)
+
+noinst_LIBRARIES = librtfl-common.a librtfl-tools.a
+
+bin_PROGRAMS = rtfl-findrepeat rtfl-tee
+
+librtfl_common_a_SOURCES = \
+ about.hh \
+ about.cc \
+ fltk_lines.hh \
+ fltk_lines.cc
+
+librtfl_tools_a_SOURCES = \
+ lines.hh \
+ lines.cc \
+ parser.hh \
+ parser.cc \
+ tools.hh \
+ tools.cc
+
+rtfl_findrepeat_SOURCES = rtfl_findrepeat.cc
+
+rtfl_findrepeat_LDADD = librtfl-tools.a ../lout/liblout.a
+
+rtfl_tee_SOURCES = rtfl_tee.c
diff --git a/common/about.cc b/common/about.cc
new file mode 100644
index 0000000..01ad3ac
--- /dev/null
+++ b/common/about.cc
@@ -0,0 +1,109 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "about.hh"
+
+#include "config.h"
+
+#include <FL/Fl_Return_Button.H>
+#include <FL/Fl_Box.H>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+namespace rtfl {
+
+namespace common {
+
+void AboutWindow::close (Fl_Widget *widget, void *data)
+{
+ ((AboutWindow*)data)->hide ();
+}
+
+
+AboutWindow::AboutWindow (const char *prgName, const char *licenceException,
+ int height) :
+ Fl_Window (WIDTH, height, "")
+{
+
+ const char *titleFmt = "RTFL: About %s";
+ const char *textFmt =
+ "%s " VERSION "\n"
+ "\n"
+ "%s is part of RTFL (Read The Figurative Logfile).\n"
+ "\n"
+ "Copyright 2013-2015 Sebastian Geerken <sgeerken@@dillo.org>\n"
+ "\n"
+ "RTFL is free software; you can redistribute it and/or modify it under "
+ "the terms of the GNU General Public License as published by the Free "
+ "Software Foundation; either version 3 of the License, or (at your "
+ "option) any later version%s.\n"
+ "\n"
+ "With RTFL comes some documentation, see “doc/rtfl.html” in the "
+ "tarball. For more informations, updates etc. see "
+ "<http://home.gna.org/rtfl/>.";
+
+ int titleLen = strlen (titleFmt) - 2 + strlen (prgName) + 1;
+ title = new char [titleLen];
+ snprintf (title, titleLen, titleFmt, prgName);
+ label (title);
+
+ char *capName = strdup (prgName);
+ capName[0] = toupper (capName[0]);
+ int textLen =
+ strlen (textFmt) - 2 + strlen (prgName) - 2 + strlen (capName) + 1
+ - 2 + strlen (licenceException);
+ text = new char[textLen];
+ snprintf (text, textLen, textFmt, prgName, capName, licenceException);
+ free (capName);
+
+ Fl_Box *textWidget =
+ new Fl_Box(SPACE, SPACE, WIDTH - 2 * SPACE,
+ height - 3 * SPACE - BUTTON_HEIGHT, text);
+ textWidget->box(FL_NO_BOX);
+ textWidget->align(FL_ALIGN_WRAP);
+
+ Fl_Return_Button *close =
+ new Fl_Return_Button(WIDTH - BUTTON_WIDTH - SPACE,
+ height - BUTTON_HEIGHT - SPACE, BUTTON_WIDTH,
+ BUTTON_HEIGHT, "Close");
+ close->callback (AboutWindow::close, this);
+}
+
+AboutWindow::~AboutWindow ()
+{
+ delete[] title;
+ delete[] text;
+}
+
+} // namespace common
+
+} // namespace rtfl
diff --git a/common/about.hh b/common/about.hh
new file mode 100644
index 0000000..2a46483
--- /dev/null
+++ b/common/about.hh
@@ -0,0 +1,30 @@
+#ifndef __COMMON_ABOUT_HH__
+#define __COMMON_ABOUT_HH__
+
+#include <FL/Fl_Window.H>
+
+namespace rtfl {
+
+namespace common {
+
+class AboutWindow: public Fl_Window
+{
+private:
+ char *title, *text;
+
+ static void close (Fl_Widget *widget, void *data);
+
+ enum { WIDTH = 450, BUTTON_WIDTH = 80, BUTTON_HEIGHT = 25, SPACE = 10 };
+
+public:
+ enum { HEIGHT_SIMPLE = 300, HEIGHT_EXCEPTION = 480 };
+
+ AboutWindow (const char *prgName, const char *licenceException, int height);
+ ~AboutWindow ();
+};
+
+} // namespace common
+
+} // namespace rtfl
+
+#endif // __COMMON_ABOUT_HH__
diff --git a/common/fltk_lines.cc b/common/fltk_lines.cc
new file mode 100644
index 0000000..5bf4b00
--- /dev/null
+++ b/common/fltk_lines.cc
@@ -0,0 +1,140 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fltk_lines.hh"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <Fl/Fl.H>
+
+using namespace lout::container::typed;
+
+namespace rtfl {
+
+namespace common {
+
+// -------------------------
+// FltkLinesSource
+// -------------------------
+
+FltkLinesSource::TimeoutInfo::TimeoutInfo (FltkLinesSource *source, int type)
+{
+ this->source = source;
+ this->type = type;
+}
+
+FltkLinesSource::FltkLinesSource ()
+{
+ timeoutInfos = new List<TimeoutInfo> (true);
+}
+
+FltkLinesSource::~FltkLinesSource ()
+{
+ delete timeoutInfos;
+}
+
+void FltkLinesSource::staticProcessInputCallback (int fd, void *data)
+{
+ ((FltkLinesSource*)data)->processInputCallback (fd);
+}
+
+void FltkLinesSource::processInputCallback (int fd)
+{
+ int n = processInput (fd);
+
+ if (n == 0) {
+ // We read non-blocking, so -1 is returned and (errno set to
+ // EAGAIN) when no data is currently available. When 0 is
+ // returned, this means that there is permanently no data
+ // (typically that the tested program has terminated). For some
+ // reasons, the cpu is hogged then; this is avoided by removing
+ // the read function again.
+ Fl::remove_fd(0, FL_READ);
+ getSink()->finish ();
+ }
+}
+
+void FltkLinesSource::setup (tools::LinesSink *sink)
+{
+ setSink (sink);
+
+ int flags = fcntl(0, F_GETFL, 0);
+ fcntl(0, F_SETFL, flags | O_NONBLOCK);
+
+ Fl::add_fd(0, FL_READ, staticProcessInputCallback, (void*)this);
+}
+
+void FltkLinesSource::addTimeout (double secs, int type)
+{
+ TimeoutInfo *timeoutInfo = new TimeoutInfo (this, type);
+ timeoutInfos->append (timeoutInfo);
+ Fl::add_timeout(secs, timeoutCallback, timeoutInfo);
+}
+
+void FltkLinesSource::timeoutCallback (void *data)
+{
+ TimeoutInfo *timeoutInfo = (TimeoutInfo*) data;
+ timeoutInfo->getSource()->getSink()->timeout (timeoutInfo->getType ());
+ timeoutInfo->getSource()->timeoutInfos->removeRef (timeoutInfo);
+}
+
+void FltkLinesSource::removeTimeout (int type)
+{
+ // Iterators will not work when the set is modified; hence this nested loop.
+ bool found;
+ do {
+ found = false;
+ for (Iterator<TimeoutInfo> it = timeoutInfos->iterator ();
+ !found && it.hasNext (); ) {
+ TimeoutInfo *timeout = it.getNext();
+ if (timeout->getType () == type) {
+ found = true;
+ Fl::remove_timeout(timeoutCallback, timeout);
+ timeoutInfos->removeRef (timeout);
+ }
+ }
+ } while (found);
+}
+
+// ---------------------------
+// FltkDefaultSource
+// ---------------------------
+
+FltkDefaultSource::FltkDefaultSource (): LinesSourceSequence (true)
+{
+ int fd = open (".rtfl", O_RDONLY);
+ if (fd != -1)
+ add (new tools::BlockingLinesSource (fd));
+
+ add (new FltkLinesSource ());
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/common/fltk_lines.hh b/common/fltk_lines.hh
new file mode 100644
index 0000000..424ab82
--- /dev/null
+++ b/common/fltk_lines.hh
@@ -0,0 +1,52 @@
+#ifndef __COMMON_FLTK_LINES_HH__
+#define __COMMON_FLTK_LINES_HH__
+
+#include "lines.hh"
+
+namespace rtfl {
+
+namespace common {
+
+class FltkLinesSource: public tools::FileLinesSource
+{
+ class TimeoutInfo: public lout::object::Object
+ {
+ private:
+ FltkLinesSource *source;
+ int type;
+
+ public:
+ TimeoutInfo (FltkLinesSource *source, int type);
+
+ inline FltkLinesSource *getSource () { return source; }
+ inline int getType () { return type; }
+ };
+
+ lout::container::typed::List<TimeoutInfo> *timeoutInfos;
+
+ static void staticProcessInputCallback (int fd, void *data);
+ static void timeoutCallback (void *data);
+ void processInputCallback (int fd);
+
+public:
+ FltkLinesSource ();
+ ~FltkLinesSource ();
+
+ void setup (tools::LinesSink *sink);
+ void addTimeout (double secs, int type);
+ void removeTimeout (int type);
+};
+
+
+class FltkDefaultSource: public tools::LinesSourceSequence
+{
+public:
+ FltkDefaultSource ();
+};
+
+
+} // namespace common
+
+} // namespace rtfl
+
+#endif // __COMMON_FLTK_LINES_HH__
diff --git a/common/lines.cc b/common/lines.cc
new file mode 100644
index 0000000..e6fa8c2
--- /dev/null
+++ b/common/lines.cc
@@ -0,0 +1,362 @@
+/*
+ * RTFL
+ *
+ * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "lines.hh"
+#include "tools.hh"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <sys/timeb.h>
+
+#if 0
+# define PRINT(fmt) printf ("---- [%p] " fmt "\n", this)
+# define PRINTF(fmt, ...) printf ("---- [%p] " fmt "\n", this, __VA_ARGS__)
+#else
+# define PRINT(fmt)
+# define PRINTF(fmt, ...)
+#endif
+
+using namespace lout::container::typed;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace tools {
+
+// -----------------------------
+// LinesSourceSequence
+// -----------------------------
+
+LinesSourceSequence::VirtualSink::VirtualSink ()
+{
+}
+
+void LinesSourceSequence::VirtualSink::processLine (char *line)
+{
+ sequence->sink->processLine (line);
+}
+
+void LinesSourceSequence::VirtualSink::setLinesSource (LinesSource *source)
+{
+}
+
+void LinesSourceSequence::VirtualSink::finish ()
+{
+ // If a child source calls sink->finish() within setup(), this is
+ // called recursively, but this does not cause problems.
+
+ if (sequence->iterator.hasNext ()) {
+ LinesSource *source = sequence->iterator.getNext ();
+ source->setup (this);
+ } else {
+ sequence->sink->finish ();
+ }
+}
+
+void LinesSourceSequence::VirtualSink::timeout (int type)
+{
+ sequence->sink->timeout (type);
+}
+
+LinesSourceSequence::LinesSourceSequence (bool ownerOfSources)
+{
+ virtualSink.sequence = this;
+ sources = new List<LinesSource> (ownerOfSources);
+ setupCalled = false;
+}
+
+LinesSourceSequence::~LinesSourceSequence ()
+{
+ delete sources;
+}
+
+void LinesSourceSequence::add (LinesSource *source)
+{
+ assert (!setupCalled);
+ sources->append (source);
+}
+
+void LinesSourceSequence::setup (LinesSink *sink)
+{
+ this->sink = sink;
+ sink->setLinesSource (this);
+ setupCalled = true;
+ iterator = sources->iterator ();
+ virtualSink.finish ();
+}
+
+void LinesSourceSequence::addTimeout (double secs, int type)
+{
+ // TODO: After calling this, no source should be added.
+ // TODO: Processed timeouts must be removed from other sources as well?
+
+ // Sent to all, even if only one child source will actually trigger the
+ // timeout; but we do not know which one.
+
+ // In the real world, LinesSourceSequence is used for ".rtfl" and stdin, so
+ // we do not have to worry too much about correctly handling timeouts.
+
+ for (Iterator<LinesSource> it = sources->iterator (); it.hasNext (); ) {
+ it.getNext()->addTimeout (secs, type);
+ }
+}
+
+void LinesSourceSequence::removeTimeout (int type)
+{
+ for (Iterator<LinesSource> it = sources->iterator (); it.hasNext (); ) {
+ it.getNext()->removeTimeout (type);
+ }
+}
+
+// -------------------------
+// FileLinesSource
+// -------------------------
+
+FileLinesSource::FileLinesSource ()
+{
+ bufPos = 0;
+ completeLine = true;
+}
+
+int FileLinesSource::processInput (int fd)
+{
+ int n;
+ if ((n = read (fd, buf + bufPos, MAX_LINE_SIZE - bufPos)) > 0) {
+ int bytesAvail = bufPos + n;
+ int startOfLine = 0;
+ bool lineProcessed;
+
+ //printf ("--> %d bytes read, %d available\n", n, bytesAvail);
+
+ do {
+ lineProcessed = false;
+ for (int i = startOfLine; !lineProcessed && i < bytesAvail; i++) {
+ if (buf[i] == '\n') {
+ buf[i] = 0;
+
+ // If lines are too long (see below, where completeLine
+ // is set to false), they are not processed.
+ if (completeLine)
+ sink->processLine (buf + startOfLine);
+
+ lineProcessed = true;
+ startOfLine = i + 1;
+
+ completeLine = true;
+ }
+ }
+ } while (lineProcessed);
+
+ memmove (buf, buf + startOfLine, bytesAvail - startOfLine);
+ bufPos = bytesAvail - startOfLine;
+
+ PRINTF ("processInput: %d bytes left in buffer", bufPos);
+
+ // Handle case when line is to large (> MAX_LINE_SIZE
+ // bytes). The whole line is discarded (completeLine), so we
+ // empty the buffer by setting bufPos to 0.
+ if (bufPos == MAX_LINE_SIZE) {
+ bufPos = 0;
+ completeLine = false;
+ }
+
+ //printf (" --> %d processed, new pos: %d; will read %d\n",
+ // startOfLine, bufPos, MAX_LINE_SIZE - bufPos);
+ }
+
+ //printf (" --> read(2) returns %d\n", n);
+
+ return n;
+}
+
+// -----------------------------
+// BlockingLinesSource
+// -----------------------------
+
+BlockingLinesSource::TimeoutInfo::TimeoutInfo (long time, int type)
+{
+ this->time = time;
+ this->type = type;
+}
+
+bool BlockingLinesSource::TimeoutInfo::equals(Object *other)
+{
+ return time == ((TimeoutInfo*)other)->time &&
+ type == ((TimeoutInfo*)other)->type;
+}
+
+int BlockingLinesSource::TimeoutInfo::hashValue()
+{
+ // This should better be hidden in lout::objects. Cf. Pointer::hashValue().
+#if SIZEOF_LONG == 4
+ return (int)time ^ type;
+#else
+ return ((intptr_t)time >> 32) ^ ((intptr_t)time) ^ type;
+#endif
+}
+
+BlockingLinesSource::BlockingLinesSource (int fd)
+{
+ this->fd = fd;
+ timeoutInfos = new HashSet<TimeoutInfo> (true);
+}
+
+BlockingLinesSource::~BlockingLinesSource ()
+{
+ delete timeoutInfos;
+}
+
+void BlockingLinesSource::setup (LinesSink *sink)
+{
+ setSink (sink);
+
+ // We read non-blocking so that select(2) will work properly.
+ // (FileLinesSource::processInput would block otherwise.)
+ int flags = fcntl(0, F_GETFL, 0);
+ fcntl(0, F_SETFL, flags | O_NONBLOCK);
+
+ bool eos = false;
+ while (!eos) {
+ fd_set readfds;
+ FD_ZERO (&readfds);
+ FD_SET (fd, &readfds);
+
+ TimeoutInfo *nextTimeout = getNextTimeoutInfo ();
+
+ struct timeval tv, *tvp;
+ if (nextTimeout == NULL) {
+ tvp = NULL;
+ PRINT ("no timeout");
+ } else {
+ long tdelta = max (nextTimeout->getTime () - getCurrentTime (), 0L);
+ tv.tv_sec = tdelta / 1000;
+ tv.tv_usec = (tdelta % 1000) * 1000;
+ tvp = &tv;
+ PRINTF ("waiting %ld (%ld, %ld)", tdelta, tv.tv_sec, tv.tv_usec);
+ }
+
+ PRINT (">> processTimeouts");
+ processTimeouts ();
+ PRINT ("<< processTimeouts");
+
+ PRINT (">> select");
+ if (select (fd + 1, &readfds, NULL, NULL, tvp) == -1)
+ syserr ("select failed");
+ PRINT ("<< select");
+
+ processTimeouts ();
+
+ if (FD_ISSET (fd, &readfds)) {
+ PRINT (">> processInput");
+ int n = processInput (fd);
+ PRINT ("<< processInput");
+ if (n == 0) {
+ eos = true;
+ }
+ }
+ }
+
+ close (fd);
+ sink->finish ();
+}
+
+void BlockingLinesSource::addTimeout (double secs, int type)
+{
+ PRINTF ("addTimeout (%g, %d)", secs, type);
+ timeoutInfos->put (new TimeoutInfo (getCurrentTime () + secs * 1000, type));
+}
+
+void BlockingLinesSource::removeTimeout (int type)
+{
+ PRINTF ("removeTimeout (%d)", type);
+
+ // Iterators will not work when the set is modified; hence this nested loop.
+ bool found;
+ do {
+ found = false;
+ for (Iterator<TimeoutInfo> it = timeoutInfos->iterator ();
+ !found && it.hasNext (); ) {
+ TimeoutInfo *timeout = it.getNext();
+ if (timeout->getType () == type) {
+ found = true;
+ timeoutInfos->remove (timeout);
+ }
+ }
+ } while (found);
+}
+
+long BlockingLinesSource::getCurrentTime ()
+{
+ struct timeb t;
+ if (ftime (&t) == -1)
+ syserr ("ftime() failed");
+ return t.time * 1000L + t.millitm;
+}
+
+BlockingLinesSource::TimeoutInfo *BlockingLinesSource::getNextTimeoutInfo ()
+{
+ TimeoutInfo *nextTimeout = NULL;
+
+ for (Iterator<TimeoutInfo> it = timeoutInfos->iterator ();
+ it.hasNext (); ) {
+ TimeoutInfo *timeout = it.getNext();
+ if (nextTimeout == NULL ||
+ timeout->getTime () < nextTimeout->getTime ())
+ nextTimeout = timeout;
+ }
+
+ return nextTimeout;
+}
+
+void BlockingLinesSource::processTimeouts ()
+{
+ long currentTime = getCurrentTime ();
+
+ while (true) {
+ TimeoutInfo *nextTimeout = getNextTimeoutInfo ();
+ if (nextTimeout == NULL)
+ break;
+
+ PRINTF ("processTimeouts: %ld > %ld? %s",
+ nextTimeout->getTime (), currentTime,
+ nextTimeout->getTime () > currentTime ? "yes" : "no");
+ if (nextTimeout->getTime () > currentTime)
+ break;
+
+ PRINT ("processTimeouts: call timeout");
+
+ getSink()->timeout (nextTimeout->getType ());
+ timeoutInfos->remove (nextTimeout);
+ }
+}
+
+} // namespace tools
+
+} // namespace rtfl
diff --git a/common/lines.hh b/common/lines.hh
new file mode 100644
index 0000000..d3bfc75
--- /dev/null
+++ b/common/lines.hh
@@ -0,0 +1,121 @@
+#ifndef __COMMON_LINES_HH__
+#define __COMMON_LINES_HH__
+
+#include "lout/object.hh"
+#include "lout/container.hh"
+
+namespace rtfl {
+
+namespace tools {
+
+class LinesSource;
+
+class LinesSink: public lout::object::Object
+{
+public:
+ virtual void setLinesSource (LinesSource *source) = 0;
+ virtual void processLine (char *line) = 0;
+ virtual void timeout (int type) = 0;
+ virtual void finish () = 0;
+};
+
+
+class LinesSource: public lout::object::Object
+{
+public:
+ virtual void setup (LinesSink *sink) = 0;
+ virtual void addTimeout (double secs, int type) = 0;
+ virtual void removeTimeout (int type) = 0;
+};
+
+
+class LinesSourceSequence: public LinesSource
+{
+private:
+ class VirtualSink: public LinesSink
+ {
+ public:
+ LinesSourceSequence *sequence;
+
+ VirtualSink ();
+ void setLinesSource (LinesSource *source);
+ void processLine (char *line);
+ void timeout (int type);
+ void finish ();
+ };
+
+ VirtualSink virtualSink;
+ LinesSink *sink;
+ lout::container::typed::List<LinesSource> *sources;
+ bool setupCalled;
+ lout::container::typed::Iterator<LinesSource> iterator;
+
+public:
+ LinesSourceSequence (bool ownerOfSources);
+ ~LinesSourceSequence ();
+ void add (LinesSource *source);
+ void setup (LinesSink *sink);
+ void addTimeout (double secs, int type);
+ void removeTimeout (int type);
+};
+
+
+class FileLinesSource: public LinesSource
+{
+private:
+ enum { MAX_LINE_SIZE = 1000 };
+
+ tools::LinesSink *sink;
+ char buf[MAX_LINE_SIZE + 1];
+ int bufPos;
+ bool completeLine;
+
+protected:
+ FileLinesSource ();
+
+ int processInput (int fd);
+ inline void setSink (LinesSink *sink) {
+ this->sink = sink; sink->setLinesSource (this); }
+ inline LinesSink *getSink () { return sink; }
+};
+
+
+class BlockingLinesSource: public FileLinesSource
+{
+private:
+ class TimeoutInfo: public lout::object::Object
+ {
+ private:
+ long time;
+ int type;
+
+ public:
+ TimeoutInfo (long time, int type);
+ bool equals(Object *other);
+ int hashValue();
+
+ inline long getTime () { return time; }
+ inline int getType () { return type; }
+ };
+
+ int fd;
+ lout::container::typed::HashSet<TimeoutInfo> *timeoutInfos;
+
+ long getCurrentTime ();
+ TimeoutInfo *getNextTimeoutInfo ();
+ void processTimeouts ();
+
+public:
+ BlockingLinesSource (int fd);
+ ~BlockingLinesSource ();
+ void setup (LinesSink *sink);
+ void addTimeout (double secs, int type);
+ void removeTimeout (int type);
+};
+
+
+} // namespace tools
+
+} // namespace rtfl
+
+#endif // __COMMON_LINES_HH__
diff --git a/common/parser.cc b/common/parser.cc
new file mode 100644
index 0000000..3311e45
--- /dev/null
+++ b/common/parser.cc
@@ -0,0 +1,250 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "parser.hh"
+
+#include <string.h>
+#include <ctype.h>
+
+namespace rtfl {
+
+namespace tools {
+
+void Parser::setLinesSource (LinesSource *source)
+{
+}
+
+void Parser::processLine (char *line)
+{
+ char *lineCopy = strdup (line);
+
+ if (strncmp (lineCopy, "[rtfl]", 6) == 0) {
+ // Pre-version: starts with "[rtfl]".
+
+ char **parts = split (lineCopy + 6, 5);
+
+ if (parts[1] && parts[2] && parts[3]) {
+ // Notice that parts[4] (arguments) is allowed to be NULL here.
+ CommonLineInfo info =
+ { parts[0], atoi(parts[1]), atoi(parts[2]), line };
+ processCommand (&info, parts[3], parts[4]);
+ } else
+ fprintf (stderr, "Incomplete line:\n%s\n", line);
+
+ freeSplit (parts);
+ } else if (strncmp (lineCopy, "[rtfl-", 6) == 0) {
+ // Versioned: starts with "[rtfl-<module>-<major>.<minor>]".
+
+ int i = 6;
+ while (isalpha (lineCopy[i]))
+ i++;
+
+ if (lineCopy[i] != '-')
+ fprintf (stderr, "Expected '-' after module:\n%s\n", line);
+ else {
+ char *module = new char[i - 6 + 1];
+ memcpy (module, lineCopy + 6, (i - 6) * sizeof (char));
+ module[i - 6] = 0;
+
+ i++;
+ if (!isdigit (lineCopy[i]))
+ fprintf (stderr, "Missing major version:\n%s\n", line);
+ else {
+ int majorVersion = 0, minorVersion = 0;
+
+ while (isdigit (lineCopy[i])) {
+ majorVersion = 10 * majorVersion + (lineCopy[i] - '0');
+ i++;
+ }
+
+ if (majorVersion == 0)
+ fprintf (stderr, "Major version must be positive:\n%s\n", line);
+ else if (lineCopy[i] != '.')
+ fprintf (stderr, "Expected '.' after major version:\n%s\n",
+ line);
+ else if (!isdigit (lineCopy[i + 1]))
+ fprintf (stderr, "Missing minor version:\n%s\n", line);
+ else {
+ i++;
+ while (isdigit (lineCopy[i])) {
+ minorVersion = 10 * minorVersion + (lineCopy[i] - '0');
+ i++;
+ }
+
+ if (lineCopy[i] != ']')
+ fprintf (stderr, "Expected ']' after minor version:\n%s\n",
+ line);
+ else {
+ char **parts = splitEscaped (lineCopy + i + 1);
+
+ if (parts[1] && parts[2] && parts[3]) {
+ // Notice that parts[4] (first argument) is allowed to be
+ // NULL here.
+ CommonLineInfo info = { parts[0], atoi(parts[1]),
+ atoi(parts[2]), line };
+ processVCommand (&info, module, majorVersion, minorVersion,
+ parts[3], parts + 4);
+ } else
+ fprintf (stderr, "Incomplete line:\n%s\n", line);
+
+ freeSplitEscaped (parts);
+ }
+ }
+ }
+
+ delete[] module;
+ }
+ }
+
+ free (lineCopy);
+}
+
+void Parser::finish ()
+{
+}
+
+void Parser::timeout (int type)
+{
+}
+
+char **Parser::splitEscaped (char *txt)
+{
+ int numParts;
+ char **parts;
+
+ scanSplit (txt, &numParts, NULL);
+ parts = new char*[numParts + 1];
+ scanSplit (txt, NULL, parts);
+ parts[numParts] = NULL;
+
+ for (int i = 0; i < numParts; i++)
+ unquote (parts[i]);
+
+ return parts;
+}
+
+void Parser::scanSplit (char *txt, int *numParts, char **parts)
+{
+ int iChar, iPart;
+ bool quoted;
+
+ if (numParts)
+ *numParts = 1;
+
+ if (parts)
+ parts[0] = txt;
+
+ for (iChar = 0, iPart = 1; txt[iChar]; iChar++) {
+ if (txt[iChar] == '\\' && txt[iChar + 1]) {
+ iChar++;
+ quoted = true;
+ } else
+ quoted = false;
+
+ if (!quoted && txt[iChar] == ':') {
+ if (parts) {
+ txt[iChar] = 0;
+ parts[iPart] = txt + iChar + 1;
+ }
+
+ iPart++;
+
+ if (numParts)
+ (*numParts)++;
+ }
+ }
+}
+
+void Parser::unquote (char *txt)
+{
+ int i, j;
+ for (i = 0, j = 0; txt[i]; i++, j++) {
+ if (txt[i] == '\\' && txt[i + 1])
+ i++;
+ txt[j] = txt[i];
+ }
+ txt[j] = 0;
+}
+
+// Free result of splitEscaped().
+void Parser::freeSplitEscaped (char **parts)
+{
+ delete[] parts;
+}
+
+// Split without escaping.
+char **Parser::split (char *txt, int maxNum)
+{
+ // Only maxNum splits. If less parts are found, less parts are
+ // returned, so the caller should check the result (first part is
+ // always defined). Notice that the original text buffer (txt) is
+ // destroyed, for speed.
+
+ //printf ("===> split ('%s', %d)\n", txt, maxNum);
+
+ char **parts = new char*[maxNum + 1];
+
+ char *start = txt;
+ int i = 0;
+ while (i < maxNum) {
+ char *end = start;
+ while (*end != 0 && *end != ':') end++;
+ int endOfTxt = *end == 0;
+
+ //printf (" start '%s'\n", start);
+ //printf (" end '%s' (%d character(s))\n",
+ // end, (int)(end - start));
+
+ parts[i] = start;
+
+ if (i < maxNum -1)
+ *end = 0;
+
+ //printf ("---> %d: '%s'\n", i, start);
+
+ i++;
+ if (endOfTxt)
+ break;
+
+ start = endOfTxt ? end : end + 1;
+ }
+
+ parts[i] = NULL;
+ return parts;
+}
+
+// Free result of split().
+void Parser::freeSplit (char **parts)
+{
+ delete[] parts;
+}
+
+} // namespace tools
+
+} // namespace rtfl
diff --git a/common/parser.hh b/common/parser.hh
new file mode 100644
index 0000000..30cf02d
--- /dev/null
+++ b/common/parser.hh
@@ -0,0 +1,47 @@
+#ifndef __COMMON_PARSER_HH__
+#define __COMMON_PARSER_HH__
+
+#include "lines.hh"
+
+namespace rtfl {
+
+namespace tools {
+
+struct CommonLineInfo
+{
+ char *fileName;
+ int lineNo;
+ int processId;
+ char *completeLine;
+};
+
+class Parser: public LinesSink
+{
+private:
+ char **splitEscaped (char *txt);
+ void scanSplit (char *txt, int *numParts, char **parts);
+ static void unquote (char *txt);
+ void freeSplitEscaped (char **parts);
+
+protected:
+ char **split (char *txt, int maxNum);
+ void freeSplit (char **parts);
+
+ virtual void processCommand (CommonLineInfo *info, char *cmd, char *args)
+ = 0;
+ virtual void processVCommand (CommonLineInfo *info, const char *module,
+ int majorVersion, int minorVersion,
+ const char *cmd, char **args) = 0;
+
+public:
+ void setLinesSource (LinesSource *source);
+ void processLine (char *line);
+ void finish ();
+ void timeout (int type);
+};
+
+} // namespace common
+
+} // namespace rtfl
+
+#endif // __COMMON_PARSER_HH__
diff --git a/common/rtfl_findrepeat.cc b/common/rtfl_findrepeat.cc
new file mode 100644
index 0000000..d464db8
--- /dev/null
+++ b/common/rtfl_findrepeat.cc
@@ -0,0 +1,534 @@
+/*
+ * RTFL
+ *
+ * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This program searches for identical sequences in a stream of RTFL
+ * messages (actually, any stream) and marks the beginnings and ends
+ * with an RTFL mark (obj-mark). You should first filter out other
+ * lines, so run
+ *
+ * $ ... | grep '^\[rtfl[^\]]*\]' | rtfl-findrepeat
+ *
+ * or use the script rtfl-objfilter.
+ *
+ * For options, see printHelp().
+ *
+ * Warning: This program is highly experimental, and especially rather
+ * inefficient. Some ideas:
+ *
+ * - When finding a suitable lenght ("-l find"), a smaller number of
+ * lines is in many cases sufficient, so use "head".
+ *
+ * - Unless the length is searched for ("-l find"), hashing multiple
+ * lines (as many as are searched as minimum), as done in the
+ * searching algorithm by Rabin and Karp, may increase the speed.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include "tools.hh"
+#include "../lout/object.hh"
+#include "../lout/container.hh"
+
+using namespace lout::misc;
+using namespace lout::object;
+using namespace lout::container::typed;
+
+enum { MAX_LINE_SIZE = 1000 };
+
+class Region: public Comparable
+{
+ int first, num;
+
+public:
+ inline Region (int first, int num) { this->first = first; this->num = num; }
+
+ bool equals (Object *other);
+ int hashValue ();
+ void intoStringBuffer (StringBuffer *sb);
+ int compareTo(Comparable *other);
+
+ inline int getFirst () { return first; }
+ inline int getNum () { return num; }
+ inline Region *cloneRegion () { return new Region (first, num); }
+
+ inline bool subSetOf (Region *other)
+ { return first >= other->first && first + num <= other->first + other->num; }
+};
+
+class Mark: public Object
+{
+public:
+ enum Type { START, END };
+
+private:
+ Type type;
+ int majorNo, minorNo, length;
+
+public:
+ inline Mark (Type type, int majorNo, int minorNo, int length)
+ { this->type = type; this->majorNo = majorNo; this->minorNo = minorNo;
+ this->length = length; }
+
+ void intoStringBuffer (StringBuffer *sb);
+
+ inline Type getType () { return type; }
+ inline int getMajorNo () { return majorNo; }
+ inline int getMinorNo () { return minorNo; }
+ inline int getLength () { return length; }
+};
+
+bool Region::equals (Object *other)
+{
+ Region *otherRegion = (Region*)other;
+ return first == otherRegion->first && num == otherRegion->num;
+}
+
+int Region::hashValue ()
+{
+ return first ^ num;
+}
+
+void Region::intoStringBuffer (StringBuffer *sb)
+{
+ char buf[32];
+
+ sb->append ("(");
+ snprintf (buf, 32, "%d", first);
+ sb->append (buf);
+ sb->append ("...");
+ snprintf (buf, 32, "%d", first + num - 1);
+ sb->append (buf);
+ sb->append (")");
+}
+
+void Mark::intoStringBuffer (StringBuffer *sb)
+{
+ char buf[32];
+
+
+ sb->append ("(");
+ sb->append (type == START ? "START" : "END");
+ sb->append (" / ");
+ snprintf (buf, 32, "%d", majorNo);
+ sb->append (buf);
+ sb->append (" / ");
+ snprintf (buf, 32, "%d", minorNo);
+ sb->append (buf);
+ sb->append (")");
+}
+
+int Region::compareTo(Comparable *other)
+{
+ Region *otherRegion = (Region*)other;
+ return first - otherRegion->first;
+}
+
+// ----------------------------------------------------------------------
+
+static bool debug = false;
+
+static void printHelp (const char *argv0)
+{
+ fprintf
+ (stderr, "Usage: %s <options>\n"
+ "\n"
+ "Options:\n"
+ " -l <n> Search for sequence of at least <n> lines.\n"
+ " -c <n> Search for sequence repeated at least <n> times.\n"
+ "\n"
+ "If an arguments is 'f' or 'find', the maximal value for this is\n"
+ "determined (possibly with the other argument set to a concrete\n"
+ "number).\n"
+ "\n"
+ "See RTFL documentation for more details.\n",
+ argv0);
+}
+
+// ----------------------------------------------------------------------
+
+static void readFile (FILE *file, Vector<String> *lines,
+ HashTable<String, Vector<Integer> > *lineNosByLines)
+{
+ char buf[MAX_LINE_SIZE + 1];
+
+ for (int lineNo = 0; fgets (buf, MAX_LINE_SIZE + 1, file); lineNo++) {
+ size_t l = strlen (buf);
+ if (buf[l - 1] == '\n') buf[l - 1] = 0;
+
+ String *line = new String (buf);
+
+ Vector<Integer> *lineNos = lineNosByLines->get (line);
+ if (lineNos == NULL) {
+ lineNos = new Vector<Integer> (1, true);
+ // Note: key is dublicated.
+ lineNosByLines->put (new String (buf), lineNos);
+ }
+ lineNos->put (new Integer (lineNo));
+
+ lines->put (line);
+ }
+}
+
+static int findRegions (Vector<String> *lines,
+ HashTable<String, Vector<Integer> > *lineNosByLines,
+ List <HashSet<Region> > *allSetsOfRegions,
+ int minLength, int minCount)
+{
+ int effMinLength = minLength == -1 ? 2 :minLength;
+ int effMinCount = minCount == -1 ? 2 : minCount;
+ int maxLength = 0, maxCount = 0;
+
+ HashTable<Region, HashSet<Region> > *setsOfRegionsByRegion =
+ new HashTable<Region, HashSet<Region> > (false, false);
+
+ List <HashSet<Region> > *tmpAllSetsOfRegions =
+ new List<HashSet<Region> > (false);
+
+ for (int lineNo1 = 0; lineNo1 < lines->size (); lineNo1++) {
+ String *line = lines->get (lineNo1);
+ Vector<Integer> *lineNos = lineNosByLines->get (line);
+
+ // Examine only lines after this.
+ Integer lineNo1Key (lineNo1);
+
+ for (int linesNoIndex = lineNos->bsearch (&lineNo1Key, true) + 1;
+ linesNoIndex < lineNos->size (); linesNoIndex++) {
+ int lineNo2 = lineNos->get(linesNoIndex)->getValue ();
+ int numMatching = 1;
+ while (lineNo2 + numMatching < lines->size () &&
+ lines->get(lineNo1 + numMatching)->equals
+ (lines->get(lineNo2 + numMatching))) {
+ numMatching++;
+
+ if (numMatching >= effMinLength) {
+ //printf ("equal: (%d...%d) and (%d...%d)\n",
+ // lineNo1, lineNo1 + numMatching - 1,
+ // lineNo2, lineNo2 + numMatching - 1);
+
+ Region r1 (lineNo1, numMatching), r2 (lineNo2, numMatching);
+ HashSet<Region> *setOfRegions;
+
+ if ((setOfRegions = setsOfRegionsByRegion->get (&r1))) {
+ if (!setsOfRegionsByRegion->contains (&r2)) {
+ assert (!setOfRegions->contains (&r2));
+ Region *rr2 = r2.cloneRegion ();
+ setOfRegions->put (rr2);
+ setsOfRegionsByRegion->put (rr2, setOfRegions);
+ }
+ } else if ((setOfRegions = setsOfRegionsByRegion->get (&r2))) {
+ if (!setsOfRegionsByRegion->contains (&r1)) {
+ assert (!setOfRegions->contains (&r1));
+ Region *rr1 = r1.cloneRegion ();
+ setOfRegions->put (rr1);
+ setsOfRegionsByRegion->put (rr1, setOfRegions);
+ }
+ } else {
+ Region *rr1 = r1.cloneRegion (), *rr2 = r2.cloneRegion ();
+ setOfRegions = new HashSet<Region> (false);
+ setOfRegions->put (rr1);
+ setOfRegions->put (rr2);
+ setsOfRegionsByRegion->put (rr1, setOfRegions);
+ setsOfRegionsByRegion->put (rr2, setOfRegions);
+ tmpAllSetsOfRegions->append (setOfRegions);
+ }
+
+ if (debug) {
+ StringBuffer sb;
+ setsOfRegionsByRegion->intoStringBuffer (&sb);
+ printf ("findRegions: setsOfRegionsByRegion = %s\n",
+ sb.getChars ());
+ }
+ }
+ }
+ }
+ }
+
+ delete setsOfRegionsByRegion;
+
+ for (Iterator<HashSet<Region> > it1 = tmpAllSetsOfRegions->iterator ();
+ it1.hasNext (); ) {
+ HashSet<Region> *set = it1.getNext ();
+ if (set->size () >= effMinCount) {
+ allSetsOfRegions->append (set);
+ maxCount = max (maxCount, set->size ());
+
+ if (minLength == -1) {
+ for (Iterator<Region> it2 = set->iterator (); it2.hasNext (); ) {
+ Region *r = it2.getNext ();
+ maxLength = max (maxLength, r->getNum ());
+ }
+ }
+ } else
+ delete set;
+ }
+
+ delete tmpAllSetsOfRegions;
+
+ if (minLength == -1)
+ return maxLength;
+ else if (minCount == -1)
+ return maxCount;
+ else
+ return -1;
+}
+
+static void sortListsOfRegions (List <HashSet<Region> > *allSetsOfRegions,
+ List <Vector<Region> > *allListsOfRegions)
+{
+ for (Iterator<HashSet<Region> > it1 = allSetsOfRegions->iterator ();
+ it1.hasNext (); ) {
+ HashSet<Region> *set = it1.getNext ();
+ Vector<Region> *list = new Vector<Region> (1, true);
+
+ for (Iterator<Region> it2 = set->iterator (); it2.hasNext (); ) {
+ Region *r = it2.getNext ();
+ list->put (r);
+ }
+
+ if (debug) {
+ StringBuffer sb;
+ list->intoStringBuffer (&sb);
+ printf ("sortListsOfRegions: list = %s\n", sb.getChars ());
+ }
+
+ list->sort ();
+ allListsOfRegions->append (list);
+ }
+}
+
+static void cleanupRegions (List <Vector <Region> > *allListsOfRegions)
+{
+ HashTable<List<Integer>, Vector<Vector<Region> > > *allListsOfLists =
+ new HashTable<List<Integer>, Vector<Vector<Region> > > (true, true);
+
+ for (Iterator<Vector <Region> > it = allListsOfRegions->iterator ();
+ it.hasNext (); ) {
+ Vector<Region> *list = it.getNext ();
+
+ List<Integer> *key = new List<Integer> (true);
+ for (int i = 1; i < list->size (); i++)
+ key->append (new Integer (list->get(i)->getFirst () -
+ list->get(i - 1)->getFirst ()));
+
+ Vector<Vector<Region> > *listOfLists = allListsOfLists->get (key);
+ if (listOfLists)
+ delete key;
+ else {
+ listOfLists = new Vector<Vector<Region> > (1, false);
+ allListsOfLists->put (key, listOfLists);
+ }
+
+ listOfLists->put (list);
+ }
+
+ allListsOfRegions->clear ();
+
+ if (debug) {
+ StringBuffer sb;
+ allListsOfLists->intoStringBuffer (&sb);
+ printf ("cleanupRegions: allListsOfLists = %s\n", sb.getChars ());
+ }
+
+ for (Iterator<List<Integer> > it = allListsOfLists->iterator ();
+ it.hasNext (); ) {
+ List<Integer> *key = it.getNext ();
+ Vector<Vector<Region> > *listOfLists = allListsOfLists->get (key);
+
+ if (debug) {
+ StringBuffer sb;
+ listOfLists->intoStringBuffer (&sb);
+ printf ("cleanupRegions: listOfLists = %s\n", sb.getChars ());
+ }
+
+ for (int i = 0; i < listOfLists->size (); i++) {
+ Vector<Region> *list1 = listOfLists->get (i);
+ Region *r1 = list1->get (0);
+ bool redundant = false;
+ for (int j = 0; j < listOfLists->size () && !redundant; j++) {
+ if (i != j) {
+ Vector<Region> *list2 = listOfLists->get (j);
+ if (list2 != NULL) {
+ Region *r2 = list2->get (0);
+ if (r1->subSetOf (r2))
+ redundant = true;
+ }
+ }
+ }
+
+ if (redundant) {
+ listOfLists->put (NULL, i);
+ delete list1;
+ } else
+ allListsOfRegions->append (list1);
+ }
+ }
+
+ delete allListsOfLists;
+}
+
+// ----------------------------------------------------------------------
+
+int main (int argc, char *argv[])
+{
+ int minLength = 2, minCount = 2;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "c:dl:")) != -1) {
+ switch (opt) {
+ case 'c':
+ if (strcmp (optarg, "f") == 0 || strcmp (optarg, "find") == 0)
+ minCount = -1;
+ else
+ minCount = atoi (optarg);
+ break;
+
+ case 'd':
+ debug = true;
+ break;
+
+ case 'l':
+ if (strcmp (optarg, "f") == 0 || strcmp (optarg, "find") == 0)
+ minLength = -1;
+ else
+ minLength = atoi (optarg);
+ break;
+
+ default:
+ printHelp (argv[0]);
+ return 1;
+ }
+ }
+
+ Vector<String> *lines = new Vector<String> (8, true);
+ HashTable<String, Vector<Integer> > *lineNosByLines =
+ new HashTable<String, Vector<Integer> > (true, true);
+
+ readFile (stdin, lines, lineNosByLines);
+
+ List <HashSet<Region> > *allSetsOfRegions =
+ new List<HashSet<Region> > (true);
+
+ int numFound = findRegions (lines, lineNosByLines, allSetsOfRegions,
+ minLength, minCount);
+
+ if (debug) {
+ StringBuffer sb;
+ allSetsOfRegions->intoStringBuffer (&sb);
+ printf ("main: allSetsOfRegions = %s\n", sb.getChars ());
+ }
+
+ delete lineNosByLines;
+
+ if (numFound != -1) {
+ delete allSetsOfRegions;
+ printf ("%d\n", numFound);
+ } else {
+ List <Vector<Region> > *allListsOfRegions =
+ new List <Vector<Region> > (false); // TODO Memory leak!
+
+ sortListsOfRegions (allSetsOfRegions, allListsOfRegions);
+
+ delete allSetsOfRegions;
+
+ if (debug) {
+ StringBuffer sb;
+ allListsOfRegions->intoStringBuffer (&sb);
+ printf ("(a) main: allListsOfRegions = %s\n", sb.getChars ());
+ }
+
+ cleanupRegions (allListsOfRegions);
+
+ if (debug) {
+ StringBuffer sb;
+ allListsOfRegions->intoStringBuffer (&sb);
+ printf ("(b) main: allListsOfRegions = %s\n", sb.getChars ());
+ }
+
+ HashTable<Integer, List<Mark> > *marksByLineNo =
+ new HashTable<Integer, List<Mark> > (true, true);
+
+ int majorNo = 0;
+ for (Iterator<Vector<Region> > it1 = allListsOfRegions->iterator ();
+ it1.hasNext (); ) {
+ Vector<Region> *list = it1.getNext ();
+ int minorNo = 0;
+
+ for (Iterator<Region> it2 = list->iterator (); it2.hasNext (); ) {
+ Region *r = it2.getNext ();
+
+ for (int typeNo = 0; typeNo < 2; typeNo++) {
+ Mark::Type type = typeNo == 0 ? Mark::START : Mark::END;
+ int lineNo =
+ r->getFirst () + (type == Mark::START ? 0 : r->getNum ());
+ Integer lineNoKey (lineNo);
+
+ List<Mark> *list = marksByLineNo->get (&lineNoKey);
+ if (list == NULL) {
+ list = new List<Mark> (true);
+ marksByLineNo->put (new Integer (lineNo), list);
+ }
+
+ list->append (new Mark (type, majorNo, minorNo, r->getNum ()));
+ }
+
+
+ minorNo++;
+ }
+
+ majorNo++;
+ }
+
+ delete allListsOfRegions;
+
+ if (debug) {
+ StringBuffer sb;
+ marksByLineNo->intoStringBuffer (&sb);
+ printf ("main: marksByLineNo = %s\n", sb.getChars ());
+ }
+
+ for (int lineNo = 0; lineNo < lines->size (); lineNo++) {
+ Integer lineNoKey (lineNo);
+ List<Mark> *list = marksByLineNo->get (&lineNoKey);
+ if (list) {
+ for (Iterator<Mark> it = list->iterator (); it.hasNext (); ) {
+ Mark *m = it.getNext ();
+ char buf[200];
+ rtfl::tools::numToRoman (m->getMajorNo () + 1, buf,
+ sizeof (buf));
+ // Certainly no ':' or '\' in the message, so no quoting
+ // necessary.
+ printf ("[rtfl-obj-1.0]n:0:0:mark:findrepeat:findrepeat:0:"
+ "Sequence %s (length %d), %d%s occurence -- %s\n",
+ buf, m->getLength (), m->getMinorNo () + 1,
+ rtfl::tools::numSuffix (m->getMinorNo () + 1),
+ m->getType () == Mark::START ? "start" : "end");
+ }
+ }
+
+ String *line = lines->get (lineNo);
+ puts (line->chars ());
+ }
+
+ delete marksByLineNo;
+ }
+
+ delete lines;
+}
diff --git a/common/rtfl_tee.c b/common/rtfl_tee.c
new file mode 100644
index 0000000..4911b73
--- /dev/null
+++ b/common/rtfl_tee.c
@@ -0,0 +1,249 @@
+/*
+ * RTFL
+ *
+ * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Like tee(1), this program duplicates a stream; however, it does not
+ * write the copy to a file, but instead sends it via pipe to another
+ * program. Example:
+ *
+ * $ foo | rtfl-tee bar | qix
+ *
+ * Here, the standard output of "foo" is passed to the standard input
+ * of both "bar" and "qix".
+ *
+ * More informations in doc/rtfl.html.
+ *
+ * TODO: Something like "echo -n foo | rtfl-tee -b cat" does not work;
+ * since the line of the first "foo" is never finished, the copy of
+ * "foo" is never printed.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+static void usrerr (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, "\n");
+ exit (1);
+}
+
+static void syserr (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, ": %s\n", strerror (errno));
+ exit (1);
+}
+
+static ssize_t ewrite(int fd, const void *buf, size_t count)
+{
+ ssize_t w;
+ if ((w = write (fd, buf, count)) == -1)
+ syserr ("write(%d, ...) failed", fd);
+ return w;
+}
+
+static void writestdout (int orig, char *buf, size_t count)
+{
+ // Basic idea: "orig" denotes to 0 (stdin of rtfl-tee) or 1 (stdout
+ // of the called program). "curorig" refers to the origin which is
+ // currently printed, so than the data from the other origin must
+ // be buffered. "startline" is set to 1 at the beginning, or iff
+ // the last printed character was '\n'. (In this case, switching is
+ // simply possible.)
+
+ static int curorig = 0, startline = 1;
+ static char obuf[2048];
+ static size_t ocount = 0;
+
+ //printf ("\nwritestdout: %d, '%c...' (%d)\n",
+ // orig, count > 0 ? buf[0] : '.', (int)count);
+ //printf ("===> curorig = %d, ocount = %d, startline = %d\n",
+ // curorig, (int)ocount, startline);
+
+ if (count > 0) {
+ if (orig != curorig) {
+ if (startline) {
+ // Simple switching case.
+ ewrite (1, obuf, ocount);
+ ocount = 0;
+ curorig = orig;
+ ewrite (1, buf, count);
+ startline = buf[count - 1] == '\n';
+ } else {
+ // Buffer.
+ size_t odiff = min (count, ocount - sizeof (obuf));
+ memcpy (obuf + ocount, buf, odiff);
+ ocount += odiff;
+ }
+ } else {
+ if (ocount == 0) {
+ // Nothing buffered: simply print all data.
+ ewrite (1, buf, count);
+ startline = buf[count - 1] == '\n';
+ } else {
+ // Only print everything until the last newline character.
+ // (Note: printing everything until the *first* newline
+ // character whould make a larger buffer necessary, but,
+ // on the other hand, preserve better the original
+ // (temporal) order of the lines.)
+ ssize_t i, nl;
+ for (nl = -1, i = count - 1; nl == -1 && i >= 0; i--)
+ if (buf[i] == '\n') nl = i;
+
+ if (nl == -1) {
+ // No newline: no switch.
+ ewrite (1, buf, count);
+ startline = 0;
+ } else {
+ // Newline: switch.
+ ewrite (1, buf, nl + 1);
+ ewrite (1, obuf, ocount);
+ startline = obuf[ocount - 1] == '\n';
+ ocount = min (sizeof (obuf), count - (nl + 1));
+ memcpy (obuf, buf + nl + 1, ocount);
+ curorig = 1 - curorig;
+ }
+ }
+ }
+ }
+}
+
+int main (int argc, char *argv[])
+{
+ int parent2child[2], child2parent[2], i, offsetcmd, bypass = 0, erroropt = 0;
+ char *argv2[argc - 1 + 1];
+ int done1, done2;
+
+ for (offsetcmd = 1; offsetcmd < argc && argv[offsetcmd][0] == '-';
+ offsetcmd++) {
+ if (argv[offsetcmd][1] == 'b')
+ bypass = 1;
+ else if (argv[offsetcmd][1] == '-') {
+ offsetcmd++;
+ break;
+ } else
+ erroropt = 1;
+ }
+
+ if (erroropt || offsetcmd >= argc)
+ usrerr ("Usage: %s [-b] [--] <program> [<program options>]", argv[0]);
+
+ if (pipe (parent2child) == -1) syserr ("pipe failed");
+ if (bypass && pipe (child2parent) == -1) syserr ("pipe failed");
+
+ switch (fork ()) {
+ case -1:
+ syserr ("fork failed");
+ break;
+
+ case 0:
+ if (close (parent2child[1]) == -1)
+ syserr ("close(%d) failed", parent2child[0]);
+ if (close (0) == -1) syserr ("close(0) failed");
+ if (dup2 (parent2child[0], 0) == -1)
+ syserr ("dup2(%d, 0) failed", parent2child[0]);
+ if (close (parent2child[0]) == -1)
+ syserr ("close(%d) failed", parent2child[0]);
+
+ if (bypass) {
+ if (close (child2parent[0]) == -1)
+ syserr ("close(%d) failed", child2parent[1]);
+ if (close (1) == -1) syserr ("close(1) failed");
+ if (dup2 (child2parent[1], 1) == -1)
+ syserr ("dup2(%d, 1) failed", child2parent[1]);
+ if (close (child2parent[1]) == -1)
+ syserr ("close(%d) failed", child2parent[1]);
+ }
+
+ for (i = 0; i < argc - offsetcmd; i++) argv2[i] = argv[i + offsetcmd];
+ argv2[argc - offsetcmd] = NULL;
+ if (execvp (argv2[0], argv2) == -1)
+ syserr ("execvp(\"%s\", ...) failed", argv2[0]);
+ break;
+
+ default:
+ if (close (parent2child[0]) == -1)
+ syserr ("close(%d) failed", parent2child[0]);
+ if (bypass && close (child2parent[1]) == -1)
+ syserr ("close(%d) failed", child2parent[1]);
+
+ done1 = 0;
+ done2 = !bypass;
+ while (!done1 || !done2) {
+ //printf ("==> done1 = %d, done2 = %d\n", done1, done2);
+
+ fd_set set;
+ FD_ZERO (&set);
+
+ if (!done1) FD_SET (0, &set);
+ if (!done2) FD_SET (child2parent[0], &set);
+
+ int s = select ((!done2 ? child2parent[0] : 0) + 1,
+ &set, NULL, NULL, NULL);
+
+ //printf ("==> s = %d\n", s);
+
+ if (s == -1) syserr ("select failed");
+ else if (s > 0) {
+ char buf[2048];
+ ssize_t n;
+
+ if (!done1 && FD_ISSET(0, &set)) {
+ if ((n = read (0, buf, sizeof (buf))) == -1)
+ syserr ("read failed");
+ else if (n == 0) {
+ if (close (parent2child[1]) == -1)
+ syserr ("close(%d) failed", parent2child[1]);
+ done1 = 1;
+ } else if (n > 0) {
+ ewrite (parent2child[1], buf, n);
+ writestdout (0, buf, n);
+ }
+ }
+
+ if (!done2 && FD_ISSET (child2parent[0], &set)) {
+ if ((n = read (child2parent[0], buf, sizeof (buf))) == -1)
+ syserr ("read failed");
+ else if (n == 0) {
+ if (close (child2parent[0]) == -1)
+ syserr ("close(%d) failed", child2parent[0]);
+ done2 = 1;
+ } else if (n > 0)
+ writestdout (1, buf, n);
+ }
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
diff --git a/common/tools.cc b/common/tools.cc
new file mode 100644
index 0000000..7dfb412
--- /dev/null
+++ b/common/tools.cc
@@ -0,0 +1,264 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tools.hh"
+
+#include <stdio.h>
+#include <errno.h>
+
+using namespace lout::object;
+using namespace lout::container::untyped;
+
+namespace rtfl {
+
+namespace tools {
+
+const char *numSuffix (int n)
+{
+ if (n % 10 == 1 && n != 11)
+ return "st";
+ else if (n % 10 == 2 && n != 12)
+ return "nd";
+ else if (n % 10 == 3 && n != 13)
+ return "rd";
+ else
+ return "th";
+}
+
+static const char
+ *const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" },
+ *const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" },
+ *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" },
+ *const roman_I3[] = { "","M","MM","MMM","MMMM" };
+
+void numToRoman (int num, char *buf, int buflen)
+{
+ int i3, i2, i1, i0;
+
+ if (buflen <= 0)
+ return;
+
+ i0 = num;
+ i1 = i0/10; i2 = i1/10; i3 = i2/10;
+ i0 %= 10; i1 %= 10; i2 %= 10;
+ if (num < 0 || i3 > 4) /* more than 4999 elements ? */
+ snprintf(buf, buflen, "****");
+ else
+ snprintf(buf, buflen, "%s%s%s%s", roman_I3[i3], roman_I2[i2],
+ roman_I1[i1], roman_I0[i0]);
+}
+
+void syserr (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, ": %s\n", strerror (errno));
+ exit (1);
+}
+
+// ----------------------------------------------------------------------
+
+EquivalenceRelation::RefTarget::RefTarget (Object *object, bool ownerOfObject)
+{
+ this->object = object;
+ this->ownerOfObject = ownerOfObject;
+ refCount = 1;
+ allKeys = new HashSet (false);
+}
+
+EquivalenceRelation::RefTarget::~RefTarget ()
+{
+ if (ownerOfObject)
+ delete object;
+ delete allKeys;
+}
+
+// ----------------------------------------------------------------------
+
+EquivalenceRelation::RefSource::RefSource (Object *key, RefTarget *target)
+{
+ this->target = target;
+ this->key = key;
+ refTarget ();
+}
+
+EquivalenceRelation::RefSource::~RefSource ()
+{
+ unrefTarget ();
+}
+
+void EquivalenceRelation::RefSource::refTarget ()
+{
+ if (target) {
+ target->ref ();
+ target->putKey (key);
+ }
+}
+
+void EquivalenceRelation::RefSource::unrefTarget ()
+{
+ if (target) {
+ target->removeKey (key);
+ target->unref ();
+ target = NULL;
+ }
+}
+
+void EquivalenceRelation::RefSource::setTarget (RefTarget *target)
+{
+ if (target != this->target) {
+ unrefTarget ();
+ this->target = target;
+ refTarget ();
+ }
+}
+
+// ----------------------------------------------------------------------
+
+EquivalenceRelation::EquivalenceRelation (bool ownerOfKeys, bool ownerOfValues)
+{
+ this->ownerOfKeys = ownerOfKeys;
+ this->ownerOfValues = ownerOfValues;
+ sources = new HashTable (ownerOfKeys, true);
+}
+
+EquivalenceRelation::~EquivalenceRelation ()
+{
+ delete sources;
+}
+
+void EquivalenceRelation::put (Object *key, Object *value)
+{
+ assert (!contains(key));
+
+ RefTarget *target = new RefTarget (value, ownerOfValues);
+ RefSource *source = new RefSource (key, target);
+ target->unref ();
+ sources->put (key, source);
+}
+
+Object *EquivalenceRelation::get (Object *key) const
+{
+ RefSource *source = (RefSource*) sources->get(key);
+ if (source) {
+ Object *object = source->getTarget()->getObject ();
+ return object;
+ } else
+ return NULL;
+}
+
+bool EquivalenceRelation::contains (Object *key) const
+{
+ return sources->contains (key);
+}
+
+Iterator EquivalenceRelation::iterator ()
+{
+ return sources->iterator ();
+}
+
+Iterator EquivalenceRelation::relatedIterator (Object *key)
+{
+ assert (contains (key));
+
+ RefSource *source = (RefSource*) sources->get (key);
+ RefTarget *target = source->getTarget ();
+ return target->getAllKeys()->iterator ();
+}
+
+void EquivalenceRelation::relate (Object *key1, Object *key2)
+{
+ assert (contains(key1) && contains(key2));
+
+ RefSource *source1 = (RefSource*) sources->get (key1);
+ RefSource *source2 = (RefSource*) sources->get (key2);
+ if (source1->getTarget () != source2->getTarget ()) {
+ // The first value is kept, the second destroyed. The caller has
+ // to care about the order.
+
+ // Consider all keys already related to `key2`; this is possible by
+ // iterating over `RefTarget::allKeys`. To avoid accessing freed memory,
+ // copy all keys to a new temporary set.
+
+ HashSet target2Keys (false);
+ for (Iterator it = source2->getTarget()->getAllKeys()->iterator ();
+ it.hasNext (); )
+ target2Keys.put (it.getNext ());
+
+ for (Iterator it = target2Keys.iterator (); it.hasNext (); ) {
+ RefSource *otherSource = (RefSource*) sources->get (it.getNext ());
+ otherSource->setTarget (source1->getTarget ());
+ }
+ }
+}
+
+void EquivalenceRelation::putRelated (Object *oldKey, Object *newKey)
+{
+ assert (contains(oldKey) && !contains(newKey));
+
+ RefSource *oldSource = (RefSource*) sources->get (oldKey);
+ RefSource *newSource = new RefSource (newKey, oldSource->getTarget ());
+ sources->put (newKey, newSource);
+}
+
+void EquivalenceRelation::removeSimple (lout::object::Object *key)
+{
+ // The order is important: a simple "sources->remove (key)" will
+ // cause an access to freed memory.
+
+ RefSource *source = (RefSource*) sources->get (key);
+ source->setTarget (NULL); // Will unref() the target.
+ sources->remove (key);
+}
+
+void EquivalenceRelation::remove (Object *key)
+{
+ assert (contains (key));
+
+ RefSource *source = (RefSource*) sources->get (key);
+ RefTarget *target = source->getTarget ();
+ target->ref ();
+
+ for (Iterator it = target->getAllKeys()->iterator (); it.hasNext (); ) {
+ Object *otherKey = it.getNext ();
+
+ // The order is important: see removeSimple().
+ RefSource *otherSource = (RefSource*) sources->get (otherKey);
+ otherSource->setTarget (NULL); // Will unref() the target.
+ sources->remove (otherKey);
+ }
+
+ target->unref ();
+}
+
+
+} // namespace tools
+
+} // namespace dw
diff --git a/common/tools.hh b/common/tools.hh
new file mode 100644
index 0000000..af8e5e9
--- /dev/null
+++ b/common/tools.hh
@@ -0,0 +1,80 @@
+#ifndef __COMMON_TOOLS_HH__
+#define __COMMON_TOOLS_HH__
+
+#include "lout/object.hh"
+#include "lout/container.hh"
+
+namespace rtfl {
+
+namespace tools {
+
+const char *numSuffix (int n);
+void numToRoman (int num, char *buf, int buflen);
+void syserr (const char *fmt, ...);
+
+class EquivalenceRelation: public lout::object::Object {
+private:
+ class RefTarget: public lout::object::Object {
+ private:
+ bool ownerOfObject;
+ int refCount;
+ lout::object::Object *object;
+ lout::container::untyped::HashSet *allKeys;
+
+ public:
+ RefTarget (lout::object::Object *object, bool ownerOfObject);
+ ~RefTarget ();
+
+ inline lout::object::Object *getObject () { return object; }
+ inline void ref () { refCount++; }
+ inline void unref () { if (--refCount == 0) delete this; }
+
+ inline lout::container::untyped::HashSet *getAllKeys ()
+ { return allKeys; }
+ inline void putKey (Object *key) { allKeys->put (key); }
+ inline void removeKey (Object *key) { allKeys->remove (key); }
+ };
+
+
+ class RefSource: public lout::object::Object {
+ RefTarget *target;
+ lout::object::Object *key;
+
+ void refTarget ();
+ void unrefTarget ();
+
+ public:
+ RefSource (lout::object::Object *key, RefTarget *target);
+ ~RefSource ();
+
+ inline RefTarget *getTarget () { return target; }
+ void setTarget (RefTarget *target);
+ };
+
+ bool ownerOfKeys, ownerOfValues;
+ lout::container::untyped::HashTable *sources;
+
+ lout::container::untyped::HashSet *initSet (lout::object::Object *o);
+
+ public:
+ EquivalenceRelation (bool ownerOfKeys, bool ownerOfValues);
+ ~EquivalenceRelation ();
+
+ void put (lout::object::Object *key, lout::object::Object *value);
+ lout::object::Object *get (lout::object::Object *key) const;
+ bool contains (lout::object::Object *key) const;
+ lout::container::untyped::Iterator iterator ();
+ lout::container::untyped::Iterator relatedIterator (Object *key);
+
+ void relate (lout::object::Object *key1, lout::object::Object *key2);
+ void putRelated (lout::object::Object *oldKey, lout::object::Object *newKey);
+
+ void removeSimple (lout::object::Object *key);
+ void remove (lout::object::Object *key);
+};
+
+} // namespace tools
+
+} // namespace rtfl
+
+#endif // __COMMON_TOOLS_HH__