aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Geerken <devnull@localhost>2012-10-07 23:35:56 +0200
committerSebastian Geerken <devnull@localhost>2012-10-07 23:35:56 +0200
commit2cd28ca74309f81d295a0860a140e030ee7f80d3 (patch)
tree3d7463a12a2c17123ce15070eb44a8a8df9e2e3e
parent177612b46579076ca5a7ddaffb96083d40d0dc06 (diff)
parentd1179582ee86f08fbc23d2363df7a794324095e8 (diff)
Merge with main repo.
-rw-r--r--ChangeLog13
-rw-r--r--dillorc7
-rw-r--r--dlib/dlib.c1
-rw-r--r--doc/dillo.14
-rw-r--r--doc/user_help.html11
-rw-r--r--dpi/datauri.c5
-rw-r--r--dpi/vsource.c4
-rw-r--r--dw/Makefile.am1
-rw-r--r--dw/fltkplatform.cc4
-rw-r--r--dw/fltkui.cc8
-rw-r--r--dw/fltkviewbase.cc5
-rw-r--r--dw/hyphenator.cc365
-rw-r--r--dw/hyphenator.hh88
-rw-r--r--dw/table.hh20
-rw-r--r--dw/textblock.cc190
-rw-r--r--dw/textblock_iterator.cc219
-rw-r--r--dw/textblock_linebreaking.cc2
-rw-r--r--lout/misc.hh8
-rw-r--r--src/Makefile.am2
-rw-r--r--src/cache.c5
-rw-r--r--src/capi.c58
-rw-r--r--src/dillo.cc10
-rw-r--r--src/domain.c151
-rw-r--r--src/domain.h19
-rw-r--r--src/html.cc24
-rw-r--r--src/keys.cc1
-rw-r--r--src/keys.hh1
-rw-r--r--src/keysrc3
-rw-r--r--src/paths.cc4
-rw-r--r--src/paths.hh1
-rw-r--r--src/png.c4
-rw-r--r--src/prefs.c1
-rw-r--r--src/prefs.h4
-rw-r--r--src/prefsparser.cc11
-rw-r--r--src/ui.cc9
-rw-r--r--src/url.c16
-rw-r--r--test/Makefile.am10
-rw-r--r--test/trie.cc17
38 files changed, 900 insertions, 406 deletions
diff --git a/ChangeLog b/ChangeLog
index 8d7d7e8f..8a1a9e15 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,11 +10,22 @@ at http://hg.dillo.org/dillo
dillo-3.0.3 [not released yet]
+- Support for CSS display property
- Patch: Johannes Hofmann
+ - Packed trie to optimize hyphenator memory consumption.
+ Patches: Johannes Hofmann
+- Fix image input coordinates (BUG#1070)
- When location bar is given focus, temporarily show panels if hidden
(BUG#1093).
+ - Fix bug where data URI has charset but no media type.
+ - Bug meter line number fix for bare carriage returns.
+ - Add some more info to various bug meter messages.
Patches: corvid
++- Automatic hyphenation.
+ Patch: Sebastian Geerken
++- Added the "view-source" keybinding (default: Ctrl-U).
+ Patch: Alexander Voigt
++- Introduced the domainrc mechanism for finer-grained control than
+ filter_auto_requests had provided.
+ Patch: p37sitdu, corvid
-----------------------------------------------------------------------------
diff --git a/dillorc b/dillorc
index 0a882ef2..84461831 100644
--- a/dillorc
+++ b/dillorc
@@ -26,13 +26,6 @@
# (While browsing, this can be changed from the tools/settings menu.)
#parse_embedded_css=YES
-# How should Dillo restrict automatic requests (e.g., redirections,
-# pages containing images or stylesheets)?
-# allow_all
-# same_domain : Permit www.example.org to load an image from img.example.org,
-# but not from the unrelated ad.doubleclick.net.
-#filter_auto_requests=same_domain
-
# Change the buffering scheme for drawing
# 0 no double buffering - useful for debugging
# 1 light buffering using a single back buffer for all windows
diff --git a/dlib/dlib.c b/dlib/dlib.c
index cdc7ca62..ebdaf0cd 100644
--- a/dlib/dlib.c
+++ b/dlib/dlib.c
@@ -909,7 +909,6 @@ char *dGethomedir ()
/*
* Get a line from a FILE stream.
- * It handles backslash as line-continues character.
* Return value: read line on success, NULL on EOF.
*/
char *dGetline (FILE *stream)
diff --git a/doc/dillo.1 b/doc/dillo.1
index 18c932a2..2efc6b4a 100644
--- a/doc/dillo.1
+++ b/doc/dillo.1
@@ -1,4 +1,4 @@
-.TH dillo 1 "June 12, 2012" "" "USER COMMANDS"
+.TH dillo 1 "September 24, 2012" "" "USER COMMANDS"
.SH NAME
dillo \- web browser
.SH SYNOPSIS
@@ -36,7 +36,7 @@ Set initial window position where \fIGEO\fR is
Display this help text and exit.
.TP
\fB\-l\fR, \fB\-\-local\fR
-Don't load images for these URL(s).
+Don't load images or stylesheets for these URL(s).
.TP
\fB\-v\fR, \fB\-\-version\fR
Display version info and exit.
diff --git a/doc/user_help.html b/doc/user_help.html
index b8c3e580..d65170d9 100644
--- a/doc/user_help.html
+++ b/doc/user_help.html
@@ -14,7 +14,7 @@
<table width="100%" bgcolor="#8080f0" cellspacing="4" cellpadding="5"
border="0o">
- <tr><td><big><font color="#302080">Welcome to Dillo 3.0</font></big>
+ <tr><td><big><font color="#302080">Welcome to Dillo 3</font></big>
</table>
<p>
@@ -63,7 +63,7 @@
<tr><td BGCOLOR="#70a0c0">
<ul>
<li> You can scroll around your Dillo main window using
- CTRL+{PgUp|PgDwn|Home|End} or using the mouse middle button
+ {PgUp|PgDn|Home|End} or using the mouse middle button
or mouse wheel. If nothing happens when keys are pressed, try
<b> <font color="#5040a0">focusing</font></b> your Dillo main
window first by clicking it (not on any link!:)).
@@ -84,8 +84,8 @@
of the page currently viewed, but it will *not* reload embedded
images during this process.
- <li> Dialogs can be closed with the ESC key<br>
- (ESC also hides the findbar and control panels).
+ <li> Dialogs can be closed with the ESC key.
+ (ESC also hides the findbar and control panels)
<li> If you want to try a different control panel, right-click over the
tools button and select one that suits your needs, then make
@@ -136,7 +136,7 @@
the above rules apply for every word in it. </li>
</ul>
<p>
- Dillo will scroll the page and highlight found text!
+ Dillo will scroll the page and highlight found text.
<p>
<small>Default shortcut: [CTRL]+"F".</small>
@@ -302,6 +302,7 @@
<tr><td>Ctrl-T <td>Tab <td>New tab
<tr><td>Ctrl-W <td>Window <td>quit tab/window
<tr><td>Ctrl-O <td>Open <td>Open file
+<tr><td>Ctrl-U <td>(conventional) <td>view source
<tr><td>Ctrl-B <td>Bookmarks <td>view bookmarks
<tr><td>Ctrl-Q <td>Quit <td>Quit dillo
<tr><td>Back or "<b>,</b>" <td>< <td>previous page
diff --git a/dpi/datauri.c b/dpi/datauri.c
index f8e2ad94..055ecd9f 100644
--- a/dpi/datauri.c
+++ b/dpi/datauri.c
@@ -216,9 +216,6 @@ static void send_failure_message(const char *url, const char *mime_type,
/*
* Get mime type from the data URI.
- * TODO: there's no point in handling "charset" because current dillo
- * only handles ISO-LATIN-1. The FLTK2 version (utf-8) could use it in the
- * future.
*/
static char *datauri_get_mime(char *url)
{
@@ -243,7 +240,7 @@ static char *datauri_get_mime(char *url)
if (len == 0) {
mime_type = dStrdup("text/plain;charset=US-ASCII");
} else if (!dStrnAsciiCasecmp(buf, "charset", 7)) {
- mime_type = dStrconcat("text/plain", buf, NULL);
+ mime_type = dStrconcat("text/plain;", buf, NULL);
} else {
mime_type = dStrdup(buf);
}
diff --git a/dpi/vsource.c b/dpi/vsource.c
index adf08a83..2f1129cb 100644
--- a/dpi/vsource.c
+++ b/dpi/vsource.c
@@ -78,9 +78,9 @@ void send_numbered_text(Dsh *sh, int data_size)
while (*p) {
snprintf(line_str, 32, "%2d: ", line);
a_Dpip_dsh_write_str(sh, 0, line_str);
- if ((p = strchr(q, '\n'))) {
+ if ((p = strpbrk(q, "\r\n"))) {
a_Dpip_dsh_write(sh, 0, q, p - q + 1);
- if (p[1] == '\r')
+ if (*p == '\r' && p[1] == '\n')
++p;
++line;
} else {
diff --git a/dw/Makefile.am b/dw/Makefile.am
index 61c92b98..aa5c88c3 100644
--- a/dw/Makefile.am
+++ b/dw/Makefile.am
@@ -74,6 +74,7 @@ libDw_widgets_a_SOURCES = \
tablecell.cc \
tablecell.hh \
textblock.cc \
+ textblock_iterator.cc \
textblock_linebreaking.cc \
textblock.hh
diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc
index 099c449c..fecd76a7 100644
--- a/dw/fltkplatform.cc
+++ b/dw/fltkplatform.cc
@@ -123,8 +123,8 @@ FltkFont::FltkFont (core::style::FontAttrs *attrs)
font = family->get (fa);
fl_font(font, size);
- /* WORKAROUND: fl_width(uint_t) is not working on non-xft X.
- * Reported to FLTK as STR #2688 */
+ // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in
+ // 1.3.0 (STR #2688).
spaceWidth = misc::max(0, (int)fl_width(" ") + letterSpacing);
int xx, xy, xw, xh;
fl_text_extents("x", xx, xy, xw, xh);
diff --git a/dw/fltkui.cc b/dw/fltkui.cc
index f9e6f3f6..7d2f6cc5 100644
--- a/dw/fltkui.cc
+++ b/dw/fltkui.cc
@@ -568,8 +568,8 @@ void FltkEntryResource::sizeRequest (core::Requisition *requisition)
if (displayed() && style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font,font->size);
- /* WORKAROUND: fl_width(uint_t) is not working on non-xft X.
- * Reported to FLTK as STR #2688 */
+ // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in
+ // 1.3.0 (STR #2688).
requisition->width =
(int)fl_width ("n")
* (maxLength == UNLIMITED_MAX_LENGTH ? 10 : maxLength)
@@ -691,8 +691,8 @@ void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition)
if (style) {
FltkFont *font = (FltkFont*)style->font;
fl_font(font->font,font->size);
- /* WORKAROUND: fl_width(uint_t) is not working on non-xft X.
- * Reported to FLTK as STR #2688 */
+ // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in
+ // 1.3.0 (STR #2688).
requisition->width =
(int)fl_width ("n") * numCols + 2 * RELIEF_X_THICKNESS;
requisition->ascent =
diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc
index 240937e2..d9782a4e 100644
--- a/dw/fltkviewbase.cc
+++ b/dw/fltkviewbase.cc
@@ -472,8 +472,11 @@ void FltkViewBase::drawArc (core::style::Color *color,
int y = translateCanvasYToViewY (centerY) - height / 2;
fl_arc(x, y, width, height, angle1, angle2);
- if (filled)
+ if (filled) {
+ // WORKAROUND: We call both fl_arc and fl_pie due to a FLTK bug
+ // (STR #2703) that was present in 1.3.0.
fl_pie(x, y, width, height, angle1, angle2);
+ }
}
void FltkViewBase::drawPolygon (core::style::Color *color,
diff --git a/dw/hyphenator.cc b/dw/hyphenator.cc
index 97844538..62a47a73 100644
--- a/dw/hyphenator.cc
+++ b/dw/hyphenator.cc
@@ -2,9 +2,9 @@
#include "../lout/misc.hh"
#include "../lout/unicode.hh"
+#include <limits.h>
#include <stdio.h>
#include <string.h>
-#include <limits.h>
#define LEN 1000
@@ -25,26 +25,44 @@ HashTable <TypedPair <TypedPointer <core::Platform>, ConstString>,
Hyphenator> (true, true);
Hyphenator::Hyphenator (core::Platform *platform,
- const char *patFile, const char *excFile)
+ const char *patFile, const char *excFile, int pack)
{
this->platform = platform;
- tree = NULL; // As long we are not sure whether a pattern file can be read.
+ trie = NULL; // As long we are not sure whether a pattern file can be read.
- FILE *patF = fopen (patFile, "r");
- if (patF) {
- tree = new HashTable <Integer, Collection <Integer> > (true, true);
- while (!feof (patF)) {
- char buf[LEN + 1];
- char *s = fgets (buf, LEN, patF);
- if (s) {
- // TODO Better exit with an error, when the line is too long.
- int l = strlen (s);
- if (s[l - 1] == '\n')
- s[l - 1] = 0;
- insertPattern (s);
+ char buf[PATH_MAX + 1];
+ snprintf(buf, sizeof (buf), "%s.trie", patFile);
+ FILE *trieF = fopen (buf, "r");
+
+ if (trieF) {
+ trie = new Trie ();
+ if (trie->load (trieF) != 0) {
+ delete trie;
+ trie = NULL;
+ }
+ fclose (trieF);
+ }
+
+ if (trie == NULL) {
+ TrieBuilder trieBuilder(pack);
+ FILE *patF = fopen (patFile, "r");
+ if (patF) {
+
+ while (!feof (patF)) {
+ char buf[LEN + 1];
+ char *s = fgets (buf, LEN, patF);
+ if (s) {
+ // TODO Better exit with an error, when the line is too long.
+ int l = strlen (s);
+ if (s[l - 1] == '\n')
+ s[l - 1] = 0;
+ insertPattern (&trieBuilder, s);
+ }
}
+
+ trie = trieBuilder.createTrie ();
+ fclose (patF);
}
- fclose (patF);
}
exceptions = NULL; // Again, only instantiated when needed.
@@ -69,10 +87,8 @@ Hyphenator::Hyphenator (core::Platform *platform,
Hyphenator::~Hyphenator ()
{
- if (tree)
- delete tree;
- if (exceptions)
- delete exceptions;
+ delete trie;
+ delete exceptions;
}
Hyphenator *Hyphenator::getHyphenator (core::Platform *platform,
@@ -111,45 +127,38 @@ Hyphenator *Hyphenator::getHyphenator (core::Platform *platform,
return hyphenator;
}
-void Hyphenator::insertPattern (char *s)
+void Hyphenator::insertPattern (TrieBuilder *trieBuilder, char *s)
{
// Convert the a pattern like 'a1bc3d4' into a string of chars 'abcd'
// and a list of points [ 0, 1, 0, 3, 4 ].
int l = strlen (s);
char chars [l + 1];
- Vector <Integer> *points = new Vector <Integer> (1, true);
+ SimpleVector<char> points (1);
// TODO numbers consisting of multiple digits?
// TODO Encoding: This implementation works exactly like the Python
// implementation, based on UTF-8. Does this always work?
int numChars = 0;
- for (int i = 0; s[i]; i++)
- if (s[i] >= '0' && s[i] <= '9')
- points->put (new Integer (s[i] - '0'), numChars);
- else
+ for (int i = 0; s[i]; i++) {
+ if (s[i] >= '0' && s[i] <= '9') {
+ points.setSize(numChars + 1, '0');
+ points.set(numChars, s[i]);
+ } else {
chars[numChars++] = s[i];
+ }
+ }
chars[numChars] = 0;
- for (int i = 0; i < numChars + 1; i++) {
- Integer *val = points->get (i);
- if (val == NULL)
- points->put (new Integer (0), i);
- }
+ points.setSize(numChars + 2, '0');
+ points.set(numChars + 1, '\0');
// Insert the pattern into the tree. Each character finds a dict
// another level down in the tree, and leaf nodes have the list of
// points.
- HashTable <Integer, Collection <Integer> > *t = tree;
- for (int i = 0; chars[i]; i++) {
- Integer c (chars[i]);
- if (!t->contains(&c))
- t->put (new Integer (chars[i]),
- new HashTable <Integer, Collection <Integer> > (true, true));
- t = (HashTable <Integer, Collection <Integer> >*) t->get (&c);
- }
+ //printf("insertPattern %s\n", chars);
- t->put (new Integer (0), points);
+ trieBuilder->insert (chars, points.getArray ());
}
void Hyphenator::insertException (char *s)
@@ -214,7 +223,7 @@ bool Hyphenator::isCharPartOfActualWord (char *s)
*/
int *Hyphenator::hyphenateWord(const char *word, int *numBreaks)
{
- if ((tree == NULL && exceptions ==NULL) || !isHyphenationCandidate (word)) {
+ if ((trie == NULL && exceptions ==NULL) || !isHyphenationCandidate (word)) {
*numBreaks = 0;
return NULL;
}
@@ -232,7 +241,7 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks)
if (wordLc[startActualWord] == 0) {
// No letters etc in word: do not hyphenate at all.
- delete wordLc;
+ free (wordLc);
*numBreaks = 0;
return NULL;
}
@@ -254,14 +263,14 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks)
int *result = new int[exceptionalBreaks->size()];
for (int i = 0; i < exceptionalBreaks->size(); i++)
result[i] = exceptionalBreaks->get(i)->getValue() + startActualWord;
- delete wordLc;
+ free (wordLc);
*numBreaks = exceptionalBreaks->size();
return result;
}
- // tree == NULL means that there is no pattern file.
- if (tree == NULL) {
- delete wordLc;
+ // trie == NULL means that there is no pattern file.
+ if (trie == NULL) {
+ free (wordLc);
*numBreaks = 0;
return NULL;
}
@@ -275,25 +284,19 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks)
SimpleVector <int> points (l + 1);
points.setSize (l + 1, 0);
- Integer null (0);
-
for (int i = 0; i < l; i++) {
- HashTable <Integer, Collection <Integer> > *t = tree;
- for (int j = i; j < l; j++) {
- Integer c (work[j]);
- if (t->contains (&c)) {
- t = (HashTable <Integer, Collection <Integer> >*) t->get (&c);
- if (t->contains (&null)) {
- Vector <Integer> *p = (Vector <Integer>*) t->get (&null);
-
- for (int k = 0; k < p->size (); k++)
- points.set(i + k, lout::misc::max (points.get (i + k),
- p->get(k)->getValue()));
- }
- } else
- break;
+ int state = trie->root;
+
+ for (int j = i; j < l && trie->validState (state); j++) {
+ const char *p = trie->getData((unsigned char) work[j], &state);
+
+ if (p) {
+ for (int k = 0; p[k]; k++)
+ points.set(i + k,
+ lout::misc::max (points.get (i + k), p[k] - '0'));
+ }
}
- }
+ }
// No hyphens in the first two chars or the last two.
// Characters are not bytes, so UTF-8 characters must be counted.
@@ -317,19 +320,241 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks)
}
}
- delete wordLc;
+ free (wordLc);
*numBreaks = breakPos.size ();
if (*numBreaks == 0)
return NULL;
else {
- // Could save some cycles by directly returning the array in the
- // SimpleVector.
- int *breakPosArray = new int[*numBreaks];
- for (int i = 0; i < *numBreaks; i++)
- breakPosArray[i] = breakPos.get(i);
- return breakPosArray;
+ return breakPos.detachArray ();
}
}
+Trie::TrieNode TrieBuilder::trieNodeNull = {'\0', 0, NULL};
+
+TrieBuilder::TrieBuilder (int pack)
+{
+ this->pack = pack;
+ dataList = new SimpleVector <DataEntry> (10000);
+ stateStack = new SimpleVector <StackEntry> (10);
+ tree = new SimpleVector <Trie::TrieNode> (20000);
+ dataZone = new ZoneAllocator (1024);
+ stateStackPush(0);
+}
+
+TrieBuilder::~TrieBuilder ()
+{
+ delete dataList;
+ delete stateStack;
+ delete tree;
+ delete dataZone;
+}
+
+void TrieBuilder::insert (const char *key, const char *value)
+{
+ dataList->increase ();
+ dataList->getLastRef ()->key = (unsigned char *) strdup(key);
+ dataList->getLastRef ()->value = dataZone->strdup (value);
+}
+
+int TrieBuilder::keyCompare (const void *p1, const void *p2)
+{
+ DataEntry *pd1 = (DataEntry *) p1;
+ DataEntry *pd2 = (DataEntry *) p2;
+
+ return strcmp ((char *) pd1->key, (char *) pd2->key);
+}
+
+int TrieBuilder::insertState (StackEntry *state, bool root)
+{
+ int i, j;
+
+ if (state->count == 0)
+ return 0;
+
+ if (root) {
+ i = 0; // we reseve slot 0 for the root state
+ } else {
+ /* The bigger pack is the more slots we check and the smaller
+ * the trie will be, but CPU consumption also increases.
+ * Reasonable values for pack seemt to be between 256 and 1024.
+ */
+ i = tree->size () - pack + 2 * state->count;
+
+ if (i < 256) // reserve first 256 entries for the root state
+ i = 256;
+ }
+
+ for (;; i++) {
+ if (i + 256 > tree->size ())
+ tree->setSize (i + 256, trieNodeNull);
+
+ for (j = 1; j < 256; j++) {
+ Trie::TrieNode *tn = tree->getRef(i + j);
+
+ if (tn->c == j || ((state->next[j] || state->data[j]) && tn->c != 0))
+ break;
+ }
+
+ if (j == 256) // found a suitable slot
+ break;
+ }
+
+ for (int j = 1; j < 256; j++) {
+ Trie::TrieNode *tn = tree->getRef(i + j);
+
+ if (state->next[j] || state->data[j]) {
+ tn->c = j;
+ tn->next = state->next[j];
+ tn->data = state->data[j];
+ }
+ }
+
+ assert (root || i >= 256);
+ assert (!root || i == 0);
+ return i;
+}
+
+void TrieBuilder::stateStackPush (unsigned char c)
+{
+ stateStack->increase ();
+ StackEntry *e = stateStack->getLastRef ();
+ memset (e, 0, sizeof (StackEntry));
+ e->c = c;
+}
+
+int TrieBuilder::stateStackPop ()
+{
+ int next = insertState (stateStack->getLastRef (), stateStack->size () == 1);
+ unsigned char c = stateStack->getLastRef ()->c;
+ const char *data = stateStack->getLastRef ()->data1;
+
+ stateStack->setSize (stateStack->size () - 1);
+
+ if (stateStack->size () > 0) {
+ assert (stateStack->getLastRef ()->next[c] == 0);
+ assert (stateStack->getLastRef ()->data[c] == NULL);
+ stateStack->getLastRef ()->next[c] = next;
+ stateStack->getLastRef ()->data[c] = data;
+ stateStack->getLastRef ()->count++;
+ }
+
+ return next;
+}
+
+Trie *TrieBuilder::createTrie ()
+{
+ // we need to sort the patterns as byte strings not as unicode
+ qsort (dataList->getArray (), dataList->size (),
+ sizeof (DataEntry), keyCompare);
+
+ for (int i = 0; i < dataList->size (); i++) {
+ insertSorted (dataList->getRef (i)->key, dataList->getRef (i)->value);
+ free (dataList->getRef (i)->key);
+ }
+
+ while (stateStack->size ())
+ stateStackPop ();
+
+ int size = tree->size ();
+ Trie *trie = new Trie(tree->detachArray(), size, true, dataZone);
+ dataZone = NULL;
+ return trie;
+}
+
+void TrieBuilder::insertSorted (unsigned char *s, const char *data)
+{
+ int len = strlen((char*)s);
+
+ for (int i = 0; i < len; i++) {
+ if (stateStack->size () > i + 1 &&
+ stateStack->getRef (i + 1)->c != s[i]) {
+ for (int j = stateStack->size () - 1; j >= i + 1; j--)
+ stateStackPop();
+ }
+
+ if (i + 1 >= stateStack->size ())
+ stateStackPush(s[i]);
+ }
+
+ while (stateStack->size () > len + 1)
+ stateStackPop();
+
+ assert (stateStack->size () == len + 1);
+ stateStack->getLastRef ()->data1 = data;
+}
+
+Trie::Trie (TrieNode *array, int size, bool freeArray, ZoneAllocator *dataZone)
+{
+ this->array = array;
+ this->size = size;
+ this->freeArray = freeArray;
+ this->dataZone = dataZone;
+};
+
+Trie::~Trie ()
+{
+ delete dataZone;
+ if (freeArray)
+ free(array);
+};
+
+void Trie::save (FILE *file)
+{
+ for (int i = 0; i < size; i++) {
+ Trie::TrieNode *tn = &array[i];
+
+ if (tn->data)
+ fprintf(file, "%u, %u, %s\n", tn->c, tn->next, tn->data);
+ else
+ fprintf(file, "%u, %u\n", tn->c, tn->next);
+ }
+}
+
+int Trie::load (FILE *file)
+{
+ int next, c, maxNext = 0;
+ SimpleVector <TrieNode> tree (100);
+ dataZone = new ZoneAllocator (1024);
+
+ while (!feof (file)) {
+ char buf[LEN + 1];
+ char *s = fgets (buf, LEN, file);
+
+ if (!s)
+ continue;
+
+ char data[LEN + 1];
+ int n = sscanf (s, "%u, %u, %s", &c, &next, data);
+
+ if (n >= 2 && c >= 0 && c < 256 && next >= 0) {
+ tree.increase ();
+ tree.getLastRef ()->c = c;
+ tree.getLastRef ()->next = next;
+ if (n >= 3)
+ tree.getLastRef ()->data = dataZone->strdup (data);
+ else
+ tree.getLastRef ()->data = NULL;
+
+ if (next > maxNext)
+ maxNext = next;
+ } else {
+ goto error;
+ }
+ }
+
+ if (maxNext >= tree.size ())
+ goto error;
+
+ size = tree.size ();
+ array = tree.detachArray ();
+ freeArray = true;
+ return 0;
+
+error:
+ delete dataZone;
+ dataZone = NULL;
+ return 1;
+}
+
} // namespace dw
diff --git a/dw/hyphenator.hh b/dw/hyphenator.hh
index 47530467..eadcf081 100644
--- a/dw/hyphenator.hh
+++ b/dw/hyphenator.hh
@@ -7,9 +7,84 @@
namespace dw {
+class Trie {
+ public:
+ struct TrieNode {
+ unsigned char c;
+ uint16_t next;
+ const char *data;
+ };
+
+ private:
+ TrieNode *array;
+ int size;
+ bool freeArray;
+ lout::misc::ZoneAllocator *dataZone;
+
+ public:
+ Trie (TrieNode *array = NULL, int size = 0, bool freeArray = false,
+ lout::misc::ZoneAllocator *dataZone = NULL);
+ ~Trie ();
+
+ static const int root = 0;
+ inline bool validState (int state) { return state >= 0 && state < size; };
+ inline const char *getData (unsigned char c, int *state)
+ {
+ if (!validState (*state))
+ return NULL;
+
+ TrieNode *tn = array + *state + c;
+
+ if (tn->c == c) {
+ *state = tn->next > 0 ? tn->next : -1;
+ return tn->data;
+ } else {
+ *state = -1;
+ return NULL;
+ }
+ };
+ void save (FILE *file);
+ int load (FILE *file);
+};
+
+class TrieBuilder {
+ private:
+ struct StackEntry {
+ unsigned char c;
+ int count;
+ int next[256];
+ const char *data[256];
+ const char *data1;
+ };
+
+ struct DataEntry {
+ unsigned char *key;
+ const char *value;
+ };
+
+ int pack;
+ static Trie::TrieNode trieNodeNull;
+ lout::misc::SimpleVector <Trie::TrieNode> *tree;
+ lout::misc::SimpleVector <DataEntry> *dataList;
+ lout::misc::SimpleVector <StackEntry> *stateStack;
+ lout::misc::ZoneAllocator *dataZone;
+
+ static int keyCompare (const void *p1, const void *p2);
+ void stateStackPush (unsigned char c);
+ int stateStackPop ();
+ int insertState (StackEntry *state, bool root);
+ void insertSorted (unsigned char *key, const char *value);
+
+ public:
+ TrieBuilder (int pack);
+ ~TrieBuilder ();
+
+ void insert (const char *key, const char *value);
+ Trie *createTrie();
+};
+
class Hyphenator: public lout::object::Object
{
-private:
static lout::container::typed::HashTable
<lout::object::TypedPair <lout::object::TypedPointer <core::Platform>,
lout::object::ConstString>,
@@ -21,26 +96,27 @@ private:
* independent, but based on UTF-8. Clarify? Change?
*/
core::Platform *platform;
- lout::container::typed::HashTable <lout::object::Integer,
- lout::container::typed::Collection
- <lout::object::Integer> > *tree;
+ Trie *trie;
+
lout::container::typed::HashTable <lout::object::ConstString,
lout::container::typed::Vector
<lout::object::Integer> > *exceptions;
- void insertPattern (char *s);
+
+ void insertPattern (TrieBuilder *trieBuilder, char *s);
void insertException (char *s);
bool isCharPartOfActualWord (char *s);
public:
Hyphenator (core::Platform *platform,
- const char *patFile, const char *excFile);
+ const char *patFile, const char *excFile, int pack = 256);
~Hyphenator();
static Hyphenator *getHyphenator (core::Platform *platform,
const char *language);
static bool isHyphenationCandidate (const char *word);
int *hyphenateWord(const char *word, int *numBreaks);
+ void saveTrie (FILE *fp) { trie->save (fp); };
};
} // namespace dw
diff --git a/dw/table.hh b/dw/table.hh
index 87bbaa76..7bcc6c1b 100644
--- a/dw/table.hh
+++ b/dw/table.hh
@@ -118,10 +118,8 @@ namespace dw {
*
* <ol>
* <li> First, only cells with colspan = 1 are regarded:
- *
* \f[ e_{\hbox{base},i,\min} = \max \{ e_{\hbox{cell},i,j,\min} \} \f]
* \f[ e_{\hbox{base},i,\max} = \max \{ e_{\hbox{cell},i,j,\max} \} \f]
- *
* only for cells \f$(i, j)\f$ with colspan = 1.
*
* <li> Then,
@@ -130,29 +128,22 @@ namespace dw {
* the cell at \f$(i_1, j)\f$ always span from \f$i_1\f$ to \f$i_2\f$.)
* If the minimal width of the column exceeds the sum of the column minima
* calculated in the last step:
- *
* \f[e_{\hbox{cell},i_1,j,\min} >
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\min}\f]
- *
* then the minimal width of this cell is apportioned to the columns:
*
* <ul>
* <li> If the minimal width of this cell also exceeds the sum of the
* column maxima:
- *
* \f[e_{\hbox{cell},i_1,j,\min} >
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}\f]
- *
* then \f$e_{\hbox{cell},i_1,j,\min}\f$ is apportioned in a simple
* way:
- *
* \f[e_{\hbox{span},i,j,\min} =
* e_{\hbox{base},i,\max}
* {e_{\hbox{span},i,j,\min} \over
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}}\f]
- *
* <li> Otherwise, the apportionment function is used:
- *
* \f[e_{\hbox{span},i,j,\min} =
* a_i (e_{\hbox{cell},i_1,j,\min},
* (e_{\hbox{cell},i_1,j,\min} \ldots
@@ -172,6 +163,7 @@ namespace dw {
* For the maxima, there is no \f$e_{\hbox{span},i,\max}\f$, but it has to
* be assured, that the maximum is always greater than or equal to the
* minimum.
+ *
* </ol>
*
* Generally, if absolute widths are specified, they are, instead of the
@@ -223,17 +215,13 @@ namespace dw {
* no percentage width has been specified. The difference to the total
* width is at max available to the columns with percentage width
* specifications:
- *
* \f[W_{\hbox{columns}_\%,\hbox{available}} = W - \sum e_{i,\min}\f]
- *
* with only those columns \f$i\f$ with no percentage width specification.
*
* <li> Then, calculate the sum of the widths, which the columns with
* percentage width specification would allocate, when fully adhering to
* them:
- *
* \f[W_{\hbox{columns}_\%,\hbox{best}} = W \sum w_{i,\%}\f]
- *
* with only those columns \f$i\f$ with a percentage width specification.
*
* <li> Two cases are distinguished:
@@ -243,25 +231,19 @@ namespace dw {
* W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the
* percentage widths can be used without any modification, by
* setting the extremes:
- *
* \f[e_{i,\min} = e_{i,\max} = W w_{i,\%}\f]
- *
* for only those columns \f$i\f$ with a percentage width
* specification.
*
* <li> \f$W_{\hbox{columns}_\%,\hbox{available}} <
* W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the widths
* for these columns must be cut down:
- *
* \f[e_{i,\min} = e_{i,\max} =
* w_{i,\%}
* {W_{\hbox{columns}_\%,\hbox{available}} \over
* w_{\hbox{total},\%}}\f]
- *
* with
- *
* \f[w_{\hbox{total},\%} = \sum w_{i,\%}\f]
- *
* in both cases for only those columns \f$i\f$ with a percentage
* width specification.
* </ul>
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 8c3134a6..ff94486b 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -2063,194 +2063,4 @@ core::Allocation *Textblock::getCBAllocation ()
return &allocation;
}
-// ----------------------------------------------------------------------
-
-Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
- core::Content::Type mask,
- bool atEnd):
- core::Iterator (textblock, mask, atEnd)
-{
- index = atEnd ? textblock->words->size () : -1;
- content.type = atEnd ? core::Content::END : core::Content::START;
-}
-
-Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
- core::Content::Type mask,
- int index):
- core::Iterator (textblock, mask, false)
-{
- this->index = index;
-
- if (index < 0)
- content.type = core::Content::START;
- else if (index >= textblock->words->size ())
- content.type = core::Content::END;
- else
- content = textblock->words->getRef(index)->content;
-}
-
-object::Object *Textblock::TextblockIterator::clone()
-{
- return new TextblockIterator ((Textblock*)getWidget(), getMask(), index);
-}
-
-int Textblock::TextblockIterator::compareTo(misc::Comparable *other)
-{
- return index - ((TextblockIterator*)other)->index;
-}
-
-bool Textblock::TextblockIterator::next ()
-{
- Textblock *textblock = (Textblock*)getWidget();
-
- if (content.type == core::Content::END)
- return false;
-
- do {
- index++;
- if (index >= textblock->words->size ()) {
- content.type = core::Content::END;
- return false;
- }
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
-
- content = textblock->words->getRef(index)->content;
- return true;
-}
-
-bool Textblock::TextblockIterator::prev ()
-{
- Textblock *textblock = (Textblock*)getWidget();
-
- if (content.type == core::Content::START)
- return false;
-
- do {
- index--;
- if (index < 0) {
- content.type = core::Content::START;
- return false;
- }
- } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
-
- content = textblock->words->getRef(index)->content;
- return true;
-}
-
-void Textblock::TextblockIterator::highlight (int start, int end,
- core::HighlightLayer layer)
-{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
- /* nothing is highlighted */
- textblock->hlStart[layer].index = index;
- textblock->hlEnd[layer].index = index;
- }
-
- if (textblock->hlStart[layer].index >= index) {
- index2 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index;
- textblock->hlStart[layer].nChar = start;
- }
-
- if (textblock->hlEnd[layer].index <= index) {
- index2 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index;
- textblock->hlEnd[layer].nChar = end;
- }
-
- textblock->queueDrawRange (index1, index2);
-}
-
-void Textblock::TextblockIterator::unhighlight (int direction,
- core::HighlightLayer layer)
-{
- Textblock *textblock = (Textblock*)getWidget();
- int index1 = index, index2 = index;
-
- if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
- return;
-
- if (direction == 0) {
- index1 = textblock->hlStart[layer].index;
- index2 = textblock->hlEnd[layer].index;
- textblock->hlStart[layer].index = 1;
- textblock->hlEnd[layer].index = 0;
- } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
- index1 = textblock->hlStart[layer].index;
- textblock->hlStart[layer].index = index + 1;
- textblock->hlStart[layer].nChar = 0;
- } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
- index1 = textblock->hlEnd[layer].index;
- textblock->hlEnd[layer].index = index - 1;
- textblock->hlEnd[layer].nChar = INT_MAX;
- }
-
- textblock->queueDrawRange (index1, index2);
-}
-
-void Textblock::queueDrawRange (int index1, int index2)
-{
- int from = misc::min (index1, index2);
- int to = misc::max (index1, index2);
-
- from = misc::min (from, words->size () - 1);
- from = misc::max (from, 0);
- to = misc::min (to, words->size () - 1);
- to = misc::max (to, 0);
-
- int line1idx = findLineOfWord (from);
- int line2idx = findLineOfWord (to);
-
- if (line1idx >= 0 && line2idx >= 0) {
- Line *line1 = lines->getRef (line1idx),
- *line2 = lines->getRef (line2idx);
- int y = lineYOffsetWidget (line1) + line1->boxAscent -
- line1->contentAscent;
- int h = lineYOffsetWidget (line2) + line2->boxAscent +
- line2->contentDescent - y;
-
- queueDrawArea (0, y, allocation.width, h);
- }
-}
-
-void Textblock::TextblockIterator::getAllocation (int start, int end,
- core::Allocation *allocation)
-{
- Textblock *textblock = (Textblock*)getWidget();
- int lineIndex = textblock->findLineOfWord (index);
- Line *line = textblock->lines->getRef (lineIndex);
- Word *word = textblock->words->getRef (index);
-
- allocation->x =
- textblock->allocation.x + textblock->lineXOffsetWidget (line);
-
- for (int i = line->firstWord; i < index; i++) {
- Word *w = textblock->words->getRef(i);
- allocation->x += w->size.width + w->effSpace;
- }
- if (start > 0 && word->content.type == core::Content::TEXT) {
- allocation->x += textblock->textWidth (word->content.text, 0, start,
- word->style);
- }
- allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
- word->size.ascent;
-
- allocation->width = word->size.width;
- if (word->content.type == core::Content::TEXT) {
- int wordEnd = strlen(word->content.text);
-
- if (start > 0 || end < wordEnd) {
- end = misc::min(end, wordEnd); /* end could be INT_MAX */
- allocation->width =
- textblock->textWidth (word->content.text, start, end - start,
- word->style);
- }
- }
- allocation->ascent = word->size.ascent;
- allocation->descent = word->size.descent;
-}
-
} // namespace dw
diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc
new file mode 100644
index 00000000..63720ffb
--- /dev/null
+++ b/dw/textblock_iterator.cc
@@ -0,0 +1,219 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007 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/>.
+ */
+
+#include "textblock.hh"
+#include "../lout/msg.h"
+#include "../lout/misc.hh"
+
+#include <stdio.h>
+#include <math.h>
+
+using namespace lout;
+
+namespace dw {
+
+Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
+ core::Content::Type mask,
+ bool atEnd):
+ core::Iterator (textblock, mask, atEnd)
+{
+ index = atEnd ? textblock->words->size () : -1;
+ content.type = atEnd ? core::Content::END : core::Content::START;
+}
+
+Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
+ core::Content::Type mask,
+ int index):
+ core::Iterator (textblock, mask, false)
+{
+ this->index = index;
+
+ if (index < 0)
+ content.type = core::Content::START;
+ else if (index >= textblock->words->size ())
+ content.type = core::Content::END;
+ else
+ content = textblock->words->getRef(index)->content;
+}
+
+object::Object *Textblock::TextblockIterator::clone()
+{
+ return new TextblockIterator ((Textblock*)getWidget(), getMask(), index);
+}
+
+int Textblock::TextblockIterator::compareTo(misc::Comparable *other)
+{
+ return index - ((TextblockIterator*)other)->index;
+}
+
+bool Textblock::TextblockIterator::next ()
+{
+ Textblock *textblock = (Textblock*)getWidget();
+
+ if (content.type == core::Content::END)
+ return false;
+
+ do {
+ index++;
+ if (index >= textblock->words->size ()) {
+ content.type = core::Content::END;
+ return false;
+ }
+ } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+
+ content = textblock->words->getRef(index)->content;
+ return true;
+}
+
+bool Textblock::TextblockIterator::prev ()
+{
+ Textblock *textblock = (Textblock*)getWidget();
+
+ if (content.type == core::Content::START)
+ return false;
+
+ do {
+ index--;
+ if (index < 0) {
+ content.type = core::Content::START;
+ return false;
+ }
+ } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
+
+ content = textblock->words->getRef(index)->content;
+ return true;
+}
+
+void Textblock::TextblockIterator::highlight (int start, int end,
+ core::HighlightLayer layer)
+{
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
+ /* nothing is highlighted */
+ textblock->hlStart[layer].index = index;
+ textblock->hlEnd[layer].index = index;
+ }
+
+ if (textblock->hlStart[layer].index >= index) {
+ index2 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index;
+ textblock->hlStart[layer].nChar = start;
+ }
+
+ if (textblock->hlEnd[layer].index <= index) {
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index;
+ textblock->hlEnd[layer].nChar = end;
+ }
+
+ textblock->queueDrawRange (index1, index2);
+}
+
+void Textblock::TextblockIterator::unhighlight (int direction,
+ core::HighlightLayer layer)
+{
+ Textblock *textblock = (Textblock*)getWidget();
+ int index1 = index, index2 = index;
+
+ if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
+ return;
+
+ if (direction == 0) {
+ index1 = textblock->hlStart[layer].index;
+ index2 = textblock->hlEnd[layer].index;
+ textblock->hlStart[layer].index = 1;
+ textblock->hlEnd[layer].index = 0;
+ } else if (direction > 0 && textblock->hlStart[layer].index <= index) {
+ index1 = textblock->hlStart[layer].index;
+ textblock->hlStart[layer].index = index + 1;
+ textblock->hlStart[layer].nChar = 0;
+ } else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
+ index1 = textblock->hlEnd[layer].index;
+ textblock->hlEnd[layer].index = index - 1;
+ textblock->hlEnd[layer].nChar = INT_MAX;
+ }
+
+ textblock->queueDrawRange (index1, index2);
+}
+
+void Textblock::queueDrawRange (int index1, int index2)
+{
+ int from = misc::min (index1, index2);
+ int to = misc::max (index1, index2);
+
+ from = misc::min (from, words->size () - 1);
+ from = misc::max (from, 0);
+ to = misc::min (to, words->size () - 1);
+ to = misc::max (to, 0);
+
+ int line1idx = findLineOfWord (from);
+ int line2idx = findLineOfWord (to);
+
+ if (line1idx >= 0 && line2idx >= 0) {
+ Line *line1 = lines->getRef (line1idx),
+ *line2 = lines->getRef (line2idx);
+ int y = lineYOffsetWidget (line1) + line1->boxAscent -
+ line1->contentAscent;
+ int h = lineYOffsetWidget (line2) + line2->boxAscent +
+ line2->contentDescent - y;
+
+ queueDrawArea (0, y, allocation.width, h);
+ }
+}
+
+void Textblock::TextblockIterator::getAllocation (int start, int end,
+ core::Allocation *allocation)
+{
+ Textblock *textblock = (Textblock*)getWidget();
+ int lineIndex = textblock->findLineOfWord (index);
+ Line *line = textblock->lines->getRef (lineIndex);
+ Word *word = textblock->words->getRef (index);
+
+ allocation->x =
+ textblock->allocation.x + textblock->lineXOffsetWidget (line);
+
+ for (int i = line->firstWord; i < index; i++) {
+ Word *w = textblock->words->getRef(i);
+ allocation->x += w->size.width + w->effSpace;
+ }
+ if (start > 0 && word->content.type == core::Content::TEXT) {
+ allocation->x += textblock->textWidth (word->content.text, 0, start,
+ word->style);
+ }
+ allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
+ word->size.ascent;
+
+ allocation->width = word->size.width;
+ if (word->content.type == core::Content::TEXT) {
+ int wordEnd = strlen(word->content.text);
+
+ if (start > 0 || end < wordEnd) {
+ end = misc::min(end, wordEnd); /* end could be INT_MAX */
+ allocation->width =
+ textblock->textWidth (word->content.text, start, end - start,
+ word->style);
+ }
+ }
+ allocation->ascent = word->size.ascent;
+ allocation->descent = word->size.descent;
+}
+
+} // namespace dw
diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc
index f183ae0a..3835273d 100644
--- a/dw/textblock_linebreaking.cc
+++ b/dw/textblock_linebreaking.cc
@@ -712,7 +712,7 @@ int Textblock::hyphenateWord (int wordIndex)
origWord.style->unref ();
origWord.spaceStyle->unref ();
- delete breakPos;
+ free (breakPos);
} else {
words->getRef(wordIndex)->canBeHyphenated = false;
}
diff --git a/lout/misc.hh b/lout/misc.hh
index fe5cdcd5..437d229e 100644
--- a/lout/misc.hh
+++ b/lout/misc.hh
@@ -149,6 +149,14 @@ public:
inline T* getArray() { return array; }
+ inline T* detachArray() {
+ T* arr = array;
+ array = NULL;
+ numAlloc = 0;
+ num = 0;
+ return arr;
+ }
+
/**
* \brief Increase the vector size by one.
*
diff --git a/src/Makefile.am b/src/Makefile.am
index 5e887d2c..05bbe00e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -82,6 +82,8 @@ dillo_SOURCES = \
dicache.h \
capi.c \
capi.h \
+ domain.c \
+ domain.h \
css.cc \
css.hh \
cssparser.cc \
diff --git a/src/cache.c b/src/cache.c
index ea9d9a1f..4a689833 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -30,7 +30,7 @@
#include "capi.h"
#include "decode.h"
#include "auth.h"
-
+#include "domain.h"
#include "timeout.hh"
#include "uicmd.hh"
@@ -682,8 +682,7 @@ static void Cache_parse_header(CacheEntry_t *entry)
/* 30x: URL redirection */
DilloUrl *location_url = a_Url_new(location_str,URL_STR_(entry->Url));
- if (prefs.filter_auto_requests == PREFS_FILTER_SAME_DOMAIN &&
- !a_Url_same_organization(entry->Url, location_url)) {
+ if (!a_Domain_permit(entry->Url, location_url)) {
/* don't redirect; just show body like usual (if any) */
MSG("Redirection not followed from %s to %s\n",
URL_HOST(entry->Url), URL_STR(location_url));
diff --git a/src/capi.c b/src/capi.c
index 6db7484c..1da2d9ca 100644
--- a/src/capi.c
+++ b/src/capi.c
@@ -26,6 +26,7 @@
#include "nav.h"
#include "dpiapi.h"
#include "uicmd.hh"
+#include "domain.h"
#include "../dpip/dpip.h"
/* for testing dpi chat */
@@ -363,55 +364,6 @@ static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
}
/*
- * When dillo wants to open an URL, this can be either due to user action
- * (e.g., typing in an URL, clicking a link), or automatic (HTTP header
- * indicates redirection, META HTML tag with refresh attribute and 0 delay,
- * and images and stylesheets on an HTML page when autoloading is enabled).
- *
- * For a user request, the action will be permitted.
- * For an automatic request, permission to load depends on the filter set
- * by the user.
- */
-static bool_t Capi_filters_test(const DilloUrl *wanted,
- const DilloUrl *requester)
-{
- bool_t ret;
-
- if (requester == NULL) {
- /* request made by user */
- ret = TRUE;
- } else {
- switch (prefs.filter_auto_requests) {
- case PREFS_FILTER_SAME_DOMAIN:
- {
- const char *req_host = URL_HOST(requester),
- *want_host = URL_HOST(wanted);
- if (want_host[0] == '\0') {
- ret = (req_host[0] == '\0' ||
- !dStrAsciiCasecmp(URL_SCHEME(wanted), "data"))
- ? TRUE : FALSE;
- } else {
- /* This will regard "www.dillo.org" and "www.dillo.org." as
- * different, but it doesn't seem worth caring about.
- */
- ret = a_Url_same_organization(wanted, requester);
- }
- if (ret == FALSE) {
- MSG("Capi_filters_test: deny from '%s' to '%s'\n", req_host,
- want_host);
- }
- break;
- }
- case PREFS_FILTER_ALLOW_ALL:
- default:
- ret = TRUE;
- break;
- }
- }
- return ret;
-}
-
-/*
* Most used function for requesting a URL.
* TODO: clean up the ad-hoc bindings with an API that allows dynamic
* addition of new plugins.
@@ -427,8 +379,12 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
const char *scheme = URL_SCHEME(web->url);
int safe = 0, ret = 0, use_cache = 0;
- dReturn_val_if_fail((a_Capi_get_flags(web->url) & CAPI_IsCached) ||
- Capi_filters_test(web->url, web->requester), 0);
+ /* web->requester is NULL if the action is initiated by user */
+ if (!(a_Capi_get_flags(web->url) & CAPI_IsCached ||
+ web->requester == NULL ||
+ a_Domain_permit(web->requester, web->url))) {
+ return 0;
+ }
/* reload test */
reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
diff --git a/src/dillo.cc b/src/dillo.cc
index 7552676d..3599ca7a 100644
--- a/src/dillo.cc
+++ b/src/dillo.cc
@@ -46,6 +46,7 @@
#include "capi.h"
#include "dicache.h"
#include "cookies.h"
+#include "domain.h"
#include "auth.h"
#include "dw/fltkcore.hh"
@@ -82,7 +83,8 @@ static const CLI_options Options[] = {
{"-h", "--help", 0, DILLO_CLI_HELP,
" -h, --help Display this help text and exit."},
{"-l", "--local", 0, DILLO_CLI_LOCAL,
- " -l, --local Don't load images for these URL(s)."},
+ " -l, --local Don't load images or stylesheets for these "
+ "URL(s)."},
{"-v", "--version", 0, DILLO_CLI_VERSION,
" -v, --version Display version info and exit."},
{"-x", "--xid", 1, DILLO_CLI_XID,
@@ -338,6 +340,11 @@ int main(int argc, char **argv)
if ((fp = Paths::getPrefsFP(PATHS_RC_KEYS))) {
Keys::parse(fp);
}
+ // parse domainrc
+ if ((fp = Paths::getPrefsFP(PATHS_RC_DOMAIN))) {
+ a_Domain_parse(fp);
+ fclose(fp);
+ }
dLib_show_messages(prefs.show_msg);
// initialize internal modules
@@ -441,6 +448,7 @@ int main(int argc, char **argv)
* (This can be left to the OS, but we'll do it, with a view to test
* and fix our memory management)
*/
+ a_Domain_freeall();
a_Cookies_freeall();
a_Cache_freeall();
a_Dicache_freeall();
diff --git a/src/domain.c b/src/domain.c
new file mode 100644
index 00000000..af8c8075
--- /dev/null
+++ b/src/domain.c
@@ -0,0 +1,151 @@
+/*
+ * File: domain.c
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+
+#include "../dlib/dlib.h"
+#include "msg.h"
+#include "list.h"
+#include "domain.h"
+
+typedef struct Rule {
+ char *origin;
+ char *destination;
+} Rule;
+
+static Rule *exceptions = NULL;
+static int num_exceptions = 0;
+static int num_exceptions_max = 1;
+
+static bool_t default_deny = FALSE;
+
+/*
+ * Parse domainrc.
+ */
+void a_Domain_parse(FILE *fp)
+{
+ char *line;
+ uint_t lineno = 0;
+
+ MSG("Reading domainrc...\n");
+
+ while ((line = dGetline(fp)) != NULL) {
+ ++lineno;
+
+ /* Remove leading and trailing whitespace */
+ dStrstrip(line);
+
+ if (line[0] && line[0] != '#') {
+ const char *delim = " \t";
+ char *tok1 = strtok(line, delim);
+ char *tok2 = strtok(NULL, delim);
+
+ if (strtok(NULL, delim) != NULL) {
+ MSG("Domain: Ignoring extraneous text at end of line %u.\n",
+ lineno);
+ }
+ if (!tok2) {
+ MSG("Domain: Not enough fields in line %u.\n", lineno);
+ } else {
+ if (dStrAsciiCasecmp(tok1, "default") == 0) {
+ if (dStrAsciiCasecmp(tok2, "deny") == 0) {
+ default_deny = TRUE;
+ MSG("Domain: Default deny.\n");
+ } else if (dStrAsciiCasecmp(tok2, "accept") == 0) {
+ default_deny = FALSE;
+ MSG("Domain: Default accept.\n");
+ } else {
+ MSG("Domain: Default action \"%s\" not recognised.\n", tok2);
+ }
+ } else {
+ a_List_add(exceptions, num_exceptions, num_exceptions_max);
+ exceptions[num_exceptions].origin = dStrdup(tok1);
+ exceptions[num_exceptions].destination = dStrdup(tok2);
+ num_exceptions++;
+ MSG("Domain: Exception from %s to %s.\n", tok1, tok2);
+ }
+ }
+ }
+ dFree(line);
+ }
+}
+
+void a_Domain_freeall(void)
+{
+ int i = 0;
+
+ for (i = 0; i < num_exceptions; i++) {
+ dFree(exceptions[i].origin);
+ dFree(exceptions[i].destination);
+ }
+ dFree(exceptions);
+}
+
+/*
+ * Wildcard ('*') pattern always matches.
+ * "example.org" pattern matches "example.org".
+ * ".example.org" pattern matches "example.org" and "sub.example.org".
+ */
+static bool_t Domain_match(const char *host, const char *pattern) {
+ int cmp = strcmp(pattern, "*");
+
+ if (cmp) {
+ if (pattern[0] != '.')
+ cmp = dStrAsciiCasecmp(host, pattern);
+ else {
+ int diff = strlen(host) - strlen(pattern);
+
+ if (diff == -1)
+ cmp = dStrAsciiCasecmp(host, pattern + 1);
+ else if (diff >= 0)
+ cmp = dStrAsciiCasecmp(host + diff, pattern);
+ }
+ }
+ return cmp ? FALSE : TRUE;
+}
+
+/*
+ * Is the resource at 'source' permitted to request the resource at 'dest'?
+ */
+bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest)
+{
+ int i;
+ bool_t ret;
+ const char *source_host, *dest_host;
+
+ if (default_deny == FALSE && num_exceptions == 0)
+ return TRUE;
+
+ source_host = URL_HOST(source);
+ dest_host = URL_HOST(dest);
+
+ if (dest_host[0] == '\0')
+ return source_host[0] == '\0' ||
+ !dStrAsciiCasecmp(URL_SCHEME(dest), "data");
+
+ if (a_Url_same_organization(source, dest))
+ return TRUE;
+
+ for (i = 0; i < num_exceptions; i++) {
+ if(Domain_match(source_host, exceptions[i].origin) &&
+ Domain_match(dest_host, exceptions[i].destination)) {
+ ret = default_deny;
+ MSG("Domain: Matched rule from %s to %s.\n", exceptions[i].origin,
+ exceptions[i].destination);
+ break;
+ }
+ }
+ if (i == num_exceptions)
+ ret = default_deny ? FALSE : TRUE;
+
+ MSG("Domain: %s from %s to %s.\n",
+ (ret == TRUE ? "permitted" : "DENIED"), source_host, dest_host);
+
+ return ret;
+}
diff --git a/src/domain.h b/src/domain.h
new file mode 100644
index 00000000..d1ca685b
--- /dev/null
+++ b/src/domain.h
@@ -0,0 +1,19 @@
+#ifndef __DOMAIN_H__
+#define __DOMAIN_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include "url.h"
+
+void a_Domain_parse(FILE *fp);
+void a_Domain_freeall(void);
+bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/html.cc b/src/html.cc
index c4a3d060..6f5e94ae 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -553,7 +553,7 @@ int DilloHtml::getCurTagLineNumber()
ofs = CurrTagOfs;
line = OldTagLine;
for (i = OldTagOfs; i < ofs; ++i)
- if (p[i] == '\n')
+ if (p[i] == '\n' || (p[i] == '\r' && p[i+1] != '\n'))
++line;
OldTagOfs = CurrTagOfs;
OldTagLine = line;
@@ -934,7 +934,7 @@ static int Html_parse_entity(DilloHtml *html, const char *token,
if (!isocode || errno || isocode > 0xffff) {
/* this catches null bytes, errors and codes >= 0xFFFF */
- BUG_MSG("numeric character reference out of range\n");
+ BUG_MSG("numeric character reference \"%s\" out of range\n", tok);
isocode = -2;
}
@@ -3482,7 +3482,19 @@ static void Html_parse_common_attrs(DilloHtml *html, char *tag, int tagsize)
html->styleEngine->setStyle (attrbuf);
}
- if (tagsize >= 10) { /* TODO prefs.hyphenate? */
+ /* handle "xml:lang" and "lang" attributes */
+ int hasXmlLang = 0;
+ if (tagsize >= 14) {
+ /* length of "<t xml:lang=i>" */
+ attrbuf = Html_get_attr2(html, tag, tagsize, "xml:lang",
+ HTML_LeftTrim | HTML_RightTrim);
+ if (attrbuf) {
+ html->styleEngine->setNonCssHint(PROPERTY_X_LANG, CSS_TYPE_STRING,
+ attrbuf);
+ hasXmlLang = 1;
+ }
+ }
+ if (!hasXmlLang && tagsize >= 10) { /* 'xml:lang' prevails over 'lang' */
/* length of "<t lang=i>" */
attrbuf = Html_get_attr2(html, tag, tagsize, "lang",
HTML_LeftTrim | HTML_RightTrim);
@@ -3506,11 +3518,11 @@ static void Html_display_listitem(DilloHtml *html)
ListItem *list_item;
int *list_number;
char buf[16];
-
+
/* Get our parent tag's variables (used as state storage) */
list_number = &html->stack->getRef(html->stack->size()-2)->list_number;
ref_list_item = &html->stack->getRef(html->stack->size()-2)->ref_list_item;
-
+
HT2TB(html)->addParbreak (0, wordStyle);
list_item = new ListItem ((ListItem*)*ref_list_item,prefs.limit_text_width);
@@ -3518,7 +3530,7 @@ static void Html_display_listitem(DilloHtml *html)
HT2TB(html)->addParbreak (0, wordStyle);
*ref_list_item = list_item;
S_TOP(html)->textblock = html->dw = list_item;
-
+
if (style->listStyleType == LIST_STYLE_TYPE_NONE) {
// none
} else if (style->listStyleType >= LIST_STYLE_TYPE_DECIMAL) {
diff --git a/src/keys.cc b/src/keys.cc
index 08d50306..27b275a3 100644
--- a/src/keys.cc
+++ b/src/keys.cc
@@ -122,6 +122,7 @@ static const KeyBinding_t default_keys[] = {
{ "forward" , KEYS_FORWARD , 0 , '.' },
{ "goto" , KEYS_GOTO , FL_CTRL , 'l' },
{ "home" , KEYS_HOME , FL_CTRL , 'h' },
+ { "view-source" , KEYS_VIEW_SOURCE , FL_CTRL , 'u' },
{ "screen-up" , KEYS_SCREEN_UP , 0 , FL_Page_Up },
{ "screen-up" , KEYS_SCREEN_UP , 0 , 'b' },
{ "screen-down" , KEYS_SCREEN_DOWN , 0 , FL_Page_Down },
diff --git a/src/keys.hh b/src/keys.hh
index d3a5d586..8d2ec53c 100644
--- a/src/keys.hh
+++ b/src/keys.hh
@@ -37,6 +37,7 @@ typedef enum {
KEYS_FORWARD,
KEYS_GOTO,
KEYS_HOME,
+ KEYS_VIEW_SOURCE,
KEYS_SCREEN_UP,
KEYS_SCREEN_DOWN,
KEYS_SCREEN_LEFT,
diff --git a/src/keysrc b/src/keysrc
index e0c488f9..11913e78 100644
--- a/src/keysrc
+++ b/src/keysrc
@@ -72,6 +72,9 @@
# "file-menu" pops up the file menu.
#<alt>f = file-menu
+# "view-source" displays the page source.
+#<ctrl>u = view-source
+
# "goto" goes to the location bar at the top of the window.
#<ctrl>l = goto
diff --git a/src/paths.cc b/src/paths.cc
index 6fccb89a..32478728 100644
--- a/src/paths.cc
+++ b/src/paths.cc
@@ -83,11 +83,11 @@ FILE *Paths::getPrefsFP(const char *rcFile)
char *path = dStrconcat(dGethomedir(), "/.dillo/", rcFile, NULL);
if (!(fp = fopen(path, "r"))) {
- MSG("paths: Cannot open file '%s'\n", path);
+ MSG("paths: Cannot open file '%s': %s\n", path, dStrerror(errno));
char *path2 = dStrconcat(DILLO_SYSCONF, rcFile, NULL);
if (!(fp = fopen(path2, "r"))) {
- MSG("paths: Cannot open file '%s'\n",path2);
+ MSG("paths: Cannot open file '%s': %s\n", path2, dStrerror(errno));
MSG("paths: Using internal defaults...\n");
} else {
MSG("paths: Using %s\n", path2);
diff --git a/src/paths.hh b/src/paths.hh
index a9efc62b..8f52cd86 100644
--- a/src/paths.hh
+++ b/src/paths.hh
@@ -14,6 +14,7 @@
#define PATHS_RC_PREFS "dillorc"
#define PATHS_RC_KEYS "keysrc"
+#define PATHS_RC_DOMAIN "domainrc"
class Paths {
public:
diff --git a/src/png.c b/src/png.c
index bf34a8d5..3bf79db7 100644
--- a/src/png.c
+++ b/src/png.c
@@ -64,7 +64,6 @@ struct _DilloPng {
DilloUrl *url; /* Primary Key for the dicache */
int version; /* Secondary Key for the dicache */
- double display_exponent; /* gamma correction */
png_uint_32 width; /* png image width */
png_uint_32 height; /* png image height */
png_structp png_ptr; /* libpng private data */
@@ -75,7 +74,6 @@ struct _DilloPng {
int error; /* error flag */
png_uint_32 previous_row;
int rowbytes; /* No. bytes in image row */
- short passes;
short channels; /* No. image channels */
/*
@@ -177,7 +175,7 @@ Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
/* Interlaced */
if (interlace_type != PNG_INTERLACE_NONE) {
- png->passes = png_set_interlace_handling(png_ptr);
+ png_set_interlace_handling(png_ptr);
}
/* get libpng to update its state */
diff --git a/src/prefs.c b/src/prefs.c
index f243205a..a7fa1bcf 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -41,7 +41,6 @@ void a_Prefs_init(void)
prefs.buffered_drawing = 1;
prefs.contrast_visited_color = TRUE;
prefs.enterpress_forces_submit = FALSE;
- prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
prefs.focus_new_tab = TRUE;
prefs.font_cursive = dStrdup(PREFS_FONT_CURSIVE);
prefs.font_factor = 1.0;
diff --git a/src/prefs.h b/src/prefs.h
index f1a3d538..7622aea3 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -26,9 +26,6 @@ extern "C" {
/* Panel sizes */
enum { P_tiny = 0, P_small, P_medium };
-enum {PREFS_FILTER_ALLOW_ALL,
- PREFS_FILTER_SAME_DOMAIN};
-
typedef struct _DilloPrefs DilloPrefs;
struct _DilloPrefs {
@@ -77,7 +74,6 @@ struct _DilloPrefs {
bool_t load_images;
bool_t load_stylesheets;
bool_t parse_embedded_css;
- int filter_auto_requests;
int32_t buffered_drawing;
char *font_serif;
char *font_sans_serif;
diff --git a/src/prefsparser.cc b/src/prefsparser.cc
index e74b5a6f..aa810b1e 100644
--- a/src/prefsparser.cc
+++ b/src/prefsparser.cc
@@ -29,7 +29,6 @@ typedef enum {
PREFS_INT32,
PREFS_DOUBLE,
PREFS_GEOMETRY,
- PREFS_FILTER,
PREFS_PANEL_SIZE
} PrefType_t;
@@ -56,7 +55,6 @@ int PrefsParser::parseOption(char *name, char *value)
{ "contrast_visited_color", &prefs.contrast_visited_color, PREFS_BOOL },
{ "enterpress_forces_submit", &prefs.enterpress_forces_submit,
PREFS_BOOL },
- { "filter_auto_requests", &prefs.filter_auto_requests, PREFS_FILTER },
{ "focus_new_tab", &prefs.focus_new_tab, PREFS_BOOL },
{ "font_cursive", &prefs.font_cursive, PREFS_STRING },
{ "font_factor", &prefs.font_factor, PREFS_DOUBLE },
@@ -166,15 +164,6 @@ int PrefsParser::parseOption(char *name, char *value)
a_Misc_parse_geometry(value, &prefs.xpos, &prefs.ypos,
&prefs.width, &prefs.height);
break;
- case PREFS_FILTER:
- if (!dStrAsciiCasecmp(value, "same_domain"))
- prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
- else {
- if (dStrAsciiCasecmp(value, "allow_all"))
- MSG_WARN("prefs: unrecognized value for filter_auto_requests\n");
- prefs.filter_auto_requests = PREFS_FILTER_ALLOW_ALL;
- }
- break;
case PREFS_PANEL_SIZE:
if (!dStrAsciiCasecmp(value, "tiny"))
prefs.panel_size = P_tiny;
diff --git a/src/ui.cc b/src/ui.cc
index ce7a2a8a..647248aa 100644
--- a/src/ui.cc
+++ b/src/ui.cc
@@ -28,6 +28,8 @@
// Include image data
#include "pixmaps.h"
#include "uicmd.hh"
+#include "history.h"
+#include "nav.h"
struct iconset {
Fl_Image *ImgMeterOK, *ImgMeterBug,
@@ -764,6 +766,11 @@ int UI::handle(int event)
} else if (cmd == KEYS_FILE_MENU) {
a_UIcmd_file_popup(a_UIcmd_get_bw_by_widget(this), FileButton);
ret = 1;
+ } else if (cmd == KEYS_VIEW_SOURCE) {
+ BrowserWindow *bw = a_UIcmd_get_bw_by_widget(this);
+ const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw));
+ a_UIcmd_view_page_source(bw, url);
+ ret = 1;
}
} else if (event == FL_RELEASE) {
if (Fl::event_button() == FL_MIDDLE_MOUSE &&
@@ -815,7 +822,7 @@ void UI::set_location(const char *str)
*/
void UI::focus_location()
{
- if (Panelmode == UI_HIDDEN) {
+ if (Panelmode == UI_HIDDEN) {
panels_toggle();
temporaryPanels(true);
}
diff --git a/src/url.c b/src/url.c
index 4c7ea356..e0349c30 100644
--- a/src/url.c
+++ b/src/url.c
@@ -688,15 +688,15 @@ static uint_t Url_host_public_internal_dots(const char *host)
if (tld_len > 0) {
/* These TLDs were chosen by examining the current publicsuffix list
- * in September 2011 and picking out those where it was simplest for
- * them to describe the situation by beginning with a "*.[tld]" rule.
+ * in October 2012 and picking out those where it was simplest for
+ * them to describe the situation by beginning with a "*.[tld]" rule
+ * or every rule was "[something].[tld]".
*/
- const char *const tlds[] = {"ar","au","bd","bn","bt","ck","cy",
- "er","et","fj","fk","gt","gu","id",
- "il","jm","ke","kh","kw","ml","mm","mt",
- "mz","ni","np","nz","om","pg","py","qa",
- "sv","tr","uk","uy","ve","ye","yu","za",
- "zm","zw"};
+ const char *const tlds[] = {"ar","au","bd","bn","ck","cy","er","et",
+ "fj","fk","gt","gu","il","jm","ke","kh",
+ "kp","kw","lb","lr","mm","mt","mz","ni",
+ "np","nz","om","pg","py","sv","tr","uk",
+ "ve","ye","za","zm","zw"};
uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
for (i = 0; i < tld_num; i++) {
diff --git a/test/Makefile.am b/test/Makefile.am
index bc851247..ab4c98b0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -24,6 +24,7 @@ noinst_PROGRAMS = \
shapes \
cookies \
liang \
+ trie \
notsosimplevector
dw_anchors_test_SOURCES = dw_anchors_test.cc
@@ -180,6 +181,15 @@ liang_LDADD = \
$(top_builddir)/lout/liblout.a \
@LIBFLTK_LIBS@
+trie_SOURCES = trie.cc
+
+trie_LDADD = \
+ $(top_builddir)/dw/libDw-widgets.a \
+ $(top_builddir)/dw/libDw-fltk.a \
+ $(top_builddir)/dw/libDw-core.a \
+ $(top_builddir)/lout/liblout.a \
+ @LIBFLTK_LIBS@
+
notsosimplevector_SOURCES = notsosimplevector.cc
notsosimplevector_LDADD = $(top_builddir)/lout/liblout.a
diff --git a/test/trie.cc b/test/trie.cc
new file mode 100644
index 00000000..943b8045
--- /dev/null
+++ b/test/trie.cc
@@ -0,0 +1,17 @@
+#include "../dw/fltkcore.hh"
+#include "../dw/hyphenator.hh"
+
+int main (int argc, char *argv[])
+{
+ dw::fltk::FltkPlatform p;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: trie <pattern file>\n");
+ exit (1);
+ }
+
+ /* Use pack = 1024 to create a really small trie - can take a while.
+ */
+ dw::Hyphenator hyphenator (&p, argv[1], NULL, 1024);
+ hyphenator.saveTrie (stdout);
+}