summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2016-06-10 11:20:07 -0400
committerFranklin Wei <frankhwei536@gmail.com>2016-06-10 11:20:07 -0400
commit2823d12c43cc8cb7228379cddf3a2f22b113e5c5 (patch)
tree77b9c79090e0e6e1a2f8a22b3eb94a5a58e0a771 /apps/plugins
parent9a6700d52e8dfb2e93623e7143204ae34f798bbf (diff)
downloadrockbox-2823d12c43cc8cb7228379cddf3a2f22b113e5c5.zip
rockbox-2823d12c43cc8cb7228379cddf3a2f22b113e5c5.tar.gz
rockbox-2823d12c43cc8cb7228379cddf3a2f22b113e5c5.tar.bz2
rockbox-2823d12c43cc8cb7228379cddf3a2f22b113e5c5.tar.xz
FS#4755: Wikiviewer plugin
* Import of Avi Eisenberg's latest patch (dated May 27 '13) * Probably not mergeable due to copyright issues Change-Id: I5a0c2879bc0947ef506eaab48d470b04afb7a870
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SUBDIRS2
-rw-r--r--apps/plugins/viewers.config1
-rw-r--r--apps/plugins/wikiviewer/SOURCES6
-rw-r--r--apps/plugins/wikiviewer/bbfuncs.c146
-rw-r--r--apps/plugins/wikiviewer/bbfuncs.h31
-rw-r--r--apps/plugins/wikiviewer/btsearch.c176
-rw-r--r--apps/plugins/wikiviewer/converter/Makefile37
-rw-r--r--apps/plugins/wikiviewer/converter/README26
-rw-r--r--apps/plugins/wikiviewer/converter/btcreate.c259
-rw-r--r--apps/plugins/wikiviewer/converter/btree.c413
-rw-r--r--apps/plugins/wikiviewer/converter/btree.h61
-rw-r--r--apps/plugins/wikiviewer/converter/create_ww.sh5
-rw-r--r--apps/plugins/wikiviewer/converter/sort_xmlentities.sh13
-rw-r--r--apps/plugins/wikiviewer/converter/xmlconv.c890
-rw-r--r--apps/plugins/wikiviewer/converter/xmlconv.h54
-rw-r--r--apps/plugins/wikiviewer/converter/xmlentities.h289
-rw-r--r--apps/plugins/wikiviewer/inflate.c1149
-rw-r--r--apps/plugins/wikiviewer/inflate.h27
-rw-r--r--apps/plugins/wikiviewer/mallocer.c103
-rw-r--r--apps/plugins/wikiviewer/mallocer.h34
-rw-r--r--apps/plugins/wikiviewer/mwdb.c138
-rw-r--r--apps/plugins/wikiviewer/mwdb.h43
-rw-r--r--apps/plugins/wikiviewer/shared/btsearch.c153
-rw-r--r--apps/plugins/wikiviewer/shared/btsearch.h26
-rw-r--r--apps/plugins/wikiviewer/shared/utf8_aux.c117
-rw-r--r--apps/plugins/wikiviewer/shared/utf8_aux.h30
-rw-r--r--apps/plugins/wikiviewer/wikiviewer.c1956
-rw-r--r--apps/plugins/wikiviewer/wikiviewer.make21
29 files changed, 6207 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 2824880..19651de 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -133,6 +133,7 @@ wav2wv,viewers
wavplay,viewers
wavrecord,apps
wavview,viewers
+wikiviewer,viewers
wormlet,games
xobox,games
xworld,games
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index d02073e..6b14b04 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -95,3 +95,5 @@ mpegplayer
#if PLUGIN_BUFFER_SIZE >= 0x80000
lua
#endif
+
+wikiviewer
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 9eca2da..1dac9e8 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -70,6 +70,7 @@ wav,viewers/wavview,10
wav,viewers/wav2wv,-
wav,viewers/mp3_encoder,-
wav,viewers/test_codec,-
+wwi,viewers/wikiviewer,-
mpg,viewers/mpegplayer,4
mpeg,viewers/mpegplayer,4
mpv,viewers/mpegplayer,4
diff --git a/apps/plugins/wikiviewer/SOURCES b/apps/plugins/wikiviewer/SOURCES
new file mode 100644
index 0000000..2022f8b
--- /dev/null
+++ b/apps/plugins/wikiviewer/SOURCES
@@ -0,0 +1,6 @@
+wikiviewer.c
+mwdb.c
+mallocer.c
+inflate.c
+btsearch.c
+bbfuncs.c
diff --git a/apps/plugins/wikiviewer/bbfuncs.c b/apps/plugins/wikiviewer/bbfuncs.c
new file mode 100644
index 0000000..bda3142
--- /dev/null
+++ b/apps/plugins/wikiviewer/bbfuncs.c
@@ -0,0 +1,146 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "lib/pluginlib_exit.h"
+
+void error_die(const char* msg)
+{
+ rb->splash(3 * HZ, msg);
+ exit(0);
+}
+
+void error_msg(const char* msg)
+{
+ (void)msg;
+}
+
+size_t safe_read(int fd, void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = rb->read(fd, buf, count);
+ } while (n < 0&&n!=-1);
+
+ return n;
+}
+
+/*
+ * Read all of the supplied buffer from a file This does multiple reads as
+ *necessary. Returns the amount read, or -1 on an error. A short read is
+ *returned on an end of file.
+ */
+ssize_t full_read(int fd, void *buf, size_t len)
+{
+ ssize_t cc;
+ ssize_t total;
+
+ total = 0;
+
+ while (len)
+ {
+ cc = safe_read(fd, buf, len);
+
+ if (cc < 0)
+ return cc; /* read() returns -1 on failure. */
+
+ if (cc == 0)
+ break;
+
+ buf = ((char *)buf) + cc;
+ total += cc;
+ len -= cc;
+ }
+
+ return total;
+}
+
+/* Die with an error message if we can't read the entire buffer. */
+void xread(int fd, void *buf, ssize_t count)
+{
+ if (count)
+ {
+ ssize_t size = full_read(fd, buf, count);
+ if (size != count)
+ error_die("short read");
+ }
+}
+
+/* Die with an error message if we can't read one character. */
+unsigned char xread_char(int fd)
+{
+ unsigned char tmp;
+
+ xread(fd, &tmp, 1);
+
+ return tmp;
+}
+
+void check_header_gzip(int src_fd)
+{
+ union {
+ unsigned char raw[8];
+ struct {
+ unsigned char method;
+ unsigned char flags;
+ unsigned int mtime;
+ unsigned char xtra_flags;
+ unsigned char os_flags;
+ } formatted;
+ } header;
+
+ xread(src_fd, header.raw, 8);
+
+ /* Check the compression method */
+ if (header.formatted.method != 8)
+/* error_die("Unknown compression method %d", header.formatted.method);*/
+ error_die("Unknown compression method");
+
+ if (header.formatted.flags & 0x04)
+ {
+ /* bit 2 set: extra field present */
+ unsigned char extra_short;
+
+ extra_short = xread_char(src_fd) + (xread_char(src_fd) << 8);
+ while (extra_short > 0)
+ {
+ /* Ignore extra field */
+ xread_char(src_fd);
+ extra_short--;
+ }
+ }
+
+ /* Discard original name if any */
+ if (header.formatted.flags & 0x08)
+ /* bit 3 set: original file name present */
+ while(xread_char(src_fd) != 0) ;
+
+ /* Discard file comment if any */
+ if (header.formatted.flags & 0x10)
+ /* bit 4 set: file comment present */
+ while(xread_char(src_fd) != 0) ;
+
+ /* Read the header checksum */
+ if (header.formatted.flags & 0x02)
+ {
+ xread_char(src_fd);
+ xread_char(src_fd);
+ }
+}
diff --git a/apps/plugins/wikiviewer/bbfuncs.h b/apps/plugins/wikiviewer/bbfuncs.h
new file mode 100644
index 0000000..87834d8
--- /dev/null
+++ b/apps/plugins/wikiviewer/bbfuncs.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef BBFUNCS_H
+#define BBFUNCS_H
+
+void error_die(const char* msg);
+void error_msg(const char* msg);
+size_t safe_read(int fd, void *buf, size_t count);
+ssize_t full_read(int fd, void *buf, size_t len);
+void xread(int fd, void *buf, ssize_t count);
+unsigned char xread_char(int fd);
+void check_header_gzip(int src_fd);
+
+#endif
diff --git a/apps/plugins/wikiviewer/btsearch.c b/apps/plugins/wikiviewer/btsearch.c
new file mode 100644
index 0000000..8ef37b5
--- /dev/null
+++ b/apps/plugins/wikiviewer/btsearch.c
@@ -0,0 +1,176 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "shared/btsearch.h"
+#include <ctype.h>
+#define KEY_MAXLEN 512
+
+static size_t sf_read(int fd, void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = rb->read(fd, buf, count);
+ } while (n < 0&&n!=-1);
+
+ return n;
+}
+
+static signed char utf8strcnmp(const unsigned char *s1, const unsigned char *s2,
+ uint16_t n1,uint16_t n2,const bool casesense)
+{
+ unsigned short c1,c2;
+ const unsigned char *s1p,*s2p;
+
+ s1p=s1;
+ s2p=s2;
+
+ for(;; )
+ {
+ if(s1p-s1==n1)
+ {
+ if(n1==n2&&n2==s2p-s2)
+ return 0;
+ else
+ return -1;
+ }
+
+ if(s2p-s2==n2)
+ {
+ if(n1==n2&&n1==s1p-s1)
+ return 0;
+ else
+ return 1;
+ }
+
+ s1p=rb->utf8decode(s1p,&c1);
+ s2p=rb->utf8decode(s2p,&c2);
+
+ if(c1==' ') c1='_';
+
+ if(c2==' ') c2='_';
+
+ if(!casesense && c1<128&&c2<128)
+ {
+ c1=tolower(c1);
+ c2=tolower(c2);
+ }
+
+ if(c1<c2)
+ return -1;
+ else if (c1>c2)
+ return 1;
+ }
+
+ return 0;
+}
+
+void search_btree(void *fp,const char* key,uint16_t rkeylen, uint32_t globoffs,
+ uint32_t* res_lo,uint32_t* res_hi,const bool casesense)
+{
+ unsigned char nd_key[KEY_MAXLEN];
+ uint8_t node_flags;
+ uint16_t node_nr_active,i,keylen;
+ uint32_t chldptr;
+ uint32_t dtaptr_lo,dtaptr_hi;
+ int file = (int)fp;
+
+ sf_read(file,&node_flags,1);
+ sf_read(file,&node_nr_active,2);
+ node_nr_active=letoh16(node_nr_active);
+
+ if(node_nr_active<1) /* error */
+ {
+ *res_lo=*res_hi=0;
+ return;
+ }
+
+ for(i=0; i<node_nr_active; i++)
+ {
+ sf_read(file, &dtaptr_lo,4);
+ dtaptr_lo=letoh32(dtaptr_lo);
+ sf_read(file, &dtaptr_hi,4);
+ dtaptr_hi=letoh32(dtaptr_hi);
+ sf_read(file, &chldptr,4);
+ chldptr=letoh32(chldptr);
+ sf_read(file, &keylen,2);
+ keylen=letoh16(keylen);
+ sf_read(file, &nd_key,(keylen<KEY_MAXLEN) ? keylen : KEY_MAXLEN);
+ nd_key[keylen]=0;
+
+ if(rb->strlen(nd_key)!=keylen)
+ {
+ *res_lo=*res_hi=0;
+ LOGF("WrongKL\n");
+ return;
+ }
+
+ if(keylen-KEY_MAXLEN>0)
+ rb->lseek(file,keylen-((keylen<KEY_MAXLEN) ? keylen : KEY_MAXLEN),1);
+
+ keylen=(keylen<KEY_MAXLEN) ? keylen : KEY_MAXLEN;
+ nd_key[keylen]=0;
+ LOGF("K:%d,%s",utf8strcnmp(((const unsigned char*)key),
+ ((const unsigned char*)nd_key),rkeylen,
+ keylen,casesense),nd_key);
+
+ if(utf8strcnmp(((const unsigned char*)key),
+ ((const unsigned char*)nd_key),rkeylen,
+ keylen,casesense)>0)
+ continue;
+
+ if(utf8strcnmp(((const unsigned char*)key),
+ ((const unsigned char*)nd_key),rkeylen,
+ keylen,casesense)==0)
+ {
+ *res_lo=dtaptr_lo;
+ *res_hi=dtaptr_hi;
+ return;
+ }
+
+ if(chldptr==0||node_flags==1)
+ {
+ *res_lo=*res_hi=0;
+ return;
+ }
+
+ rb->lseek(file,globoffs+chldptr,0);
+ search_btree(fp,key,rkeylen,globoffs,res_lo,res_hi,casesense);
+ return;
+ }
+
+ if(node_flags!=1) /* node not leaf */
+ {
+ sf_read(file, &chldptr,4);
+ chldptr=letoh32(chldptr);
+
+ if(chldptr==0) /*leaf */
+ {
+ *res_lo=*res_hi=0;
+ return;
+ }
+
+ rb->lseek(file,globoffs+chldptr,0);
+ search_btree(fp,key,rkeylen,globoffs,res_lo,res_hi,casesense);
+ return;
+ }
+
+ *res_lo=*res_hi=0;
+}
diff --git a/apps/plugins/wikiviewer/converter/Makefile b/apps/plugins/wikiviewer/converter/Makefile
new file mode 100644
index 0000000..87e2166
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/Makefile
@@ -0,0 +1,37 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+CC = $(PREFIX)gcc
+CFLAGS=-Wall -W -g
+
+ifeq ($(findstring CYGWIN,$(shell uname)),CYGWIN)
+EXT=.exe
+CFLAGS+=-mno-cygwin
+else ifeq ($(findstring mingw32msvc,$(PREFIX)),mingw32msvc)
+EXT=.exe
+else
+EXT=
+endif
+
+all: xmlconv$(EXT) btcreate$(EXT) btsearch$(EXT)
+
+../shared/utf8_aux.o: ../shared/utf8_aux.h
+ $(CC) $(CFLAGS) -c -o $@ ../shared/utf8_aux.c
+
+btcreate$(EXT): btcreate.c btree.c btree.h ../shared/utf8_aux.o
+ $(CC) $(CFLAGS) -o $@ btcreate.c btree.c ../shared/utf8_aux.o
+
+btsearch$(EXT): ../shared/btsearch.c ../shared/utf8_aux.o
+ $(CC) $(CFLAGS) -DBTSEARCH_MAIN -o $@ ../shared/btsearch.c ../shared/utf8_aux.o
+
+xmlconv$(EXT): xmlconv.c xmlconv.h xmlentities.h
+ $(CC) $(CFLAGS) -o $@ -O3 xmlconv.c -lpcre -lz -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+
+clean:
+ rm -f btcreate$(EXT) btsearch$(EXT) xmlconv$(EXT) utf8_aux.o
diff --git a/apps/plugins/wikiviewer/converter/README b/apps/plugins/wikiviewer/converter/README
new file mode 100644
index 0000000..e364f82
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/README
@@ -0,0 +1,26 @@
+Wikipedia XML dump converter
+
+btconvert utility written by Frederik Vestre
+
+wikipedia-dump to media wiki viewer converter rewritten by Matthias Larisch from the
+original converter written by Frederik Vestre. Great improvement in functionality by Adam Gashlin.
+
+help:
+
+make all compiles all neccessary tools
+
+
+usage help:
+
+./xmlconv your_wikipedia_file output_prefix
+
+./btcreate output_prefix.wwt output_prefix.wwr output_prefix.wwi
+
+copy output_prefix.wwi and output_prefix0.wwa to your rockboxdevice
+
+for big wikidumps, there will be more than one .wwa file (named 0-N)
+
+
+Bugs:
+
+- does not work under cygwin (segfault)
diff --git a/apps/plugins/wikiviewer/converter/btcreate.c b/apps/plugins/wikiviewer/converter/btcreate.c
new file mode 100644
index 0000000..9703467
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/btcreate.c
@@ -0,0 +1,259 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <string.h>
+#include "../shared/utf8_aux.h"
+#include "btree.h"
+
+#define KEYLEN_MAX 200
+#define DEBUG 0
+
+/**A key is a int_fast_16_t that holds the size in char's of the rest of the
+ key, and then the rest of the key*/
+static unsigned char *getkey(void* key)
+{
+ return (unsigned char *)(key+sizeof(uint_fast16_t));
+}
+
+static char compare(void *key1,void *key2)
+{
+#if DEBUG == 1
+ printf("CMP:%s,%s:%d:%d,%d\n", getkey(key1), getkey(key2),
+ utf8strcnmp(getkey(key1), getkey(key2),
+ *(uint_fast16_t*)key1,
+ *(uint_fast16_t*)key2, false),
+ *(uint_fast16_t*)key1,
+ *(uint_fast16_t*)key2);
+#endif
+
+ return utf8strcnmp(getkey(key1), getkey(key2),
+ *(uint_fast16_t*)key1, *(uint_fast16_t*)key2, false);
+}
+
+static void int16le(uint16_t val, unsigned char* addr)
+{
+ addr[0] = val & 0xFF;
+ addr[1] = (val >> 8) & 0xff;
+}
+
+static void int32le(uint32_t val, unsigned char* addr)
+{
+ addr[0] = val & 0xFF;
+ addr[1] = (val >> 8) & 0xff;
+ addr[2] = (val >> 16) & 0xff;
+ addr[3] = (val >> 24) & 0xff;
+}
+
+static unsigned short bnode_write_len(void *tree, bt_node *node) /* depends on
+ key/data
+ structure */
+{
+ (void)tree;
+ /*Static node length:
+ 1:uint8_t (flags:leaf) 2:uint16_t (nr_active) 3*/
+ unsigned int i;
+ uint16_t bnodelen=3;
+ for(i=0; i < node->nr_active; i++)
+ {
+ /*Static key length:
+ 8:uint64_t (datapointer) 4:uint32_t (childpointer) 2:uint16_t
+ (keylength) 14
+ */
+ bnodelen += (*(uint_fast16_t*)node->key_vals[i]->key);
+ bnodelen += 14;
+ }
+ if(!node->leaf)
+ bnodelen += 4;
+
+ return bnodelen;
+}
+
+static void bnode_write(FILE *file,void *tree,bt_node *node,uint32_t *chldptrs) /*
+ depends
+ on
+ key/data
+ structure
+ */
+{
+ unsigned char bnode_write_buffer[((btree*)tree)->node_write_len(tree,node)];
+ memset(bnode_write_buffer,111,((btree*)tree)->node_write_len(tree,node));
+ unsigned short bfcntr=0,i=0;
+ bnode_write_buffer[bfcntr]=node->leaf;
+ bfcntr++;
+ int16le(node->nr_active,&bnode_write_buffer[bfcntr]);
+ bfcntr+=2;
+ for(i=0; i < node->nr_active; i++)
+ {
+ /*Why do I have to write these (dataptrs) inverse of how they were
+ readed?*/
+ int32le(((uint32_t)(*((uint32_t*)node->key_vals[i]->val))),&bnode_write_buffer[bfcntr]);
+ bfcntr+=4;
+ int32le(((uint32_t)(*((uint32_t*)(node->key_vals[i]->val+sizeof(uint32_t))))),&bnode_write_buffer[bfcntr]);
+ bfcntr+=4;
+ int32le(chldptrs[i],&bnode_write_buffer[bfcntr]);
+ bfcntr+=4;
+ int16le((*(uint_fast16_t*)node->key_vals[i]->key),&bnode_write_buffer[bfcntr]);
+ bfcntr+=2;
+ memcpy(&bnode_write_buffer[bfcntr],(node->key_vals[i]->key+sizeof(uint_fast16_t)),(*(uint_fast16_t*)node->key_vals[i]->key));
+ bfcntr+=(*(uint_fast16_t*)node->key_vals[i]->key);
+ }
+ if(!node->leaf)
+ {
+ int32le(chldptrs[node->nr_active],&bnode_write_buffer[bfcntr]);
+ bfcntr+=4;
+ }
+
+ fwrite(bnode_write_buffer,sizeof(char),bfcntr,file); /* is it correct with
+ sizeof */
+}
+
+static unsigned int keysize(void * key)
+{
+ return sizeof(uint_fast16_t)+(sizeof(char)*(*(uint_fast16_t*)key));
+}
+
+static unsigned int datasize(void * data)
+{
+ (void)data;
+ return sizeof(uint32_t)*2;
+}
+
+static void *mallockey(unsigned char* str,uint_fast16_t length)
+{
+ void *ret = malloc(sizeof(uint_fast16_t)+(sizeof(unsigned char)*length));
+ void *rtm=(ret+sizeof(uint_fast16_t));
+ memcpy(rtm,str,length);
+ *((uint_fast16_t*)ret)=length;
+ return ret;
+}
+
+static bt_key_val* makekv(char* key,uint_fast16_t keylen,uint32_t data1,uint32_t data2)
+{
+ bt_key_val * kv;
+ kv = (bt_key_val*)malloc(sizeof(bt_key_val));
+ kv->key = mallockey((unsigned char*)key,keylen);
+ kv->val = malloc(sizeof(uint32_t)*2);
+ *(uint32_t *)kv->val = data1;
+ *(uint32_t *)(kv->val+sizeof(uint32_t)) = data2;
+ return kv;
+}
+
+int main(int argc,char * argv[])
+{
+ if(argc<4)
+ {
+ printf("Usage: <in-list> <redirect-list> <out-tree>\n");
+ return 0;
+ }
+
+ btree * tree;
+ bt_key_val * kv;
+ void *key;
+ bt_key_val * kvr;
+ tree = btree_create(30);
+ tree->key_size = keysize;
+ tree->data_size = datasize;
+ tree->compare = compare;
+ tree->node_write_len=bnode_write_len;
+ tree->node_write=bnode_write;
+ char finlne[KEYLEN_MAX+1],tinlne[KEYLEN_MAX+1];
+ char elmhdr[12];
+ uint_fast16_t fkeylen=0,tkeylen=0;
+ FILE *fd=fopen(argv[1],"r");
+ while(fread(elmhdr,sizeof(uint8_t),12,fd)==12)
+ {
+ if(*((uint32_t*)&elmhdr[8])==0)
+ {
+ printf("Skipping empty\n");
+ continue;
+ }
+
+ fkeylen=*((uint32_t*)&elmhdr[8])<KEYLEN_MAX ? *((uint32_t*)&elmhdr[8]) : KEYLEN_MAX;
+ fread(finlne,sizeof(char),fkeylen,fd);
+ if((*(uint32_t*)&elmhdr[8])-fkeylen>0)
+ fseek(fd,(*((uint32_t*)&elmhdr[8]))-fkeylen,SEEK_CUR);
+
+ finlne[fkeylen]=0;
+ if(strlen(finlne)!=(fkeylen))
+ {
+ printf("Bad keylen: %s\n",finlne);
+ continue;
+ }
+
+ kv=makekv(finlne,fkeylen,*((uint32_t*)&elmhdr),*((uint32_t*)&elmhdr[4]));
+ btree_insert_key(tree,kv);
+ kv=0;
+ }
+ printf("SCRLD\n");
+ fclose(fd);
+ fd=NULL;
+ if(strlen(argv[2])!=0)
+ {
+ FILE *rfd=fopen(argv[2],"rb");
+ while(fread(elmhdr,sizeof(uint8_t),8,rfd)==8)
+ {
+ if(*((uint32_t*)&elmhdr[0])==0||*((uint32_t*)&elmhdr[4])==0)
+ {
+ printf("Skipping empty\n");
+ continue;
+ }
+
+ fkeylen=*((uint32_t*)&elmhdr[0])<KEYLEN_MAX ? *((uint32_t*)&elmhdr[0]) : KEYLEN_MAX;
+ tkeylen=*((uint32_t*)&elmhdr[4])<KEYLEN_MAX ? *((uint32_t*)&elmhdr[4]) : KEYLEN_MAX;
+ fread(finlne,sizeof(char),fkeylen,rfd);
+ if((*(uint32_t*)&elmhdr[0])-fkeylen>0)
+ {
+ printf("MXLADJf %s,%d,skip %d\n",finlne,*(uint32_t*)&elmhdr[0],(*((uint32_t*)&elmhdr[0]))-fkeylen);
+ fseek(rfd,(*((uint32_t*)&elmhdr[0]))-fkeylen,SEEK_CUR);
+ }
+
+ fread(tinlne,sizeof(char),tkeylen,rfd);
+ if((*(uint32_t*)&elmhdr[4])-tkeylen>0)
+ {
+ printf("MXLADJt %s,%d,skip %d\n",tinlne,*(uint32_t*)&elmhdr[4],(*((uint32_t*)&elmhdr[4]))-tkeylen);
+ fseek(rfd,(*((uint32_t*)&elmhdr[4]))-tkeylen,SEEK_CUR);
+ }
+
+ finlne[fkeylen]=0;
+ tinlne[tkeylen]=0;
+ if(strlen(finlne)!=(fkeylen)||strlen(tinlne)!=(tkeylen))
+ {
+ printf("Bad keylen: %s,%s,%d=%d,%d=%d\n",finlne,tinlne,strlen(finlne),(fkeylen),strlen(tinlne),(tkeylen));
+ continue;
+ }
+
+ key=mallockey((unsigned char*) tinlne,tkeylen);
+ if((kvr=btree_search(tree,key))!=NULL)
+ {
+ kv=makekv(finlne,fkeylen,(*((uint32_t*)kvr->val)),(*((uint32_t*)(kvr->val+sizeof(uint32_t)))));
+ btree_insert_key(tree,kv);
+ }
+
+ free(key);
+ }
+ fclose(rfd);
+ rfd=NULL;
+ }
+
+ FILE *ofd=fopen(argv[3],"w");
+ btree_write(ofd,tree,tree->root);
+ fclose(ofd);
+ ofd=NULL;
+
+ return 0;
+}
diff --git a/apps/plugins/wikiviewer/converter/btree.c b/apps/plugins/wikiviewer/converter/btree.c
new file mode 100644
index 0000000..a7883d8
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/btree.c
@@ -0,0 +1,413 @@
+/*B-tree from lkml before 23.07.2006, licensed under GPLv2.
+ * btree.c Copyright (C) 2006 Vishal Patil (vishpat AT gmail DOT com) Copyright
+ *(C) 2006 Frederik M.J.V. (comparistion and write functions) TREE_EDITABLE
+ *should work, but isn't tested.
+ */
+
+/*LE write function from rockbox, scramble.c Copyright (C) 2002 by Bjørn
+ Stenberg GPL*/
+#include "btree.h"
+
+typedef enum {left = -1,right = 1} position_t;
+
+typedef struct {
+ bt_node * node;
+ unsigned int index;
+} node_pos;
+
+static void print_single_node(btree *btree, bt_node * node);
+static bt_node * allocate_btree_node (unsigned int order);
+static int free_btree_node (bt_node * node);
+
+static node_pos get_btree_node(btree * btree,void * key);
+
+/**
+ * Used to create a btree with just the root node
+ * @param order The order of the B-tree
+ * @return The an empty B-tree
+ */
+btree * btree_create(unsigned int order)
+{
+ btree * btree;
+ btree = mem_alloc(sizeof(*btree));
+ btree->order = order;
+ btree->root = allocate_btree_node(order);
+ btree->root->leaf = true;
+ btree->root->nr_active = 0;
+ btree->root->next = NULL;
+ btree->root->level = 0;
+ return btree;
+}
+
+/**
+ * Function used to allocate memory for the btree node
+ * @param order Order of the B-Tree
+ * @param leaf boolean set true for a leaf node
+ * @return The allocated B-tree node
+ */
+static bt_node * allocate_btree_node (unsigned int order)
+{
+ bt_node * node;
+
+ /*Allocate memory for the node */
+ node = (bt_node *)mem_alloc(sizeof(bt_node));
+
+ /* Initialize the number of active nodes */
+ node->nr_active = 0;
+
+ /* Initialize the keys */
+ node->key_vals = (bt_key_val **)mem_alloc(2*order*sizeof(bt_key_val*) - 1);
+
+ /* Initialize the child pointers */
+ node->children = (bt_node **)mem_alloc(2*order*sizeof(bt_node*));
+
+ /* Use to determine whether it is a leaf */
+ node->leaf = true;
+
+ /* Use to determine the level in the tree */
+ node->level = 0;
+
+ /* Initialize the linked list pointer to NULL */
+ node->next = NULL;
+
+ return node;
+}
+
+/**
+ * Function used to free the memory allocated to the b-tree
+ * @param node The node to be freed
+ * @param order Order of the B-Tree
+ * @return The allocated B-tree node
+ */
+static int free_btree_node (bt_node * node)
+{
+ mem_free(node->children);
+ mem_free(node->key_vals);
+ mem_free(node);
+
+ return 0;
+}
+
+/**
+ * Used to split the child node and adjust the parent so that it has two
+ *children
+ * @param parent Parent Node
+ * @param index Index of the child node
+ * @param child Full child node
+ *
+ */
+static void btree_split_child(btree * btree, bt_node * parent,
+ unsigned int index,
+ bt_node * child)
+{
+ unsigned int i = 0;
+ unsigned int order = btree->order;
+
+ bt_node * new_child = allocate_btree_node(btree->order);
+ new_child->leaf = child->leaf;
+ new_child->level = child->level;
+ new_child->nr_active = btree->order - 1;
+
+ /* Copy the higher order keys to the new child */
+ for(i=0; i<order - 1; i++)
+ {
+ new_child->key_vals[i] = child->key_vals[i + order];
+ if(!child->leaf)
+ new_child->children[i] =
+ child->children[i + order];
+ }
+
+ /* Copy the last child pointer */
+ if(!child->leaf)
+ new_child->children[i] =
+ child->children[i + order];
+
+ child->nr_active = order - 1;
+
+ for(i = parent->nr_active + 1; i > index + 1; i--)
+ {
+ parent->children[i] = parent->children[i - 1];
+ }
+ parent->children[index + 1] = new_child;
+
+ for(i = parent->nr_active; i > index; i--)
+ {
+ parent->key_vals[i] = parent->key_vals[i - 1];
+ }
+
+ parent->key_vals[index] = child->key_vals[order - 1];
+ parent->nr_active++;
+}
+
+/**
+ * Used to insert a key in the non-full node
+ * @param btree The btree
+ * @param node The node to which the key will be added
+ * @param the key value pair
+ * @return void
+ */
+
+static void btree_insert_nonfull (btree * btree, bt_node * parent_node,
+ bt_key_val * key_val)
+{
+ int i;
+ bt_node * child;
+ bt_node * node = parent_node;
+
+insert: i = node->nr_active - 1;
+ if(node->leaf)
+ {
+ while(i >= 0 && btree->compare(key_val->key,node->key_vals[i]->key)<0)
+ {
+ node->key_vals[i + 1] = node->key_vals[i];
+ i--;
+ }
+ node->key_vals[i + 1] = key_val;
+ node->nr_active++;
+ }
+ else
+ {
+ while (i >= 0 && btree->compare(key_val->key,node->key_vals[i]->key)<0)
+ {
+ i--;
+ }
+ i++;
+ child = node->children[i];
+
+ if(child->nr_active == 2*btree->order - 1)
+ {
+ btree_split_child(btree,node,i,child);
+ if(btree->compare(key_val->key,node->key_vals[i]->key)>0)
+ i++;
+ }
+
+ node = node->children[i];
+ goto insert;
+ }
+}
+
+/**
+ * Function used to insert node into a B-Tree
+ * @param root Root of the B-Tree
+ * @param node The node to be inserted
+ * @param compare Function used to compare the two nodes of the tree
+ * @return success or failure
+ */
+int btree_insert_key(btree * btree, bt_key_val * key_val)
+{
+ bt_node * rnode;
+
+ rnode = btree->root;
+ if(rnode->nr_active == (2*btree->order - 1))
+ {
+ bt_node * new_root;
+ new_root = allocate_btree_node(btree->order);
+ new_root->level = btree->root->level + 1;
+ btree->root = new_root;
+ new_root->leaf = false;
+ new_root->nr_active = 0;
+ new_root->children[0] = rnode;
+ btree_split_child(btree,new_root,0,rnode);
+ btree_insert_nonfull(btree,new_root,key_val);
+ }
+ else
+ btree_insert_nonfull(btree,rnode,key_val);
+
+ return 0;
+}
+
+/**
+ * Function used to get the node containing the given key
+ * @param btree The btree to be searched
+ * @param key The the key to be searched
+ * @return The node and position of the key within the node
+ */
+node_pos get_btree_node(btree * btree,void * key)
+{
+ node_pos kp;
+ kp.node=NULL;
+ kp.index=0;
+ bt_node * node;
+ unsigned int i = 0;
+ node = btree->root;
+
+ for (;; i = 0)
+ {
+ /* Fix the index of the key greater than or equal to the key that we
+ would like to search */
+
+ while (i < node->nr_active && btree->compare(key, node->key_vals[i]->key)>0 )
+ i++;
+
+ /* If we find such key return the key-value pair */
+ if(i < node->nr_active && btree->compare(key,node->key_vals[i]->key)==0)
+ {
+ kp.node = node;
+ kp.index = i;
+ return kp;
+ }
+
+ /* If the node is leaf and if we did not find the key
+ return NULL */
+ if(node->leaf)
+ return kp;
+
+ /* To got a child node */
+ node = node->children[i];
+ }
+ return kp;
+}
+
+/**
+ * Used to destory btree
+ * @param btree The B-tree
+ * @return none
+ */
+void btree_destroy(btree * btree)
+{
+ unsigned int i;
+ unsigned int current_level;
+
+ bt_node * head, * tail, * node;
+ bt_node * child, * del_node;
+
+ node = btree->root;
+ current_level = node->level;
+ head = node;
+ tail = node;
+
+ while(true)
+ {
+ if(head == NULL)
+ break;
+
+ if (head->level < current_level)
+ current_level = head->level;
+
+ if(head->leaf == false)
+ for(i = 0; i < head->nr_active + 1; i++)
+ {
+ child = head->children[i];
+ tail->next = child;
+ tail = child;
+ child->next = NULL;
+ }
+
+ del_node = head;
+ head = head->next;
+ free_btree_node(del_node);
+ }
+}
+
+/**
+ * Function used to search a node in a B-Tree
+ * @param btree The B-tree to be searched
+ * @param key Key of the node to be search
+ * @return The key-value pair
+ */
+bt_key_val * btree_search(btree * btree,void * key)
+{
+ bt_key_val * key_val = NULL;
+ node_pos kp = get_btree_node(btree,key);
+
+ if(kp.node)
+ key_val = kp.node->key_vals[kp.index];
+
+ return key_val;
+}
+
+#ifdef DEBUG
+#include <stdint.h>
+/**
+ * Used to print the keys of the bt_node
+ * @param node The node whose keys are to be printed
+ * @return none
+ */
+
+static void print_single_node(btree *btree, bt_node * node)
+{
+ unsigned int i = 0;
+
+ print(" { ");
+ while(i < node->nr_active)
+ {
+ print("(%d)%s(%d) ",btree->key_size(node->key_vals[i]->key),
+ (char*)(node->key_vals[i]->key+sizeof(int16_t)),node->level);
+ i++;
+ }
+ print("} (0x%x,%d) ", (unsigned int)node,node->leaf);
+}
+
+/**
+ * Function used to print the B-tree
+ * @param root Root of the B-Tree
+ * @param print_key Function used to print the key value
+ * @return none
+ */
+
+void print_subtree(btree *btree,bt_node * node)
+{
+ unsigned int i;
+ unsigned int current_level;
+
+ bt_node * head, * tail;
+ bt_node * child;
+
+ current_level = node->level;
+ head = node;
+ tail = node;
+
+ while(true)
+ {
+ if(head == NULL)
+ break;
+
+ if (head->level < current_level)
+ {
+ current_level = head->level;
+ print("\n");
+ }
+
+ print_single_node(btree,head);
+
+ if(head->leaf == false)
+ for(i = 0; i < head->nr_active + 1; i++)
+ {
+ child = head->children[i];
+ tail->next = child;
+ tail = child;
+ child->next = NULL;
+ }
+
+ head = head->next;
+ }
+ print("\n");
+}
+
+uint32_t btree_write(FILE *file,btree *btree,bt_node *node)
+{
+ long ndestrtfo,tmpfo;
+ unsigned short i;
+ ndestrtfo=ftell(file);
+ if(node==0)
+ {
+ printf("NDNL\n");
+ return 0;
+ }
+
+ uint32_t chldptrs[node->nr_active+1];
+ fseek(file,ndestrtfo+btree->node_write_len(btree,node),SEEK_SET);
+ if(node->leaf==false)
+ for(i=0; i < node->nr_active+1; i++)
+ {
+ chldptrs[i]=btree_write(file,btree,node->children[i]);
+ }
+
+ tmpfo=ftell(file);
+ fseek(file,ndestrtfo,SEEK_SET);
+ btree->node_write(file,btree,node,chldptrs);
+ fseek(file,tmpfo,SEEK_SET);
+ return (uint32_t)ndestrtfo;
+}
+
+#endif
diff --git a/apps/plugins/wikiviewer/converter/btree.h b/apps/plugins/wikiviewer/converter/btree.h
new file mode 100644
index 0000000..596ab8a
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/btree.h
@@ -0,0 +1,61 @@
+/*B-tree from lkml before 23.07.2006, licensed under GPLv2.
+ * btree.c Copyright (C) 2006 Vishal Patil (vishpat AT gmail DOT com) Copyright
+ *(C) 2006 Frederik M.J.V. (comparistion and write functions) TREE_EDITABLE
+ *should work, but isn't tested.
+ */
+#ifndef _BTREE_H_
+#define _BTREE_H_
+
+/* Platform dependent headers */
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <strings.h>
+
+#define DEBUG
+#define mem_alloc malloc
+#define mem_free free
+#define bcopy bcopy
+#define print printf
+
+typedef struct {
+ void * key;
+ void * val;
+} bt_key_val;
+
+typedef struct bt_node {
+ struct bt_node * next; /* Pointer used for linked list */
+ bool leaf; /* Used to indicate whether leaf or not */
+ unsigned int nr_active; /* Number of active keys */
+ unsigned int level; /* Level in the B-Tree */
+ bt_key_val ** key_vals; /* Array of keys and values */
+ struct bt_node ** children; /* Array of pointers to child nodes */
+} bt_node;
+
+typedef struct {
+ unsigned int order; /* B-Tree order */
+ bt_node * root; /* Root of the B-Tree */
+ unsigned int (*key_size)(void * key); /* Return the key size */
+ unsigned int (*data_size)(void * data); /* Return the data size */
+ char (*compare)(void *key1,void *key2); /* compare keys return(ret):
+ 1<2:ret<0 1=2:ret=0 1>2:ret>0 */
+ void (*node_write)(FILE *file,void *tree,bt_node *node,uint32_t *chldptrs);
+ unsigned short (*node_write_len)(void *tree,bt_node *node);
+ void (*print_key)(void * key); /* Print the key */
+} btree;
+
+extern btree * btree_create(unsigned int order);
+extern int btree_insert_key(btree * btree, bt_key_val * key_val);
+extern int btree_delete_key(btree * btree,bt_node * subtree,void * key);
+extern bt_key_val * btree_search(btree * btree, void * key);
+extern void btree_destroy(btree * btree);
+extern void * btree_get_max_key(btree * btree);
+extern void * btree_get_min_key(btree * btree);
+uint32_t btree_write(FILE *file,btree *btree,bt_node *node);
+#ifdef DEBUG
+extern void print_subtree(btree * btree,bt_node * node);
+#endif
+
+
+#endif
diff --git a/apps/plugins/wikiviewer/converter/create_ww.sh b/apps/plugins/wikiviewer/converter/create_ww.sh
new file mode 100644
index 0000000..824b8e4
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/create_ww.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+./xmlconv $1 $2
+./btcreate $2.wwt $2.wwr $2.wwi
+rm $2.wwt $2.wwr
diff --git a/apps/plugins/wikiviewer/converter/sort_xmlentities.sh b/apps/plugins/wikiviewer/converter/sort_xmlentities.sh
new file mode 100644
index 0000000..1a67a77
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/sort_xmlentities.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# Helper script which sorts xmlentities.h
+
+START=$(grep -m 1 -nF 'ENT("' xmlentities.h | sed 's/\([0-9]*\).*/\1/')
+END=$(grep -nF 'ENT("' xmlentities.h | tail -n 1 | sed 's/\([0-9]*\).*/\1/')
+
+head -n $(($START - 1)) xmlentities.h > xmlentities.h.new
+grep 'ENT("' xmlentities.h | LC_ALL=C sort -b | uniq >> xmlentities.h.new
+awk "NR > $END { print \$0 }" xmlentities.h >> xmlentities.h.new
+
+rm xmlentities.h
+mv xmlentities.h.new xmlentities.h
diff --git a/apps/plugins/wikiviewer/converter/xmlconv.c b/apps/plugins/wikiviewer/converter/xmlconv.c
new file mode 100644
index 0000000..938ed2c
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/xmlconv.c
@@ -0,0 +1,890 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Matthias Larisch
+ * Copyright (C) 2007 Adam Gashlin (hcs)
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/*
+ Usage: xmlconv wiki_xml output_prefix
+
+ generates output_prefix.wwr, output_prefix.wwt, output_prefixN.wwa where N is
+ 0-? (each file ~ 1 GB)
+
+ This is a rewrite from original ruby converter (2007 by Frederik Vestre)
+ */
+
+#include <stdio.h>
+#include <pcre.h>
+#include <limits.h>
+#include <string.h>
+#include <zlib.h>
+#include <ctype.h>
+#include "xmlconv.h"
+#include "xmlentities.h"
+
+static FILE *titlefile;
+static FILE *redirectfile;
+static FILE *outfile;
+static int outfilenum;
+static char outfilename[PATH_MAX];
+static pcre *regex_redirect;
+static int ovector_temp[30];
+static int matchcount_temp;
+
+static article_header buf_article_header;
+static title_entry buf_title_entry;
+
+static void strreverse(char* begin, char* end)
+{
+ char aux;
+ while(end>begin)
+ {
+ aux=*end;
+ *end--=*begin;
+ *begin++=aux;
+ }
+}
+
+static void itoa(int value, char* str, int base)
+{
+ static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ char* wstr=str;
+ int sign;
+
+ /* Validate base */
+ if (base<2 || base>35)
+ {
+ *wstr='\0'; return;
+ }
+
+ /* Take care of sign */
+ if ((sign=value) < 0) value = -value;
+
+ /* Conversion. Number is reversed. */
+ do {
+ *wstr++ = num[value%base];
+ } while(value/=base);
+
+ if(sign<0)
+ *wstr++='-';
+
+ *wstr='\0';
+
+ /* Reverse string */
+ strreverse(str,wstr-1);
+}
+
+static int open_outfile(int mode)
+{
+ char filename[PATH_MAX];
+ strcpy(filename,outfilename);
+ if(mode==1) outfilenum++;
+
+ itoa(outfilenum,filename+strlen(filename),10);
+ strcat(filename,".wwa");
+
+ if(mode==1)
+ {
+ if((outfile=fopen(filename,"w"))==NULL)
+ {
+ printf("Error on creating article output file\n");
+ return 0;
+ }
+ }
+ else if((outfile=fopen(filename,"a"))==NULL)
+ {
+ printf("Error on reopening article output file\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void insert_redirect(char redirect_from[255], char redirect_to[255])
+{
+ unsigned int buffer;
+ buffer=strlen(redirect_from);
+ fwrite(&buffer, sizeof(unsigned int), 1, redirectfile);
+ buffer=strlen(redirect_to);
+ fwrite(&buffer, sizeof(unsigned int), 1, redirectfile);
+ fputs(redirect_from,redirectfile);
+ fputs(redirect_to,redirectfile);
+}
+
+/*
+ write a unicode value to a character buffer as UTF-8 increments *offset by
+ numer of bytes written to buf by hcs, based on the wikipedia article on UTF-8
+ */
+static void writeUTF8(char * buf, int * offset, int value)
+{
+ if (value>=0 && value<=0x7f)
+ buf[(*offset)++]=value;
+ else if (value>=0x80 && value <=0x7ff)
+ {
+ buf[(*offset)++]=((value >> 6)&0x1f)|0xC0;
+ buf[(*offset)++]=(value&0x3f)|0x80;
+ }
+ else if (value>=0x800 && value <=0xffff)
+ {
+ buf[(*offset)++]=((value >> 12)&0xf)|0xe0;
+ buf[(*offset)++]=((value >> 6)&0x3f)|0x80;
+ buf[(*offset)++]=(value&0x3f)|0x80;
+ }
+ else if (value>=0x10000 && value <= 0x10ffff)
+ {
+ buf[(*offset)++]=((value >> 18)&0xf)|0xf0;
+ buf[(*offset)++]=((value >> 12)&0x3f)|0x80;
+ buf[(*offset)++]=((value >> 6)&0x3f)|0x80;
+ buf[(*offset)++]=(value&0x3f)|0x80;
+ }
+ else
+ perror("writeUTF8");
+}
+
+/* wiki code and HTML parsing (by hcs) */
+
+static int inbold;
+static int initalic;
+
+/*
+ int parse_(char * outbuf, const char * inbuf, int * const inlen);
+ outbuf: output buffer inbuf: input buffer inlength: total length of input
+ buffer (few parsers will consume the entire input), decremented by number of
+ bytes consumed
+ */
+
+#define CONSUME(amt) { const int tempc=(amt); inbuf+=tempc; inlen-=tempc; }
+#define PRODUCE(c) { (*(outbuf++))=(c); }
+#define PRODUCEUTF8(c) { int temp=0; writeUTF8(outbuf, &temp, (c)); outbuf+=temp; }
+/* between PARSEHEAD and PARSETAIL goes a block for the body of the while loop
+ */
+#define PARSEHEAD(name) static void doparse_ ## name(char ** outbufptr, const char ** inbufptr, int * const inlenptr) { \
+ const char * inbuf = *inbufptr; \
+ char * outbuf = *outbufptr; \
+ int inlen = *inlenptr; \
+ while (inlen>0)
+#define PARSETAIL \
+ *inbufptr = inbuf; \
+ *outbufptr = outbuf; \
+ *inlenptr = inlen; \
+ }
+#define DESCEND(name) doparse_ ## name(&outbuf,&inbuf,&inlen)
+#define PASS { PRODUCE(inbuf[0]); CONSUME(1); }
+#define PARSEPROTO(name) static void doparse_ ## name(char ** outbufptr, const char ** inbufptr, int * const inlenptr)
+#define PARSEPROTOINLINE(name) static inline void doparse_ ## name(char ** outbufptr, const char ** inbufptr, int * const inlenptr)
+
+/* there has got to be a better way */
+
+PARSEPROTO(comment);
+PARSEPROTO(linkname);
+PARSEPROTO(link);
+PARSEPROTO(ref);
+PARSEPROTO(article);
+PARSEPROTOINLINE(text);
+
+/* assume we're just past the comment opening tag */
+PARSEHEAD(comment)
+{
+ if (inbuf[0]=='-' && !memcmp(inbuf+1,"-&gt;",5))
+ {
+ CONSUME(6);
+ break;
+ }
+ else
+ CONSUME(1);
+}
+PARSETAIL
+
+PARSEHEAD(linkname)
+{
+ if (inbuf[0]=='|')
+ {
+ CONSUME(1);
+ /* take back anything we've written so far, only the last is display
+ text */
+ outbuf=*outbufptr;
+ }
+ else if (inbuf[0]==']' && inbuf[1]==']')
+ break;
+ else if (inbuf[0]=='[' && inbuf[1]=='[')
+ {
+ /* for instance, links in image caption */
+ PRODUCE(MARKUP_STARTLINK);
+ CONSUME(2);
+ DESCEND(link);
+ }
+ else DESCEND(text);
+}
+PARSETAIL
+
+PARSEHEAD(link)
+{
+ if (inbuf[0]==']' && inbuf[1]==']')
+ {
+ CONSUME(2);
+ PRODUCE(MARKUP_ENDLINK);
+ break;
+ }
+ else if (inbuf[0]=='|')
+ {
+ CONSUME(1);
+ PRODUCE(MARKUP_MODE);
+ DESCEND(linkname);
+ /* should come back with ]] */
+ CONSUME(2);
+ PRODUCE(MARKUP_ENDLINK);
+ break;
+ }
+ else DESCEND(text);
+}
+PARSETAIL
+
+PARSEHEAD(ref)
+{
+ if (inbuf[0]=='&' && !memcmp(inbuf+1,"lt;/",4) && tolower(inbuf[5])=='r' &&
+ tolower(inbuf[6])=='e' && tolower(inbuf[7])=='f' &&
+ !memcmp(inbuf+8,"&gt;",4))
+ {
+ CONSUME(12);
+ break;
+ }
+ else
+ CONSUME(1);
+}
+PARSETAIL
+
+PARSEHEAD(article)
+{
+ /* links */
+ switch (inbuf[0])
+ {
+ case '[':
+ if (!memcmp(inbuf,"[[",2))
+ {
+ /* better-than-nothing image processing */
+ if (!memcmp(inbuf+3,"mage:",5) && (inbuf[2]=='i' || inbuf[2]=='I'))
+ {
+ CONSUME(2);
+ PRODUCE(MARKUP_STARTLINK);
+ while (inbuf[0]!='|' && memcmp(inbuf,"]]",2) )
+ {
+ PASS;
+ }
+ PRODUCE(MARKUP_MODE);
+ PRODUCE('[');
+ PRODUCE('I');
+ PRODUCE(']');
+ PRODUCE(MARKUP_ENDLINK);
+ PRODUCE(' ');
+ if (!memcmp(inbuf,"]]",2))
+ {
+ CONSUME(2);
+ }
+ else
+ {
+ CONSUME(1);
+ DESCEND(linkname);
+ /* should come back with ]] */
+ CONSUME(2);
+ PRODUCE('\n');
+ }
+ }
+ else
+ {
+ PRODUCE(MARKUP_STARTLINK);
+ CONSUME(2);
+ DESCEND(link);
+ }
+
+ break;
+ }
+
+ default:
+ DESCEND(text);
+ }
+}
+PARSETAIL
+
+static int entity_comparer(const void *_key, const void *_ent)
+{
+ const char* key = (const char*) _key;
+ struct html_entity* ent = (struct html_entity*) _ent;
+
+ return strncmp(key, ent->string, ent->string_length);
+}
+
+/* stuff that can be anywhere in plain text */
+/* only processes current character (unless it is something we recognize) */
+
+static inline void doparse_text(char ** outbufptr, const char ** inbufptr, int * const inlenptr)
+{
+ const char * inbuf = *inbufptr;
+ char * outbuf = *outbufptr;
+ int inlen = *inlenptr;
+
+ switch (inbuf[0])
+ {
+ case '\0':
+ printf("Read zero but end of string not reached\n");
+ CONSUME(1);
+ break;
+ case '\'':
+ if (!memcmp(inbuf+1,"''",2))
+ {
+ CONSUME(3);
+ if (inbold)
+ {
+ PRODUCE(MARKUP_ENDBOLD);
+ inbold=0;
+ }
+ else
+ {
+ PRODUCE(MARKUP_STARTBOLD);
+ inbold=1;
+ }
+
+ break;
+ }
+
+ if (inbuf[1]=='\'')
+ {
+ CONSUME(2);
+ if (initalic)
+ {
+ PRODUCE(MARKUP_ENDITALIC);
+ initalic=0;
+ }
+ else
+ {
+ PRODUCE(MARKUP_STARTITALIC);
+ initalic=1;
+ }
+
+ break;
+ }
+
+ PASS;
+ break;
+ case '&':
+ /* comment */
+ if (!memcmp(inbuf+1,"lt;!--",6))
+ {
+ CONSUME(7);
+ DESCEND(comment);
+ break;
+ }
+
+ /* references (really clutter things up) */
+ if (!memcmp(inbuf+1,"lt;",3) && tolower(inbuf[4])=='r' &&
+ tolower(inbuf[5])=='e' && tolower(inbuf[6])=='f' &&
+ (inbuf[7]=='/' || inbuf[7]==' ' || inbuf[7]=='&'))
+ {
+ int i;
+ CONSUME(7);
+
+ /* handle <ref /> (stanislaw lem) */
+ /* find end angle bracket */
+ for (i=0;; i++)
+ {
+ if (inbuf[i]=='&' && !memcmp(inbuf+i+1,"gt;",3))
+ break;
+ }
+ /* check for self-terminating tag */
+ if (inbuf[i-1]=='/')
+ {
+ CONSUME(i+4);
+ }
+ else
+ {
+ DESCEND(ref);
+ }
+
+ break;
+ }
+
+ /* entities */
+ /* &amp; &gt; &lt; and &quot; are necessary for storage in XML */
+ if (!memcmp(inbuf+1,"amp;",4))
+ {
+ /*
+ might contain another entitized entity (&ndash; appears in the
+ dump as &amp;ndash;)
+ */
+
+ CONSUME(5);
+
+ if (inbuf[0] == '#') /* numeric (thus far untested) */
+ {
+ int consumed=0;
+ int value=0;
+ char d;
+ consumed++;
+ if (inbuf[consumed]=='x')
+ { /* hexadecimal */
+ for (consumed++; isxdigit(d=inbuf[consumed]); consumed++)
+ {
+ if (isupper(d)) value=value*16+d-'A'+10;
+ else if (islower(d)) value=value*16+d-'a'+10;
+ else if (isdigit(d)) value=value*16+d-'0';
+ else break; /* this condition should be redundant */
+ }
+ /* I do not consider &#x; to be a valid entity */
+ if (consumed > 3 && inbuf[consumed]==';')
+ {
+#ifdef DEBUG
+ {
+ int i;
+ for (i=0; i<6 && inbuf[i]!=';' && inbuf[i]!='&'; i++) ;
+ if (inbuf[i]==';')
+ {
+ printf("numeric entity: ");
+ for (i=0; inbuf[i]!=';'; i++)
+ {
+ printf("%c",inbuf[i]);
+ }
+ printf(" evaluated as 0x%x\n",value);
+ }
+ }
+#endif
+ consumed++;
+ PRODUCEUTF8(value);
+ CONSUME(consumed);
+ break;
+ }
+
+ /* otherwise fall through to normal handling */
+ }
+ else /* decimal */
+ {
+ for (; isdigit(d=inbuf[consumed]); consumed++)
+ {
+ if (isdigit(d)) value=value*10+d-'0';
+ else break; /* this condition should be redundant */
+ }
+ /* I do not consider &#; to be a valid entity */
+ if (consumed > 2 && inbuf[consumed]==';')
+ {
+#ifdef DEBUG
+ {
+ int i;
+ for (i=0; i<6 && inbuf[i]!=';' && inbuf[i]!='&'; i++) ;
+ if (inbuf[i]==';')
+ {
+ printf("numeric entity: ");
+ for (i=0; inbuf[i]!=';'; i++)
+ {
+ printf("%c",inbuf[i]);
+ }
+ printf(" evaluated as %d\n",value);
+ }
+ }
+#endif
+ consumed++;
+ PRODUCEUTF8(value);
+ CONSUME(consumed);
+ }
+
+ /* otherwise fall through to normal handling */
+ }
+ }
+
+ struct html_entity* result = bsearch(inbuf, entities, ENT_COUNT,
+ sizeof(struct html_entity),
+ &entity_comparer);
+ if(result != NULL)
+ {
+ PRODUCEUTF8(result->utf8);
+ CONSUME(result->string_length);
+ break;
+ }
+
+ {
+ int i;
+ for (i=0; i<6 && inbuf[i]!=';' && (isalpha(inbuf[i]) || isdigit(inbuf[i])); i++) ;
+ if (inbuf[i]==';')
+ {
+ printf("unsupported entity: ");
+ for (i=0; inbuf[i]!=';'; i++)
+ {
+ printf("%c",inbuf[i]);
+ }
+ printf("\n");
+ }
+ }
+
+ PRODUCE('&');
+ break;
+ }
+
+ if (!memcmp(inbuf+1,"lt;",3))
+ {
+ PRODUCE('<');
+ CONSUME(4);
+ break;
+ }
+
+ if (!memcmp(inbuf+1,"gt;",3))
+ {
+ PRODUCE('>');
+ CONSUME(4);
+ break;
+ }
+
+ if (!memcmp(inbuf+1,"quot;",5))
+ {
+ PRODUCE('"');
+ CONSUME(6);
+ break;
+ }
+
+ {
+ int i;
+ printf ("unknown entity at top level: ");
+ for (i=0; inbuf[i]!=';'; i++)
+ {
+ printf("%c",inbuf[i]);
+ }
+ printf("\n");
+ }
+
+ PASS;
+ break;
+ default:
+ PASS;
+ }
+PARSETAIL
+
+static int parse_article(char * article_parsed, char * article, int article_length_temp)
+{
+ char * article_parsed_end = article_parsed;
+ const char * article_end = article;
+ int templen = article_length_temp;
+ inbold=0;
+ initalic=0;
+ doparse_article(&article_parsed_end,&article_end,&templen);
+
+ return article_parsed_end-article_parsed;
+}
+
+/* */
+
+static void do_article(int id, char title[255], char *article, int article_length)
+{
+ /* Redirect processing, redirects are short in general... speedup :) */
+ if(article_length<1000)
+ {
+ matchcount_temp=pcre_exec(regex_redirect, NULL, article, article_length,
+ 0, PCRE_NOTEMPTY, ovector_temp, 30);
+ if(matchcount_temp>1)
+ {
+ char redirect_to[255];
+ pcre_copy_substring(article, ovector_temp, matchcount_temp, 1,
+ redirect_to, 255);
+ if(strlen(redirect_to)<1)
+ printf("Could not process redirect %s\n",title);
+ else
+ {
+ insert_redirect(title, redirect_to);
+ return;
+ }
+ }
+ }
+
+ static char article_parsed[S_ARTBUF*2];
+ int article_parsed_length=0;
+
+ /*
+ This function processes the stylistics in the article. We give the
+ pointer as parameter so it changes our article in same memory
+ */
+ article_parsed_length=parse_article(article_parsed, article, article_length);
+
+ /*
+ At this point we should have the correct article in article-string.
+ This needs to be compressed using gzip and then written to the
+ outfile:
+ */
+ buf_article_header.id=id;
+ buf_article_header.article_length=article_parsed_length;
+ buf_article_header.title_length=strlen(title);
+ if((int)ftell(outfile)+buf_article_header.article_length >= 0x40000000)
+ {
+ /* Begin new output file at about 1gb */
+ fclose(outfile);
+ open_outfile(1);
+ }
+
+ buf_title_entry.filenumber=outfilenum;
+ buf_title_entry.fileposition=(int)ftello(outfile);
+ buf_title_entry.title_length=buf_article_header.title_length;
+ fwrite (&buf_title_entry, sizeof(buf_title_entry), 1, titlefile);
+ fwrite (title, sizeof(char), buf_title_entry.title_length, titlefile);
+
+ gzFile *gzipfile;
+ if((gzipfile=gzdopen(dup(fileno(outfile)),"w9"))==NULL)
+ {
+ perror("gzdopen");
+ return;
+ }
+
+ gzwrite(gzipfile, &buf_article_header, sizeof(buf_article_header));
+ gzwrite(gzipfile, title, buf_title_entry.title_length);
+ gzwrite(gzipfile, article_parsed, article_parsed_length);
+ gzflush(gzipfile, Z_FINISH);
+ gzclose(gzipfile);
+ fseeko(outfile,0,SEEK_END);
+}
+
+static void display_progress(long value, long max)
+{
+ long i, normalized_val = ((long double)value/(long double)max)*80;
+
+ max -= 5; /* sizeof("[" "]xx%") */
+
+ printf("[");
+ for(i=0; i<80; i++)
+ printf("%c", i < normalized_val ? '*' : ' ');
+ printf("]%02d%%\r", (int) (((long double)value/(long double)max)*100));
+ fflush(stdout);
+}
+
+int main(int argc,char * argv[])
+{
+ printf("Wikipedia XML Converter 0.12\n");
+ if(argc < 3)
+ {
+ printf("Help:\nThis program converts wikipedia-xml-dump to rockbox ww format.\n");
+ printf("Usage: xmlconv <input-xml-dump> <output-prefix>\n");
+ printf("example: xmlconv dewiki.xml meindewiki\n");
+ return 0;
+ }
+
+ FILE *xmldump;
+
+ strcpy(outfilename,argv[2]);
+ if (argv[1][0]=='-' && argv[1][1]=='\0')
+ {
+ printf("working on stdin with output prefix %s\n",argv[2]);
+ xmldump = stdin;
+ }
+ else
+ {
+ printf("working on %s with output prefix %s\n",argv[1], outfilename);
+ if((xmldump=fopen(argv[1],"rb"))==NULL)
+ {
+ printf("Error on opening input file\n");
+ return 1;
+ }
+ }
+
+ if((titlefile=fopen(strcat(outfilename,".wwt"),"wb"))==NULL)
+ {
+ printf("Error on opening title output file\n");
+ return 1;
+ }
+
+ strcpy(outfilename,argv[2]);
+ if((redirectfile=fopen(strcat(outfilename,".wwr"),"wb"))==NULL)
+ {
+ printf("Error on opening redirect output file\n");
+ return 1;
+ }
+
+ strcpy(outfilename,argv[2]);
+ outfilenum=-1;
+ if(!open_outfile(1)) return 1;
+
+ long long int dumplen=0, actlen=0;
+ if (xmldump != stdin)
+ {
+ fseeko(xmldump,0,SEEK_END);
+ dumplen=ftello(xmldump);
+ fseeko(xmldump,0,SEEK_SET);
+ }
+ else
+ dumplen=-1;
+
+ char linebuffer[S_ROWBUF];
+ int i=0;
+ const char *error=NULL;
+ int erroffset;
+ int ovector_open[30], ovector_end[30];
+ int matches_open, matches_closed;
+ pcre *regex_tagname, *regex_endtag, *regex_endtag_text, *regex_betweentags;
+ regex_tagname=pcre_compile("<([a-zA-Z0-9]+)[^>/]*(/)?>", 0, &error, &erroffset, NULL);
+ regex_endtag=pcre_compile("</([a-zA-Z0-9]+)[^>]*>", 0, &error, &erroffset, NULL);
+ regex_endtag_text=pcre_compile("([^<]*)</[a-zA-Z0-9]+[^>]*>", 0, &error, &erroffset, NULL);
+ regex_betweentags=pcre_compile("<([a-zA-Z0-9]+)[^>]*>([^<]*)(</\\1[^>]*>)?", 0, &error, &erroffset, NULL);
+ regex_redirect=pcre_compile("(?i)#REDIRECT:? ?\\[\\[([^\\]]*)]]", 0, &error, &erroffset, NULL);
+ char tagbuf_o[30];
+ char tagbuf_c[30];
+ char title[255];
+ int id=0;
+ char article[S_ARTBUF+1];
+ char buffer[255];
+ int article_length=0;
+ int mode=TAG_OUTSIDE;
+
+ i=0;
+ while (fgets(linebuffer,8191,xmldump)!=0)
+ {
+ i++;
+ if(i%5000==0 && dumplen > 0)
+ {
+ actlen=ftello(xmldump);
+ display_progress(actlen, dumplen);
+ }
+
+ linebuffer[strlen(linebuffer)-1]=0;
+ matches_open=pcre_exec(regex_tagname, NULL, linebuffer, strlen(linebuffer), 0, PCRE_NOTEMPTY, ovector_open, 30);
+ matches_closed=pcre_exec(regex_endtag, NULL, linebuffer, strlen(linebuffer), 0, PCRE_NOTEMPTY, ovector_end, 30);
+
+ if(matches_open>=2)
+ {
+ pcre_copy_substring(linebuffer, ovector_open, matches_open, 1, tagbuf_o, 30);
+ if(strcasecmp(tagbuf_o, "page")==0)
+ {
+ if(mode!=TAG_OUTSIDE) printf("page begin but not outside before\n");
+
+ mode=TAG_PAGE;
+ }
+
+ if(strcasecmp(tagbuf_o, "title")==0 && mode==TAG_PAGE)
+ {
+ if((matches_open=pcre_exec(regex_betweentags, NULL, linebuffer, strlen(linebuffer), 0, PCRE_NOTEMPTY, ovector_open, 30))<1)
+ printf("title empty\n");
+ else
+ pcre_copy_substring(linebuffer, ovector_open, matches_open, 2, title, 255);
+ }
+
+ if(strcasecmp(tagbuf_o, "revision")==0)
+ {
+ if(mode!=TAG_PAGE)
+ printf("revision but not in page");
+
+ mode=TAG_REVISION;
+ }
+
+ if(strcasecmp(tagbuf_o, "id")==0 && mode==TAG_REVISION)
+ {
+ if((matches_open=pcre_exec(regex_betweentags, NULL, linebuffer,
+ strlen(linebuffer), 0, PCRE_NOTEMPTY, ovector_open, 30))<1)
+ printf("id empty on %s\n", title);
+ else
+ {
+ pcre_copy_substring(linebuffer, ovector_open, matches_open,
+ 2, buffer, 255);
+ id=atoi(buffer);
+ }
+ }
+
+ if(strcasecmp(tagbuf_o, "contributor")==0)
+ {
+ if(mode!=TAG_REVISION)
+ printf("contributor but not in revision");
+
+ mode=TAG_CONTRIBUTOR;
+ }
+
+ if(strcasecmp(tagbuf_o, "text")==0)
+ {
+ if(mode!=TAG_REVISION)
+ {
+ printf("text but not in revision");
+ if(mode!=TAG_PAGE)
+ printf("and even not in page");
+ }
+
+ article[0]=0; /* clear article buffer
+ */
+ article_length=0; /* sync length */
+ if(matches_open!=3) mode=TAG_TEXT; /* When there are 3
+ matches -> matched
+ <text [...]/> */
+ else continue; /* When 3 matches ->
+ no text so go on */
+
+ if((matches_open=pcre_exec(regex_betweentags, NULL, linebuffer,
+ strlen(linebuffer), 0, 0, ovector_open, 30))<1)
+ printf("text didnt match second time\n");
+ else
+ {
+ pcre_copy_substring(linebuffer, ovector_open, matches_open,
+ 2, article, 8191);
+ article_length=strlen(article);
+ }
+ }
+ }
+ else if(mode==TAG_TEXT && matches_closed<2)
+ {
+ *(article+article_length)='\n';
+ strcpy(article+article_length+1,linebuffer);
+ article_length+=(strlen(linebuffer)+1);
+ if(article_length>=S_ARTBUF)
+ {
+ printf("Article buffer overflow! Bad sourcefile or increase buffersize\n");
+ return 0;
+ }
+ }
+
+ if(matches_closed==2)
+ {
+ pcre_copy_substring(linebuffer, ovector_end, matches_closed, 1,
+ tagbuf_c, 30);
+ if(strcasecmp(tagbuf_c, "contributor")==0)
+ {
+ if(mode!=TAG_CONTRIBUTOR)
+ printf("contributor end but not opened");
+
+ mode=TAG_REVISION;
+ }
+
+ if(strcasecmp(tagbuf_c, "revision")==0)
+ {
+ if(mode!=TAG_REVISION)
+ printf("revision end but not opened");
+
+ mode=TAG_PAGE;
+ }
+
+ if(strcasecmp(tagbuf_c, "text")==0)
+ {
+ if(mode!=TAG_TEXT)
+ printf("text end but not opened");
+
+ mode=TAG_REVISION;
+ do_article(id, title, article, article_length);
+ }
+
+ if(strcasecmp(tagbuf_c, "page")==0)
+ {
+ if(mode!=TAG_PAGE)
+ printf("page end but not opened");
+
+ mode=TAG_OUTSIDE;
+ }
+ }
+ }
+ fclose(titlefile);
+ fclose(redirectfile);
+ fclose(outfile);
+ fclose(xmldump);
+
+ return 0;
+}
diff --git a/apps/plugins/wikiviewer/converter/xmlconv.h b/apps/plugins/wikiviewer/converter/xmlconv.h
new file mode 100644
index 0000000..0d7cece
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/xmlconv.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Mode-defines: */
+#define TAG_OUTSIDE 1
+#define TAG_ERR -1
+#define TAG_PAGE 2
+#define TAG_REVISION 3
+#define TAG_TEXT 4
+#define TAG_CONTRIBUTOR 5
+#define S_ROWBUF (32*1024) /* 32kByte Rowbuffer */
+#define S_ARTBUF (8096*1024) /* 8MB Article Buffer */
+#pragma pack(push, 1)
+typedef struct
+{
+ unsigned int id;
+ unsigned int article_length;
+ unsigned short title_length;
+} article_header;
+
+typedef struct
+{
+ unsigned int filenumber;
+ unsigned int fileposition;
+ unsigned int title_length;
+} title_entry;
+#pragma pack(pop)
+
+#define MARKUP_STARTULINE 1
+#define MARKUP_ENDULINE 2
+#define MARKUP_STARTBOLD 3
+#define MARKUP_ENDBOLD 4
+#define MARKUP_STARTITALIC 5
+#define MARKUP_ENDITALIC 6
+#define MARKUP_STARTLINK 7
+#define MARKUP_ENDLINK 8
+#define MARKUP_LINEFEED 10
+#define MARKUP_MODE 15
diff --git a/apps/plugins/wikiviewer/converter/xmlentities.h b/apps/plugins/wikiviewer/converter/xmlentities.h
new file mode 100644
index 0000000..c8a1a80
--- /dev/null
+++ b/apps/plugins/wikiviewer/converter/xmlentities.h
@@ -0,0 +1,289 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* adapted from http://htmlhelp.com/reference/html40/entities/ */
+/* run ./sort_xmlentities.sh when changing this file */
+
+struct html_entity
+{
+ const char *string;
+ unsigned short utf8;
+ int string_length;
+};
+
+#define ENT_COUNT sizeof(entities)/sizeof(struct html_entity)
+#define ENT(x, y) {x, y, sizeof(x) - 1}
+struct html_entity entities[] =
+{
+ ENT("AElig;", 0xC6),
+ ENT("Aacute;", 0xC1),
+ ENT("Acirc;", 0xC2),
+ ENT("Agrave;", 0xC0),
+ ENT("Alpha;", 913),
+ ENT("Aring;", 0xC5),
+ ENT("Atilde;", 0xC3),
+ ENT("Auml;", 0xC4),
+ ENT("Beta;", 914),
+ ENT("Ccedil;", 0xC7),
+ ENT("Chi;", 935),
+ ENT("Dagger;", 8225),
+ ENT("Delta;", 916),
+ ENT("ETH;", 0xD0),
+ ENT("Eacute;", 0xC9),
+ ENT("Ecirc;", 0xCA),
+ ENT("Egrave;", 0xC8),
+ ENT("Epsilon;", 917),
+ ENT("Eta;", 919),
+ ENT("Euml;", 0xCB),
+ ENT("Gamma;", 915),
+ ENT("Iacute;", 0xCD),
+ ENT("Icirc;", 0xCE),
+ ENT("Igrave;", 0xCC),
+ ENT("Iota;", 921),
+ ENT("Iuml;", 0xCF),
+ ENT("Kappa;", 922),
+ ENT("Lambda;", 923),
+ ENT("Mu;", 924),
+ ENT("Ntilde;", 0xD1),
+ ENT("Nu;", 925),
+ ENT("OElig;", 338),
+ ENT("Oacute;", 0xD3),
+ ENT("Ocirc;", 0xD4),
+ ENT("Ograve;", 0xD2),
+ ENT("Omega;", 937),
+ ENT("Omicron;", 927),
+ ENT("Oslash;", 0xD8),
+ ENT("Otilde;", 0xD5),
+ ENT("Ouml;", 0xD6),
+ ENT("Phi;", 934),
+ ENT("Pi;", 928),
+ ENT("Prime;", 8243),
+ ENT("Psi;", 936),
+ ENT("Rho;", 929),
+ ENT("Scaron;", 352),
+ ENT("Sigma;", 931),
+ ENT("THORN;", 0xDE),
+ ENT("Tau;", 932),
+ ENT("Theta;", 920),
+ ENT("Uacute;", 0xDA),
+ ENT("Ucirc;", 0xDB),
+ ENT("Ugrave;", 0xD9),
+ ENT("Upsilon;", 933),
+ ENT("Uuml;", 0xDC),
+ ENT("Xi;", 926),
+ ENT("Yacute;", 0xDD),
+ ENT("Yuml;", 376),
+ ENT("Zeta;", 918),
+ ENT("aacute;", 0xE1),
+ ENT("acirc;", 0xE2),
+ ENT("acute;", 0xB4),
+ ENT("aelig;", 0xE6),
+ ENT("agrave;", 0xE0),
+ ENT("alefsym;", 8501),
+ ENT("alpha;", 945),
+ ENT("amp;", 38),
+ ENT("and;", 8743),
+ ENT("ang;", 8736),
+ ENT("apos;", 0x27),
+ ENT("aring;", 0xE5),
+ ENT("asymp;", 8776),
+ ENT("atilde;", 0xE3),
+ ENT("auml;", 0xE4),
+ ENT("bdquo;", 8222),
+ ENT("beta;", 946),
+ ENT("brvbar;", 0xA6),
+ ENT("bull;", 8226),
+ ENT("cap;", 8745),
+ ENT("ccedil;", 0xE7),
+ ENT("cedil;", 0xB8),
+ ENT("cent;", 0xA2),
+ ENT("chi;", 967),
+ ENT("circ;", 710),
+ ENT("clubs;", 9827),
+ ENT("cong;", 8773),
+ ENT("copy;", 0xA9),
+ ENT("crarr;", 8629),
+ ENT("cup;", 8746),
+ ENT("curren;", 0xA4),
+ ENT("dArr;", 8659),
+ ENT("dagger;", 8224),
+ ENT("darr;", 8595),
+ ENT("deg;", 0xB0),
+ ENT("delta;", 948),
+ ENT("diams;", 9830),
+ ENT("divide;", 0xF7),
+ ENT("eacute;", 0xE9),
+ ENT("ecirc;", 0xEA),
+ ENT("egrave;", 0xE8),
+ ENT("empty;", 8709),
+ ENT("emsp;", 8195),
+ ENT("ensp;", 8194),
+ ENT("epsilon;", 949),
+ ENT("equiv;", 8801),
+ ENT("eta;", 951),
+ ENT("eth;", 0xF0),
+ ENT("euml;", 0xEB),
+ ENT("euro;", 8364),
+ ENT("exist;", 8707),
+ ENT("fnof;", 402),
+ ENT("forall;", 8704),
+ ENT("frac12;", 0xBD),
+ ENT("frac13;", 0x2153),
+ ENT("frac14;", 0xBC),
+ ENT("frac34;", 0xBE),
+ ENT("frasl;", 8260),
+ ENT("gamma;", 947),
+ ENT("ge;", 8805),
+ ENT("gt;", 62),
+ ENT("hArr;", 8660),
+ ENT("harr;", 8596),
+ ENT("hearts;", 9829),
+ ENT("hellip;", 8230),
+ ENT("iacute;", 0xED),
+ ENT("icirc;", 0xEE),
+ ENT("iexcl;", 0xA1),
+ ENT("igrave;", 0xEC),
+ ENT("image;", 8465),
+ ENT("infin;", 8734),
+ ENT("int;", 8747),
+ ENT("iota;", 953),
+ ENT("iquest;", 0xBF),
+ ENT("isin;", 8712),
+ ENT("iuml;", 0xEF),
+ ENT("kappa;", 954),
+ ENT("lArr;", 8656),
+ ENT("lambda;", 955),
+ ENT("lang;", 9001),
+ ENT("laquo;", 0xAB),
+ ENT("larr;", 8592),
+ ENT("lceil;", 8968),
+ ENT("ldquo;", 8220),
+ ENT("le;", 8804),
+ ENT("lfloor;", 8970),
+ ENT("lowast;", 8727),
+ ENT("loz;", 9674),
+ ENT("lrm;", 8206),
+ ENT("lsaquo;", 8249),
+ ENT("lsquo;", 8216),
+ ENT("lt;", 60),
+ ENT("macr;", 0xAF),
+ ENT("mdash;", 8212),
+ ENT("micro;", 0xB5),
+ ENT("middot;", 0xB7),
+ ENT("minus;", 8722),
+ ENT("mu;", 956),
+ ENT("nabla;", 8711),
+ ENT("nbsp;", 0xA0),
+ ENT("ndash;", 8211),
+ ENT("ne;", 8800),
+ ENT("ni;", 8715),
+ ENT("not;", 0xAC),
+ ENT("notin;", 8713),
+ ENT("nsub;", 8836),
+ ENT("ntilde;", 0xF1),
+ ENT("nu;", 957),
+ ENT("oacute;", 0xF3),
+ ENT("ocirc;", 0xF4),
+ ENT("oelig;", 339),
+ ENT("ograve;", 0xF2),
+ ENT("oline;", 8254),
+ ENT("omega;", 969),
+ ENT("omicron;", 959),
+ ENT("oplus;", 8853),
+ ENT("or;", 8744),
+ ENT("ordf;", 0xAA),
+ ENT("ordm;", 0xBA),
+ ENT("oslash;", 0xF8),
+ ENT("otilde;", 0xF5),
+ ENT("otimes;", 8855),
+ ENT("ouml;", 0xF6),
+ ENT("para;", 0xB6),
+ ENT("part;", 8706),
+ ENT("permil;", 8240),
+ ENT("perp;", 8869),
+ ENT("phi;", 966),
+ ENT("pi;", 960),
+ ENT("piv;", 982),
+ ENT("plusmn;", 0xB1),
+ ENT("pm;", 0xB1),
+ ENT("pound;", 0xA3),
+ ENT("prime;", 8242),
+ ENT("prod;", 8719),
+ ENT("prop;", 8733),
+ ENT("psi;", 968),
+ ENT("quot", 34),
+ ENT("rArr;", 8658),
+ ENT("radic;", 8730),
+ ENT("rang;", 9002),
+ ENT("raquo;", 0xBB),
+ ENT("rarr;", 8594),
+ ENT("rceil;", 8969),
+ ENT("rdquo;", 8221),
+ ENT("real;", 8476),
+ ENT("reg;", 0xAE),
+ ENT("rfloor;", 8971),
+ ENT("rho;", 961),
+ ENT("rlm;", 8207),
+ ENT("rsaquo;", 8250),
+ ENT("rsquo;", 8217),
+ ENT("sbquo;", 8218),
+ ENT("scaron;", 353),
+ ENT("sdot;", 8901),
+ ENT("sect;", 0xA7),
+ ENT("shy;", 0xAD),
+ ENT("sigma;", 963),
+ ENT("sigmaf;", 962),
+ ENT("sim;", 8764),
+ ENT("spades;", 9824),
+ ENT("sub;", 8834),
+ ENT("sube;", 8838),
+ ENT("sum;", 8721),
+ ENT("sup1;", 0xB9),
+ ENT("sup2;", 0xB2),
+ ENT("sup3;", 0xB3),
+ ENT("sup;", 8835),
+ ENT("supe;", 8839),
+ ENT("szlig;", 0xDF),
+ ENT("tau;", 964),
+ ENT("there4;", 8756),
+ ENT("theta;", 952),
+ ENT("thetasym;", 977),
+ ENT("thinsp;", 8201),
+ ENT("thorn;", 0xFE),
+ ENT("tilde;", 732),
+ ENT("times;", 0xD7),
+ ENT("trade;", 8482),
+ ENT("uArr;", 8657),
+ ENT("uacute;", 0xFA),
+ ENT("uarr;", 8593),
+ ENT("ucirc;", 0xFB),
+ ENT("ugrave;", 0xF9),
+ ENT("uml;", 0xA8),
+ ENT("upsih;", 978),
+ ENT("upsilon;", 965),
+ ENT("uuml;", 0xFC),
+ ENT("weierp;", 8472),
+ ENT("xi;", 958),
+ ENT("yacute;", 0xFD),
+ ENT("yen;", 0xA5),
+ ENT("yuml;", 0xFF),
+ ENT("zeta;", 950),
+ ENT("zwj;", 8205),
+ ENT("zwnj;", 8204),
+};
diff --git a/apps/plugins/wikiviewer/inflate.c b/apps/plugins/wikiviewer/inflate.c
new file mode 100644
index 0000000..db54eb7
--- /dev/null
+++ b/apps/plugins/wikiviewer/inflate.c
@@ -0,0 +1,1149 @@
+/*
+ * gunzip implementation for wikiviewer (c) Frederik M.J.V., 2006.
+ * some bug fixes by Adam Gashlin gunzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
+ *well as stdin/stdout, and to generally behave itself wrt command line
+ *handling.
+ *
+ * General cleanup to better adhere to the style guide and make use of standard
+ *busybox functions by Glenn McGrath <bug1@iinet.net.au>
+ *
+ * read_gz interface + associated hacking by Laurence Anderson
+ *
+ * Fixed huft_build() so decoding end-of-block code does not grab more bits than
+ *necessary (this is required by unzip applet), added inflate_cleanup() to free
+ *leaked bytebuffer memory (used in unzip.c), and some minor style guide
+ *cleanups by Ed Clark
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ *Copyright (C) 1992-1993 Jean-loup Gailly The unzip code was written and put in
+ *the public domain by Mark Adler. Portions of the lzw code are derived from the
+ *public domain 'compress'
+ * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
+ *Ken Turkowski, Dave Mack and Peter Jannesen.
+ *
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <inttypes.h>
+#ifndef NULL
+#define NULL 0
+#endif
+#define ENABLE_DESKTOP 0
+#define USE_DESKTOP(...)
+#include "mallocer.h"
+#include "bbfuncs.h"
+#include "inflate.h"
+#include "mallocer.h"
+
+typedef struct huft_s {
+ unsigned char e; /* number of extra bits or operation */
+ unsigned char b; /* number of bits in this code or subcode */
+ union {
+ unsigned short n; /* literal, length base, or distance base */
+ struct huft_s *t; /* pointer to next level of table */
+ } v;
+} huft_t;
+
+/*static void *mainmembuf;*/
+static void *huftbuffer1;
+static void *huftbuffer2;
+
+#define HUFT_MMP1 8
+#define HUFT_MMP2 9
+
+static int gunzip_src_fd;
+static unsigned int gunzip_bytes_out; /* number of output bytes */
+static unsigned int gunzip_outbuf_count; /* bytes in output buffer */
+
+/* gunzip_window size--must be a power of two, and at least 32K for zip's
+ deflate method */
+enum {
+ gunzip_wsize = 0x8000
+};
+
+static unsigned char *gunzip_window;
+static uint32_t ifl_total;
+
+static uint32_t gunzip_crc;
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16 /* maximum bit length of any code (16 for explode) */
+#define N_MAX 288 /* maximum number of codes in any set */
+
+/* bitbuffer */
+static unsigned int gunzip_bb; /* bit buffer */
+static unsigned char gunzip_bk; /* bits in bit buffer */
+
+/* These control the size of the bytebuffer */
+static unsigned int bytebuffer_max = 0x8000;
+static unsigned char *bytebuffer = NULL;
+static unsigned int bytebuffer_offset = 0;
+static unsigned int bytebuffer_size = 0;
+
+static const unsigned short mask_bits[] = {
+ 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* Copy lengths for literal codes 257..285 */
+static const unsigned short cplens[] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const unsigned char cplext[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+ 5, 5, 5, 0, 99, 99
+}; /* 99==invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const unsigned short cpdist[] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+ 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const unsigned char cpdext[] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const unsigned char border[] = {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+static const uint32_t crc_table[256]= {
+ 0,1996959894,-301047508,-1727442502,124634137,1886057615,
+ -379345611,-1637575261,249268274,2044508324,-522852066,
+ -1747789432,162941995,2125561021,-407360249,-1866523247,
+ 498536548,1789927666,-205950648,-2067906082,450548861,
+ 1843258603,-187386543,-2083289657,325883990,1684777152,
+ -43845254,-1973040660,335633487,1661365465,-99664541,
+ -1928851979,997073096,1281953886,-715111964,-1570279054,
+ 1006888145,1258607687,-770865667,-1526024853,901097722,
+ 1119000684,-608450090,-1396901568,853044451,1172266101,
+ -589951537,-1412350631,651767980,1373503546,-925412992,
+ -1076862698,565507253,1454621731,-809855591,-1195530993,
+ 671266974,1594198024,-972236366,-1324619484,795835527,
+ 1483230225,-1050600021,-1234817731,1994146192,31158534,
+ -1731059524,-271249366,1907459465,112637215,-1614814043,
+ -390540237,2013776290,251722036,-1777751922,-519137256,
+ 2137656763,141376813,-1855689577,-429695999,1802195444,
+ 476864866,-2056965928,-228458418,1812370925,453092731,
+ -2113342271,-183516073,1706088902,314042704,-1950435094,
+ -54949764,1658658271,366619977,-1932296973,-69972891,
+ 1303535960,984961486,-1547960204,-725929758,1256170817,
+ 1037604311,-1529756563,-740887301,1131014506,879679996,
+ -1385723834,-631195440,1141124467,855842277,-1442165665,
+ -586318647,1342533948,654459306,-1106571248,-921952122,
+ 1466479909,544179635,-1184443383,-832445281,1591671054,
+ 702138776,-1328506846,-942167884,1504918807,783551873,
+ -1212326853,-1061524307,-306674912,-1698712650,62317068,
+ 1957810842,-355121351,-1647151185,81470997,1943803523,
+ -480048366,-1805370492,225274430,2053790376,-468791541,
+ -1828061283,167816743,2097651377,-267414716,-2029476910,
+ 503444072,1762050814,-144550051,-2140837941,426522225,
+ 1852507879,-19653770,-1982649376,282753626,1742555852,
+ -105259153,-1900089351,397917763,1622183637,-690576408,
+ -1580100738,953729732,1340076626,-776247311,-1497606297,
+ 1068828381,1219638859,-670225446,-1358292148,906185462,
+ 1090812512,-547295293,-1469587627,829329135,1181335161,
+ -882789492,-1134132454,628085408,1382605366,-871598187,
+ -1156888829,570562233,1426400815,-977650754,-1296233688,
+ 733239954,1555261956,-1026031705,-1244606671,752459403,
+ 1541320221,-1687895376,-328994266,1969922972,40735498,
+ -1677130071,-351390145,1913087877,83908371,-1782625662,
+ -491226604,2075208622,213261112,-1831694693,-438977011,
+ 2094854071,198958881,-2032938284,-237706686,1759359992,
+ 534414190,-2118248755,-155638181,1873836001,414664567,
+ -2012718362,-15766928,1711684554,285281116,-1889165569,
+ -127750551,1634467795,376229701,-1609899400,-686959890,
+ 1308918612,956543938,-1486412191,-799009033,1231636301,
+ 1047427035,-1362007478,-640263460,1088359270,936918000,
+ -1447252397,-558129467,1202900863,817233897,-1111625188,
+ -893730166,1404277552,615818150,-1160759803,-841546093,
+ 1423857449,601450431,-1285129682,-1000256840,1567103746,
+ 711928724,-1274298825,-1022587231,1510334235,755167117
+};
+
+static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current,
+ const unsigned int required)
+{
+ while (*current < required)
+ {
+ if (bytebuffer_offset >= bytebuffer_size)
+ {
+ /* Leave the first 4 bytes empty so we can always unwind the
+ bitbuffer to the front of the bytebuffer, leave 4 bytes free at
+ end of tail so we can easily top up buffer in
+ check_trailer_gzip() */
+ if (1 > (bytebuffer_size = safe_read(gunzip_src_fd, &bytebuffer[4],
+ bytebuffer_max - 8)))
+ error_die("unexpected end of file");
+
+ bytebuffer_size += 4;
+ bytebuffer_offset = 4;
+ }
+
+ bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current;
+ bytebuffer_offset++;
+ *current += 8;
+ }
+ return(bitbuffer);
+}
+
+/*
+ * Free the malloc'ed tables built by huft_build(), which makes a linked list of
+ *the tables it made, with the links in a dummy first entry of each table.
+ * t: table to free
+ */
+static int huft_free(huft_t * t,unsigned char bufnum)
+{
+ wpw_reset_mempool(bufnum);
+ if(t==0)
+ {
+ }
+
+ return 0;
+}
+
+/* Given a list of code lengths and a maximum table size, make a set of tables
+ to decode that set of codes. Return zero on success, one if the given code
+ set is incomplete (the tables are still built in this case), two if the input
+ is invalid (all zero length codes or an oversubscribed set of lengths), and
+ three if not enough memory.
+ *
+ * b: code lengths in bits (all assumed <= BMAX) n: number of codes
+ *(assumed <= N_MAX) s: number of simple-valued codes (0..s-1) d: list of
+ *base values for non-simple codes e: list of extra bits for non-simple codes
+ *t: result: starting table m: maximum lookup bits, returns actual bufnum:
+ *the number of the memory pool to fetch memory from
+ */
+static
+int huft_build(unsigned int *b, const unsigned int n,
+ const unsigned int s, const unsigned short *d,
+ const unsigned char *e, huft_t ** t, unsigned int *m,
+ unsigned char bufnum)
+{
+ unsigned a=0; /* counter for codes of length k */
+ unsigned c[BMAX + 1]; /* bit length count table */
+ unsigned eob_len=0; /* length of end-of-block code (value 256) */
+ unsigned f=0; /* i repeats in table every f entries */
+ int g=0; /* maximum code length */
+ int htl=0; /* table level */
+ unsigned i=0; /* counter, current code */
+ unsigned j=0; /* counter */
+ int k=0; /* number of bits in current code */
+ unsigned *p; /* pointer into c[], b[], or v[] */
+ huft_t *q; /* points to current table */
+ huft_t r; /* table entry for structure assignment */
+ huft_t *u[BMAX]; /* table stack */
+ unsigned v[N_MAX]; /* values in order of bit length */
+ int ws[BMAX+1]; /* bits decoded stack */
+ int w=0; /* bits decoded */
+ unsigned x[BMAX + 1]; /* bit offsets, then code stack */
+ unsigned *xp; /* pointer into x */
+ int y=0; /* number of dummy codes added */
+ unsigned z=0; /* number of entries in current table */
+
+ /* Length of EOB code, if any */
+ eob_len = n > 256 ? b[256] : BMAX;
+
+ /* Generate counts for each bit length */
+ rb->memset((void *)c, 0, sizeof(c));
+ p = b;
+ i = n;
+ do {
+ c[*p]++; /* assume all entries <= BMAX */
+ p++; /* Can't combine with above line (Solaris bug) */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (huft_t *) NULL;
+ *m = 0;
+ return 2;
+ }
+
+ /* Find minimum and maximum length, bound *m by those */
+ for (j = 1; (c[j] == 0) && (j <= BMAX); j++) ;
+
+ k = j; /* minimum code length */
+ for (i = BMAX; (c[i] == 0) && i; i--) ;
+
+ g = i; /* maximum code length */
+ *m = (*m < j) ? j : ((*m > i) ? i : *m);
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ {
+ if ((y -= c[j]) < 0)
+ return 2; /* bad input: more codes than bits */
+ }
+
+ if ((y -= c[i]) < 0)
+ return 2;
+
+ c[i] += y;
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1;
+ xp = x + 2;
+ while (--i) /* note that i == g from above */
+ {
+ *xp++ = (j += *p++);
+ }
+
+ /* Make a table of values in order of bit lengths */
+ p = b;
+ i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ htl = -1; /* no tables yet--level -1 */
+ w = ws[0] = 0; /* bits decoded */
+ u[0] = (huft_t *) NULL; /* just to keep compilers happy */
+ q = (huft_t *) NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > ws[htl + 1])
+ {
+ w = ws[++htl];
+
+ /* compute minimum size table less than or equal to *m bits */
+ z = (z = g - w) > *m ? *m : z; /* upper limit on table size */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+
+ j = ((unsigned)(w + j) > eob_len && (unsigned)w < eob_len)
+ ? eob_len - w : j; /* make EOB code end at table */
+ z = 1 << j; /* table entries for j-bit table */
+ ws[htl+1] = w + j; /* set bits decoded in stack */
+
+ /* allocate and link in new table */
+ q = (huft_t *) wpw_malloc(bufnum,(z + 1) * sizeof(huft_t));
+ if(q==0)
+ return 3;
+
+ *t = q + 1; /* link to list for huft_free() */
+ t = &(q->v.t);
+ u[htl] = ++q; /* table starts after link */
+
+ /* connect to last table, if there is one */
+ if (htl)
+ {
+ x[htl] = i; /* save pattern for backing up */
+
+ /* bits to dump before this table */
+ r.b = (unsigned char) (w - ws[htl - 1]);
+ r.e = (unsigned char) (16 + j); /* bits in this table */
+ r.v.t = q; /* pointer to this table */
+ j = (i & ((1 << w) - 1)) >> ws[htl - 1];
+ u[htl - 1][j] = r; /* connect to last table */
+ }
+ }
+
+ /* set up table entry in r */
+ r.b = (unsigned char) (k - w);
+ if (p >= v + n)
+ r.e = 99; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is EOB
+ code */
+ r.v.n = (unsigned short) (*p++); /* simple code is just the
+ value */
+ }
+ else
+ {
+ r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists
+ */
+ r.v.n = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ {
+ q[j] = r;
+ }
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i &j; j >>= 1)
+ {
+ i ^= j;
+ }
+ i ^= j;
+
+ /* backup over finished tables */
+ while ((i & ((1 << w) - 1)) != x[htl])
+ {
+ w = ws[--htl];
+ }
+ }
+ }
+
+ /* return actual size of base table */
+ *m = ws[1];
+
+ /* Return true (1) if we were given an incomplete table */
+ return y != 0 && g != 1;
+}
+
+/*
+ * inflate (decompress) the codes in a deflated (compressed) block. Return an
+ *error code or zero if it all goes ok.
+ *
+ * tl, td: literal/length and distance decoder tables bl, bd: number of bits
+ *decoded by tl[] and td[]
+ */
+static int inflate_codes_resumeCopy = 0;
+static int inflate_codes(huft_t * my_tl, huft_t * my_td,
+ const unsigned int my_bl, const unsigned int my_bd,
+ int setup)
+{
+ static unsigned int e; /* table entry flag/number of extra bits */
+ static unsigned int n, d; /* length and index for copy */
+ static unsigned int w; /* current gunzip_window position */
+ static huft_t *t; /* pointer to table entry */
+ static unsigned int ml, md; /* masks for bl and bd bits */
+ static unsigned int b; /* bit buffer */
+ static unsigned int k; /* number of bits in bit buffer */
+ static huft_t *tl, *td;
+ static unsigned int bl, bd;
+
+ if (setup) /* 1st time we are called, copy in variables */
+ {
+ tl = my_tl;
+ td = my_td;
+ bl = my_bl;
+ bd = my_bd;
+ /* make local copies of globals */
+ b = gunzip_bb; /* initialize bit buffer */
+ k = gunzip_bk;
+ w = gunzip_outbuf_count; /* initialize gunzip_window position
+ */
+
+ /* inflate the coded data */
+ ml = mask_bits[bl]; /* precompute masks for speed */
+ md = mask_bits[bd];
+ return 0; /* Don't actually do anything the first time */
+ }
+
+ if (inflate_codes_resumeCopy) goto do_copy;
+
+ while (1) /* do until end of block */
+ {
+ b = fill_bitbuffer(b, &k, bl);
+ if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
+ do {
+ if (e == 99)
+ error_die("inflate_codes error 1");
+
+ b >>= t->b;
+ k -= t->b;
+ e -= 16;
+ b = fill_bitbuffer(b, &k, e);
+ } while ((e =
+ (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
+
+ b >>= t->b;
+ k -= t->b;
+ if (e == 16) /* then it's a literal */
+ {
+ gunzip_window[w++] = (unsigned char) t->v.n;
+ if (w == gunzip_wsize)
+ {
+ gunzip_outbuf_count = (w);
+ w = 0;
+ return 1; /* We have a block to read */
+ }
+ }
+ else /* it's an EOB or a length */
+ { /* exit if end of block */
+ if (e == 15)
+ break;
+
+ /* get length of block to copy */
+ b = fill_bitbuffer(b, &k, e);
+ n = t->v.n + ((unsigned) b & mask_bits[e]);
+ b >>= e;
+ k -= e;
+
+ /* decode distance of block to copy */
+ b = fill_bitbuffer(b, &k, bd);
+ if ((e = (t = td + ((unsigned) b & md))->e) > 16)
+ do {
+ if (e == 99)
+ error_die("inflate_codes error 2");
+
+ b >>= t->b;
+ k -= t->b;
+ e -= 16;
+ b = fill_bitbuffer(b, &k, e);
+ } while ((e =
+ (t =
+ t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
+
+ b >>= t->b;
+ k -= t->b;
+ b = fill_bitbuffer(b, &k, e);
+ d = w - t->v.n - ((unsigned) b & mask_bits[e]);
+ b >>= e;
+ k -= e;
+
+ /* do the copy */
+do_copy: do {
+ n -= (e =
+ (e =
+ gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
+ /* copy to new buffer to prevent possible overwrite */
+ if (w - d >= e) /* (this test assumes unsigned comparison)
+ */
+ {
+ rb->memcpy(gunzip_window + w, gunzip_window + d, e);
+ w += e;
+ d += e;
+ }
+ else
+ {
+ /* do it slow to avoid memcpy() overlap */
+ /* !NOMEMCPY */
+ do {
+ gunzip_window[w++] = gunzip_window[d++];
+ } while (--e);
+ }
+
+ if (w == gunzip_wsize)
+ {
+ gunzip_outbuf_count = (w);
+ if (n) inflate_codes_resumeCopy = 1;
+ else inflate_codes_resumeCopy = 0;
+
+ w = 0;
+ return 1;
+ }
+ } while (n);
+ inflate_codes_resumeCopy = 0;
+ }
+ }
+
+ /* restore the globals from the locals */
+ gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
+ gunzip_bb = b; /* restore global bit buffer */
+ gunzip_bk = k;
+
+ /* normally just after call to inflate_codes, but save code by putting it
+ here */
+ /* free the decoding tables, return */
+ huft_free(tl,HUFT_MMP1);
+ huft_free(td,HUFT_MMP2);
+
+ /* done */
+ return 0;
+}
+
+static int inflate_stored(int my_n, int my_b_stored, int my_k_stored, int setup)
+{
+ static unsigned int n, b_stored, k_stored, w;
+ if (setup)
+ {
+ n = my_n;
+ b_stored = my_b_stored;
+ k_stored = my_k_stored;
+ w = gunzip_outbuf_count; /* initialize gunzip_window position */
+ return 0; /* Don't do anything first time */
+ }
+
+ /* read and output the compressed data */
+ while (n--)
+ {
+ b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
+ gunzip_window[w++] = (unsigned char) b_stored;
+ if (w == gunzip_wsize)
+ {
+ gunzip_outbuf_count = (w);
+ w = 0;
+ b_stored >>= 8;
+ k_stored -= 8;
+ return 1; /* We have a block */
+ }
+
+ b_stored >>= 8;
+ k_stored -= 8;
+ }
+
+ /* restore the globals from the locals */
+ gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
+ gunzip_bb = b_stored; /* restore global bit buffer */
+ gunzip_bk = k_stored;
+ return 0; /* Finished */
+}
+
+/*
+ * decompress an inflated block e: last block flag
+ *
+ * GLOBAL VARIABLES: bb, kk,
+ */
+/* Return values: -1 = inflate_stored, -2 = inflate_codes */
+static int inflate_block(int *e)
+{
+ unsigned t; /* block type */
+ unsigned int b; /* bit buffer */
+ unsigned int k; /* number of bits in bit buffer */
+
+ /* make local bit buffer */
+
+ b = gunzip_bb;
+ k = gunzip_bk;
+
+ /* read in last block bit */
+ b = fill_bitbuffer(b, &k, 1);
+ *e = (int) b & 1;
+ b >>= 1;
+ k -= 1;
+
+ /* read in block type */
+ b = fill_bitbuffer(b, &k, 2);
+ t = (unsigned) b & 3;
+ b >>= 2;
+ k -= 2;
+
+ /* restore the global bit buffer */
+ gunzip_bb = b;
+ gunzip_bk = k;
+
+ /* inflate that block type */
+ switch (t)
+ {
+ case 0: /* Inflate stored */
+ {
+ unsigned int n=0; /* number of bytes in block */
+ unsigned int b_stored=0; /* bit buffer */
+ unsigned int k_stored=0; /* number of bits in bit buffer */
+
+ /* make local copies of globals */
+ b_stored = gunzip_bb; /* initialize bit buffer */
+ k_stored = gunzip_bk;
+
+ /* go to byte boundary */
+ n = k_stored & 7;
+ b_stored >>= n;
+ k_stored -= n;
+
+ /* get the length and its complement */
+ b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
+ n = ((unsigned) b_stored & 0xffff);
+ b_stored >>= 16;
+ k_stored -= 16;
+
+ b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
+ if (n != (unsigned) ((~b_stored) & 0xffff))
+ return 1; /* error in compressed data */
+
+ b_stored >>= 16;
+ k_stored -= 16;
+
+ inflate_stored(n, b_stored, k_stored, 1); /* Setup inflate_stored */
+ return -1;
+ }
+ case 1: /* Inflate fixed decompress an inflated type 1 (fixed
+ Huffman codes) block. We should either replace this
+ with a custom decoder, or at least precompute the
+ Huffman tables.
+ */
+ {
+ int i; /* temporary variable */
+ huft_t *tl; /* literal/length code table */
+ huft_t *td; /* distance code table */
+ unsigned int bl; /* lookup bits for tl */
+ unsigned int bd; /* lookup bits for td */
+ unsigned int l[288]; /* length list for huft_build */
+
+ /* set up literal table */
+ for (i = 0; i < 144; i++)
+ {
+ l[i] = 8;
+ }
+ for (; i < 256; i++)
+ {
+ l[i] = 9;
+ }
+ for (; i < 280; i++)
+ {
+ l[i] = 7;
+ }
+ for (; i < 288; i++) /* make a complete, but wrong code set */
+ {
+ l[i] = 8;
+ }
+ bl = 7;
+ if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl,HUFT_MMP1)) != 0)
+ return i;
+
+ /* set up distance table */
+ for (i = 0; i < 30; i++) /* make an incomplete code set */
+ {
+ l[i] = 5;
+ }
+ bd = 5;
+ if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd,HUFT_MMP2)) > 1)
+ {
+ huft_free(tl,HUFT_MMP1);
+ return i;
+ }
+
+ /* decompress until an end-of-block code */
+ inflate_codes(tl, td, bl, bd, 1); /* Setup inflate_codes */
+
+ /* huft_free code moved into inflate_codes */
+
+ return -2;
+ }
+ case 2: /* Inflate dynamic */
+ {
+ const int dbits = 6; /* bits in base distance lookup table */
+ const int lbits = 9; /* bits in base literal/length lookup table */
+
+ huft_t *tl; /* literal/length code table */
+ huft_t *td; /* distance code table */
+ unsigned int i; /* temporary variables */
+ unsigned int j;
+ unsigned int l; /* last length */
+ unsigned int m; /* mask for bit lengths table */
+ unsigned int n; /* number of lengths to get */
+ unsigned int bl; /* lookup bits for tl */
+ unsigned int bd; /* lookup bits for td */
+ unsigned int nb; /* number of bit length codes */
+ unsigned int nl; /* number of literal/length codes */
+ unsigned int nd; /* number of distance codes */
+
+ unsigned int ll[286 + 30]; /* literal/length and distance code
+ lengths */
+ unsigned int b_dynamic; /* bit buffer */
+ unsigned int k_dynamic; /* number of bits in bit buffer */
+
+ /* make local bit buffer */
+ b_dynamic = gunzip_bb;
+ k_dynamic = gunzip_bk;
+
+ /* read in table lengths */
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+ nl = 257 + ((unsigned int) b_dynamic & 0x1f); /* number of
+ literal/length codes
+ */
+
+ b_dynamic >>= 5;
+ k_dynamic -= 5;
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
+ nd = 1 + ((unsigned int) b_dynamic & 0x1f); /* number of distance
+ codes */
+
+ b_dynamic >>= 5;
+ k_dynamic -= 5;
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
+ nb = 4 + ((unsigned int) b_dynamic & 0xf); /* number of bit length
+ codes */
+
+ b_dynamic >>= 4;
+ k_dynamic -= 4;
+ if (nl > 286 || nd > 30)
+ return 1; /* bad lengths */
+
+ /* read in bit-length-code lengths */
+ for (j = 0; j < nb; j++)
+ {
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+ ll[border[j]] = (unsigned int) b_dynamic & 7;
+ b_dynamic >>= 3;
+ k_dynamic -= 3;
+ }
+ for (; j < 19; j++)
+ {
+ ll[border[j]] = 0;
+ }
+
+ /* build decoding table for trees--single level, 7 bit lookup */
+ bl = 7;
+ i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl,HUFT_MMP1);
+ if (i != 0)
+ {
+ if (i == 1)
+ huft_free(tl,HUFT_MMP1);
+
+ return i; /* incomplete code set */
+ }
+
+ /* read in literal and distance code lengths */
+ n = nl + nd;
+ m = mask_bits[bl];
+ i = l = 0;
+ while ((unsigned int) i < n)
+ {
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
+ j = (td = tl + ((unsigned int) b_dynamic & m))->b;
+ b_dynamic >>= j;
+ k_dynamic -= j;
+ j = td->v.n;
+ if (j < 16) /* length of code in bits (0..15) */
+ ll[i++] = l = j; /* save last length in l */
+ else if (j == 16) /* repeat last length 3 to 6 times */
+ {
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
+ j = 3 + ((unsigned int) b_dynamic & 3);
+ b_dynamic >>= 2;
+ k_dynamic -= 2;
+ if ((unsigned int) i + j > n)
+ return 1;
+
+ while (j--)
+ {
+ ll[i++] = l;
+ }
+ }
+ else if (j == 17) /* 3 to 10 zero length codes */
+ {
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
+ j = 3 + ((unsigned int) b_dynamic & 7);
+ b_dynamic >>= 3;
+ k_dynamic -= 3;
+ if ((unsigned int) i + j > n)
+ return 1;
+
+ while (j--)
+ {
+ ll[i++] = 0;
+ }
+ l = 0;
+ }
+ else /* j == 18: 11 to 138 zero length codes */
+ {
+ b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
+ j = 11 + ((unsigned int) b_dynamic & 0x7f);
+ b_dynamic >>= 7;
+ k_dynamic -= 7;
+ if ((unsigned int) i + j > n)
+ return 1;
+
+ while (j--)
+ {
+ ll[i++] = 0;
+ }
+ l = 0;
+ }
+ }
+
+ /* free decoding table for trees */
+ huft_free(tl,HUFT_MMP1);
+
+ /* restore the global bit buffer */
+ gunzip_bb = b_dynamic;
+ gunzip_bk = k_dynamic;
+
+ /* build the decoding tables for literal/length and distance codes */
+ bl = lbits;
+
+ if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl,HUFT_MMP1)) != 0)
+ {
+ if (i == 1)
+ {
+ error_die("Incomplete literal tree");
+ huft_free(tl,HUFT_MMP1);
+ }
+
+ return i; /* incomplete code set */
+ }
+
+ bd = dbits;
+ if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd,HUFT_MMP2)) != 0)
+ {
+ if (i == 1)
+ {
+ error_die("incomplete distance tree");
+ huft_free(td,HUFT_MMP2);
+ }
+
+ huft_free(tl,HUFT_MMP1);
+ return i; /* incomplete code set */
+ }
+
+ /* decompress until an end-of-block code */
+ inflate_codes(tl, td, bl, bd, 1); /* Setup inflate_codes */
+
+ /* huft_free code moved into inflate_codes */
+
+ return -2;
+ }
+ default:
+ /* bad block type */
+ error_die("bad block type");
+ }
+ return 0;
+}
+
+static void calculate_gunzip_crc(void)
+{
+ unsigned int n;
+ for (n = 0; n < gunzip_outbuf_count; n++)
+ {
+ gunzip_crc = crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff]
+ ^ (gunzip_crc >> 8);
+ }
+ gunzip_bytes_out += gunzip_outbuf_count;
+}
+
+static int inflate_get_next_window_method = -1; /* Method == -1 for stored, -2
+ for codes */
+static int inflate_get_next_window_e = 0;
+static int inflate_get_next_window_needAnotherBlock = 1;
+
+static int inflate_get_next_window(void)
+{
+ gunzip_outbuf_count = 0;
+
+ while(1)
+ {
+ int ret=0;
+ if (inflate_get_next_window_needAnotherBlock)
+ {
+ if(inflate_get_next_window_e)
+ {
+ calculate_gunzip_crc();
+ inflate_get_next_window_e = 0;
+ inflate_get_next_window_needAnotherBlock = 1;
+ return 0;
+ } /* Last block */
+
+ inflate_get_next_window_method = inflate_block(&inflate_get_next_window_e);
+ inflate_get_next_window_needAnotherBlock = 0;
+ }
+
+ switch (inflate_get_next_window_method)
+ {
+ case -1: ret = inflate_stored(0,0,0,0);
+ break;
+ case -2: ret = inflate_codes(0,0,0,0,0);
+ break;
+ default:
+ error_die("inflate error");
+ }
+
+ if (ret == 1)
+ {
+ calculate_gunzip_crc();
+ return 1; /* More data left */
+ }
+ else inflate_get_next_window_needAnotherBlock = 1; /* End of that
+ block */
+ }
+ /* Doesnt get here */
+}
+
+/* Initialise bytebuffer, be careful not to overfill the buffer */
+static void inflate_init(unsigned int bufsize)
+{
+ /* Set the bytebuffer size, default is same as gunzip_wsize */
+ bytebuffer_max = bufsize + 8;
+ bytebuffer_offset = 4;
+ bytebuffer_size = 0;
+}
+
+static void inflate_cleanup(void)
+{
+ /* free(bytebuffer); */
+}
+
+USE_DESKTOP(long long) static int
+inflate_unzip(int in,char* outbuffer,uint32_t outbuflen)
+{
+ USE_DESKTOP(long long total = 0; )
+ typedef void (*sig_type)(int);
+
+ /* Allocate all global buffers (for DYN_ALLOC option) */
+ gunzip_outbuf_count = 0;
+ gunzip_bytes_out = 0;
+ gunzip_src_fd = in;
+
+ /* initialize gunzip_window, bit buffer */
+ gunzip_bk = 0;
+ gunzip_bb = 0;
+
+ /* Create the crc table */
+ gunzip_crc = ~0;
+
+ /* Allocate space for buffer */
+ while(1)
+ {
+ int ret = inflate_get_next_window();
+ if((signed int)outbuflen-(signed int)gunzip_outbuf_count<0)
+ {
+ error_msg("write_error");
+ return -1;
+ }
+
+ rb->memcpy(outbuffer,gunzip_window,gunzip_outbuf_count);
+ outbuffer+=sizeof(char)*gunzip_outbuf_count;
+ ifl_total+=sizeof(char)*gunzip_outbuf_count;
+ outbuflen-=gunzip_outbuf_count;
+ USE_DESKTOP(total += gunzip_outbuf_count; )
+ if (ret == 0) break;
+ }
+
+ /* Store unused bytes in a global buffer so calling applets can access it */
+ if (gunzip_bk >= 8)
+ {
+ /* Undo too much lookahead. The next read will be byte aligned so we can
+ discard unused bits in the last meaningful byte. */
+ bytebuffer_offset--;
+ bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
+ gunzip_bb >>= 8;
+ gunzip_bk -= 8;
+ }
+
+ return USE_DESKTOP(total) + 0;
+}
+
+USE_DESKTOP(long long) static int
+inflate_gunzip(int in, char* outbuffer,uint32_t outbuflen)
+{
+ uint32_t stored_crc = 0;
+ unsigned int count;
+ USE_DESKTOP(long long total = ) inflate_unzip(in, outbuffer,outbuflen);
+
+ USE_DESKTOP(if (total < 0) return total;
+
+ )
+
+ /* top up the input buffer with the rest of the trailer */
+ count = bytebuffer_size - bytebuffer_offset;
+ if (count < 8)
+ {
+ xread(in, &bytebuffer[bytebuffer_size], 8 - count);
+ bytebuffer_size += 8 - count;
+ }
+
+ for (count = 0; count != 4; count++)
+ {
+ stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8));
+ bytebuffer_offset++;
+ }
+
+ /* Validate decompression - crc */
+ if (stored_crc != (~gunzip_crc))
+ {
+ error_msg("crc error");
+ return -1;
+ }
+
+ /* Validate decompression - size */
+ if ((signed int)gunzip_bytes_out !=
+ (bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) |
+ (bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24)))
+ {
+ error_msg("incorrect length");
+ return -1;
+ }
+
+ return USE_DESKTOP(total) + 0;
+}
+
+/*An allocated memory buffer at least 0x13100 (72448) bytes long*/
+uint32_t decompress(const char *inflenme,char* outbuffer,uint32_t outbuflen,uint32_t offset,char* membuf)
+{
+ signed char status=0;
+ int exitcode=0;
+ int src_fd;
+ ifl_total=0;
+ /* reset statics */
+ inflate_codes_resumeCopy = 0;
+ inflate_get_next_window_method = -1; /* Method == -1 for stored, -2 for
+ codes */
+ inflate_get_next_window_e = 0;
+ inflate_get_next_window_needAnotherBlock = 1;
+ /* init */
+ inflate_init(0x8000-8);
+ /*Memory init*/
+ huftbuffer1=membuf;
+ huftbuffer2=membuf+0x2A00;
+ gunzip_window=membuf+0x2A00+0xA00;
+ bytebuffer=membuf+0x2A00+0xA00+0x8000;
+ wpw_init_mempool_pdm(HUFT_MMP1,(unsigned char*)huftbuffer1,0x2A00);
+ wpw_init_mempool_pdm(HUFT_MMP2,(unsigned char*)huftbuffer2,0xA00);
+
+ src_fd = rb->open(inflenme, O_RDONLY);
+ if(src_fd==-1)
+ return 0;
+
+ rb->lseek(src_fd,offset,SEEK_SET);
+ if ((exitcode=xread_char(src_fd)) == 0x1f)
+ {
+ unsigned char magic2;
+ magic2 = xread_char(src_fd);
+ if (magic2 == 0x8b)
+ {
+ check_header_gzip(src_fd); /* FIXME: xfunc? _or_die? */
+ status = inflate_gunzip(src_fd, outbuffer,outbuflen);
+ }
+ else
+ {
+ error_msg("invalid magic");
+ exitcode = -1;
+ }
+
+ if (status < 0)
+ {
+ error_msg("error inflating");
+ exitcode = -1;
+ }
+ }
+ else
+ {
+ error_msg("invalid magic");
+ exitcode = -1;
+ }
+
+ inflate_cleanup();
+ wpw_destroy_mempool(HUFT_MMP1);
+ wpw_destroy_mempool(HUFT_MMP2);
+ rb->close(src_fd);
+
+ if(exitcode==-1)
+ return 0;
+
+ return ifl_total;
+}
diff --git a/apps/plugins/wikiviewer/inflate.h b/apps/plugins/wikiviewer/inflate.h
new file mode 100644
index 0000000..db2b3e1
--- /dev/null
+++ b/apps/plugins/wikiviewer/inflate.h
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef INFLATE_H
+#define INFLATE_H
+#include <inttypes.h>
+#include "plugin.h"
+
+uint32_t decompress(const char *inflenme,char* outbuffer,uint32_t outbuflen,
+ uint32_t offset,char* membuf);
+#endif
diff --git a/apps/plugins/wikiviewer/mallocer.c b/apps/plugins/wikiviewer/mallocer.c
new file mode 100644
index 0000000..a1c4ba9
--- /dev/null
+++ b/apps/plugins/wikiviewer/mallocer.c
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Frederik M.J. Vestre
+ * Copyright (C) 2006 Adam Gashlin
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+/*
+ Based on the malloc in codeclib Copyright (C) 2005 Dave Chapman
+ */
+
+#include "mallocer.h"
+
+unsigned char* mallocbuf[MEMPOOL_MAX];
+long mem_ptr[MEMPOOL_MAX];
+size_t bufsize[MEMPOOL_MAX];
+
+#include "plugin.h"
+
+int wpw_init_mempool(unsigned char mempool)
+{
+ mem_ptr[mempool] = 0;
+ mallocbuf[mempool] = (unsigned char *)rb->plugin_get_buffer(&bufsize[mempool]);
+ return 0;
+}
+
+int wpw_init_mempool_pdm(unsigned char mempool,
+ unsigned char* mem,long memsize)
+{
+ mem_ptr[mempool] = 0;
+ mallocbuf[mempool] = mem;
+ bufsize[mempool]=memsize;
+ return 0;
+}
+
+void wpw_reset_mempool(unsigned char mempool)
+{
+ mem_ptr[mempool]=0;
+}
+
+void wpw_destroy_mempool(unsigned char mempool)
+{
+ mem_ptr[mempool] = 0;
+ mallocbuf[mempool] =0;
+ bufsize[mempool]=0;
+}
+
+long wpw_available(unsigned char mempool)
+{
+ return bufsize[mempool]-mem_ptr[mempool];
+}
+
+void* wpw_malloc(unsigned char mempool,size_t size)
+{
+ void* x;
+
+ if (mem_ptr[mempool] + size > bufsize[mempool] )
+ return NULL;
+
+ x=&mallocbuf[mempool][mem_ptr[mempool]];
+ mem_ptr[mempool]+=(size+3)&~3; /* Keep memory 32-bit aligned */
+
+ return(x);
+}
+
+void* wpw_calloc(unsigned char mempool,size_t nmemb, size_t size)
+{
+ void* x;
+ x = wpw_malloc(mempool,nmemb*size);
+ if (x == NULL)
+ return NULL;
+
+ rb->memset(x,0,nmemb*size);
+ return(x);
+}
+
+void codec_free(unsigned char mempool,void* ptr)
+{
+ (void)ptr;
+ (void)mempool;
+}
+
+void* wpw_realloc(unsigned char mempool,void* ptr, size_t size)
+{
+ void* x;
+ (void)ptr;
+ x = wpw_malloc(mempool,size);
+ return(x);
+}
diff --git a/apps/plugins/wikiviewer/mallocer.h b/apps/plugins/wikiviewer/mallocer.h
new file mode 100644
index 0000000..a4bf520
--- /dev/null
+++ b/apps/plugins/wikiviewer/mallocer.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#define MEMPOOL_MAX 10
+#include <inttypes.h>
+#include "plugin.h"
+
+int wpw_init_mempool(unsigned char mempool);
+int wpw_init_mempool_pdm(unsigned char mempool,
+ unsigned char* mem,long memsize);
+
+void wpw_reset_mempool(unsigned char mempool);
+void wpw_destroy_mempool(unsigned char mempool);
+void* wpw_malloc(unsigned char mempool,size_t size);
+void* wpw_calloc(unsigned char mempool,size_t nmemb, size_t size);
+void codec_free(unsigned char mempool,void* ptr);
+void* wpw_realloc(unsigned char mempool,void* ptr, size_t size);
+long wpw_available(unsigned char mempool);
diff --git a/apps/plugins/wikiviewer/mwdb.c b/apps/plugins/wikiviewer/mwdb.c
new file mode 100644
index 0000000..a3b808b
--- /dev/null
+++ b/apps/plugins/wikiviewer/mwdb.c
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Frederik M.J. Vestre
+ * Copyright (C) 2006 Adam Gashlin
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "mallocer.h" /*mallocer resetable memory allocator*/
+#include "inflate.h"
+#include "shared/btsearch.h"
+#include <inttypes.h>
+#include "plugin.h"
+
+/*Remember to require this:
+ extern struct plugin_api *rb;
+
+ Matthias Larisch: This is already done in btsearch.h. maybe it should be
+ moved
+ */
+
+unsigned int mwdb_p_xtpt;
+
+static int mwdb_nprintf(const char *fmt, ...)
+{
+ char p_buf[50];
+ bool ok;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ok = rb->vsnprintf(p_buf,sizeof(p_buf), fmt, ap);
+ va_end(ap);
+
+ rb->lcd_putsxy(1,mwdb_p_xtpt, (unsigned char *)p_buf);
+ rb->lcd_update();
+
+ mwdb_p_xtpt+=rb->font_get(FONT_UI)->height;
+ if(mwdb_p_xtpt>LCD_HEIGHT-rb->font_get(FONT_UI)->height)
+ {
+ mwdb_p_xtpt=0;
+ rb->lcd_clear_display();
+ }
+
+ return 1;
+}
+
+int mwdb_findarticle(const char* filename,
+ char* artnme,
+ uint16_t artnmelen,
+ uint32_t * res_lo,
+ uint32_t * res_hi,
+ bool progress,
+ bool promptname,
+ bool casesense)
+{
+ int fd;
+ char fnbuf[rb->strlen(filename)+6];
+
+ if(promptname)
+ {
+#ifdef HAVE_TOUCHSCREEN
+ /* this is a hack till proper touchscreen keyboard input arrives */
+ rb->touchscreen_set_mode(TOUCHSCREEN_BUTTON);
+#endif
+ if (rb->kbd_input(artnme,artnmelen))
+ {
+ artnme[0]='\0';
+#ifdef HAVE_TOUCHSCREEN
+ rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
+#endif
+ return false; /* proper abort on keyboard abort */
+ }
+#ifdef HAVE_TOUCHSCREEN
+ rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
+#endif
+ }
+
+
+ rb->snprintf(fnbuf,rb->strlen(filename)+6,"%s.wwi",filename);
+ fd = rb->open(fnbuf, 0);
+
+ if (fd==-1)
+ return false;
+
+ if(progress)
+ {
+ mwdb_p_xtpt=0;
+ rb->lcd_clear_display();
+ mwdb_nprintf("Searching...");
+ }
+
+ search_btree((void*)fd,artnme,rb->strlen(artnme),0,res_lo,res_hi,casesense);
+ rb->close(fd);
+
+ if(*res_hi==0) /* not found */
+ return false;
+ else if(progress)
+ mwdb_nprintf("Found:%d%d",*res_lo,*res_hi);
+
+ return true;
+}
+
+int mwdb_loadarticle(const char * filename,
+ void * scratchmem,
+ void * articlemem,
+ uint32_t articlememlen,
+ uint32_t *articlelen,
+ uint32_t res_lo,
+ uint32_t res_hi)
+{
+ char fnbuf[rb->strlen(filename)+6];
+
+ mwdb_p_xtpt=0;
+ rb->lcd_clear_display();
+ mwdb_nprintf("Loading %d%d...",res_lo,res_hi);
+
+ rb->snprintf(fnbuf,rb->strlen(filename)+6,"%s%lu.wwa",filename,res_lo);
+
+ *articlelen=decompress(fnbuf,articlemem,articlememlen,res_hi,scratchmem);
+
+ ((unsigned char*)articlemem)[*articlelen]='\0';
+
+ return true;
+}
diff --git a/apps/plugins/wikiviewer/mwdb.h b/apps/plugins/wikiviewer/mwdb.h
new file mode 100644
index 0000000..73b3916
--- /dev/null
+++ b/apps/plugins/wikiviewer/mwdb.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef MWDB_H
+#define MWDB_H
+#include <inttypes.h>
+
+
+int mwdb_findarticle(const char* filename,
+ char* artnme,
+ uint16_t artnmelen,
+ uint32_t *res_lo,
+ uint32_t *res_hi,
+ bool progress,
+ bool promptname,
+ bool casesense
+ );
+
+int mwdb_loadarticle(const char * filename,
+ void * scratchmem,
+ void * articlemem,
+ uint32_t articlememlen,
+ uint32_t *articlelen,
+ uint32_t res_lo,
+ uint32_t res_hi);
+
+#endif
diff --git a/apps/plugins/wikiviewer/shared/btsearch.c b/apps/plugins/wikiviewer/shared/btsearch.c
new file mode 100644
index 0000000..9df9479
--- /dev/null
+++ b/apps/plugins/wikiviewer/shared/btsearch.c
@@ -0,0 +1,153 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "utf8_aux.h"
+#include "btsearch.h"
+
+#if 0
+#define DEBUGF printf
+#else
+#define DEBUGF(...)
+#endif
+
+#define ROCKBOX_LITTLE_ENDIAN
+#define KEY_MAXLEN 200
+
+static inline unsigned short swap16(unsigned short value)
+/*
+ result[15..8] = value[ 7..0];
+ result[ 7..0] = value[15..8];
+ */
+{
+ return (value >> 8) | (value << 8);
+}
+
+static inline unsigned long swap32(unsigned long value)
+/*
+ result[31..24] = value[ 7.. 0];
+ result[23..16] = value[15.. 8];
+ result[15.. 8] = value[23..16];
+ result[ 7.. 0] = value[31..24];
+ */
+{
+ unsigned long hi = swap16(value >> 16);
+ unsigned long lo = swap16(value & 0xffff);
+ return (lo << 16) | hi;
+}
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+#define letoh16(x) (x)
+#define letoh32(x) (x)
+#else
+#define letoh16(x) swap16(x)
+#define letoh32(x) swap32(x)
+#endif
+
+#ifdef BTSEARCH_MAIN
+int main(int argc,char * argv[])
+{
+ if(argc<3)
+ printf("Usage: btsearch <file> <key>\n");
+ else
+ {
+ FILE *fd=fopen(argv[1],"r");
+ uint32_t res_lo;
+ uint32_t res_hi;
+ fseek(fd,0,SEEK_SET);
+ search_btree(fd, argv[2], strlen(argv[2]), 0, &res_lo, &res_hi, false);
+ printf("Result: %d,%d\n",res_lo,res_hi);
+ }
+
+ return 0;
+}
+
+#endif
+
+void search_btree(void* file, const char* key, uint16_t rkeylen, uint32_t globoffs,
+ uint32_t* res_lo, uint32_t* res_hi, const bool casesense)
+{
+ unsigned char nd_key[KEY_MAXLEN];
+ uint8_t node_flags;
+ uint16_t node_nr_active,i,keylen;
+ uint32_t chldptr;
+ uint64_t dtaptr_lo,dtaptr_hi;
+ fread(&node_flags,sizeof(uint8_t),1,file);
+ fread(&node_nr_active,sizeof(uint16_t),1,file);
+ node_nr_active=letoh16(node_nr_active);
+ if(node_nr_active<1) /* error */
+ goto err;
+
+ for(i=0; i<node_nr_active; i++)
+ {
+ fread(&dtaptr_lo,sizeof(uint32_t),1,file);
+ dtaptr_lo=letoh32(dtaptr_lo);
+ fread(&dtaptr_hi,sizeof(uint32_t),1,file);
+ dtaptr_hi=letoh32(dtaptr_hi);
+ fread(&chldptr,sizeof(uint32_t),1,file);
+ chldptr=letoh32(chldptr);
+ fread(&keylen,sizeof(uint16_t),1,file);
+ keylen=letoh32(keylen);
+ fread(&nd_key,sizeof(unsigned char),(keylen<KEY_MAXLEN) ? keylen : KEY_MAXLEN,file);
+ if(keylen-((keylen<KEY_MAXLEN) ? keylen : KEY_MAXLEN)>0)
+ fseek(file,keylen-((keylen<KEY_MAXLEN) ? keylen : KEY_MAXLEN),SEEK_CUR);
+
+ keylen=(keylen<KEY_MAXLEN) ? keylen : KEY_MAXLEN;
+
+ nd_key[keylen]=0;
+ DEBUGF("CMP: %s, %s\n", key, nd_key);
+ if(utf8strcnmp(((const unsigned char*)key),((const unsigned char*)nd_key),rkeylen,keylen,casesense)>0)
+ continue;
+
+ if(utf8strcnmp(((const unsigned char*)key),((const unsigned char*)nd_key),rkeylen,keylen,casesense)==0)
+ {
+ DEBUGF("Found! %s\n", nd_key);
+ *res_lo=dtaptr_lo;
+ *res_hi=dtaptr_hi;
+ return;
+ }
+
+ if(chldptr==0||node_flags==1)
+ goto err;
+
+ fseek(file,globoffs+chldptr,SEEK_SET);
+ search_btree(file,key,rkeylen,globoffs,res_lo,res_hi,casesense);
+ return;
+ }
+
+ if(node_flags!=1) /* node not leaf */
+ {
+ fread(&chldptr,sizeof(uint32_t),1,file);
+ chldptr=letoh32(chldptr);
+ if(chldptr==0) /* leaf */
+ goto err;
+
+ fseek(file,globoffs+chldptr,SEEK_SET);
+ search_btree(file,key,rkeylen,globoffs,res_lo,res_hi,casesense);
+ }
+
+ return;
+err:
+ *res_lo=*res_hi=0;
+}
diff --git a/apps/plugins/wikiviewer/shared/btsearch.h b/apps/plugins/wikiviewer/shared/btsearch.h
new file mode 100644
index 0000000..07b150b
--- /dev/null
+++ b/apps/plugins/wikiviewer/shared/btsearch.h
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef BTSEARCH_H
+#define BTSEARCH_H
+#include <inttypes.h>
+
+void search_btree(void *fp, const char* key, uint16_t rkeylen, uint32_t globoffs,
+ uint32_t* res_lo, uint32_t* res_hi, const bool casesense);
+#endif
diff --git a/apps/plugins/wikiviewer/shared/utf8_aux.c b/apps/plugins/wikiviewer/shared/utf8_aux.c
new file mode 100644
index 0000000..3b30a7c
--- /dev/null
+++ b/apps/plugins/wikiviewer/shared/utf8_aux.c
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <ctype.h>
+#include "utf8_aux.h"
+
+char utf8strcnmp(const unsigned char *s1, const unsigned char *s2,uint16_t n1,uint16_t n2, const bool casesense)
+{
+ unsigned short c1,c2;
+ const unsigned char *s1p,*s2p;
+ s1p=s1;
+ s2p=s2;
+ for(;; )
+ {
+ if(s1p-s1==n1)
+ {
+ if(n1==n2&&n2==s2p-s2) return 0;
+ else return -1;
+ }
+
+ if(s2p-s2==n2)
+ {
+ /* printf("N1:%u,N2:%u,s1p-s1:%d\n",n1,n2,s1p-s1); */
+ if(n1==n2&&n1==s1p-s1) return 0;
+ else return 1;
+ }
+
+ s1p=utf8decode(s1p,&c1);
+ s2p=utf8decode(s2p,&c2);
+ if(c1==' ') c1='_';
+
+ if(c2==' ') c2='_';
+
+ /* if(s1p-s1==1&&s2p-s2==1&&c1<128&&c2<128){ */
+ if(!casesense && c1<128&&c2<128)
+ {
+ /* printf("TLC\n); */
+ c1=tolower(c1);
+ c2=tolower(c2);
+ }
+
+ if(c1<c2) return -1;
+ else if (c1>c2) return 1;
+ }
+ /* printf("CMPEND\n"); */
+ return 0; /*won't happen*/
+}
+
+/* Decode 1 UTF-8 char and return a pointer to the next char. */
+const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs)
+{
+ unsigned char c = *utf8++;
+ unsigned long code;
+ int tail = 0;
+
+ if ((c <= 0x7f) || (c >= 0xc2))
+ {
+ /* Start of new character. */
+ if (c < 0x80) /* U-00000000 - U-0000007F, 1 byte */
+ code = c;
+ else if (c < 0xe0) /* U-00000080 - U-000007FF, 2 bytes */
+ {
+ tail = 1;
+ code = c & 0x1f;
+ }
+ else if (c < 0xf0) /* U-00000800 - U-0000FFFF, 3 bytes */
+ {
+ tail = 2;
+ code = c & 0x0f;
+ }
+ else if (c < 0xf5) /* U-00010000 - U-001FFFFF, 4 bytes */
+ {
+ tail = 3;
+ code = c & 0x07;
+ }
+ else
+ /* Invalid size. */
+ code = 0xfffd;
+
+ while (tail-- && ((c = *utf8++) != 0))
+ {
+ if ((c & 0xc0) == 0x80)
+ /* Valid continuation character. */
+ code = (code << 6) | (c & 0x3f);
+ else
+ {
+ /* Invalid continuation char */
+ code = 0xfffd;
+ utf8--;
+ break;
+ }
+ }
+ }
+ else
+ /* Invalid UTF-8 char */
+ code = 0xfffd;
+
+ /* currently we don't support chars above U-FFFF */
+ *ucs = (code < 0x10000) ? code : 0xfffd;
+ return utf8;
+}
diff --git a/apps/plugins/wikiviewer/shared/utf8_aux.h b/apps/plugins/wikiviewer/shared/utf8_aux.h
new file mode 100644
index 0000000..4e14c54
--- /dev/null
+++ b/apps/plugins/wikiviewer/shared/utf8_aux.h
@@ -0,0 +1,30 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __UTF8_AUX_H_
+#define __UTF8_AUX_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs);
+char utf8strcnmp(const unsigned char *s1, const unsigned char *s2, uint16_t n1,
+ uint16_t n2, const bool casesense);
+
+#endif /* __UTF8_AUX_H_ */
diff --git a/apps/plugins/wikiviewer/wikiviewer.c b/apps/plugins/wikiviewer/wikiviewer.c
new file mode 100644
index 0000000..a17c1a4
--- /dev/null
+++ b/apps/plugins/wikiviewer/wikiviewer.c
@@ -0,0 +1,1956 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Frederik M.J. Vestre
+ * Copyright (C) 2006 Adam Gashlin
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include <ctype.h>
+#include "lib/playback_control.h"
+#include <inttypes.h>
+#include "mallocer.h" /*mallocer resetable memory allocator*/
+#include "mwdb.h"
+#include "lib/pluginlib_exit.h"
+#if LCD_DEPTH > 1
+ #define COLORLINKS
+#endif
+
+/* keymappings */
+
+#if (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define WIKIVIEWER_MENU BUTTON_MENU
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD BUTTON_SCROLL_FWD
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_SCROLL_FWD|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_SCROLL_BACK
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_SCROLL_BACK|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_PLAY
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_PLAY|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define WIKIVIEWER_MENU BUTTON_OFF
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_REC|BUTTON_MODE)
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_MODE)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_MODE)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_REC
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_REC|BUTTON_MODE)
+
+#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
+#define WIKIVIEWER_MENU BUTTON_MENU
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_A
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_A|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
+#define WIKIVIEWER_MENU BUTTON_MENU
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_BACK
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_BACK|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == MROBE100_PAD)
+#define WIKIVIEWER_MENU BUTTON_MENU
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_DISPLAY
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_DISPLAY|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
+#define WIKIVIEWER_MENU BUTTON_REC
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_PLAY
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_PLAY|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
+#define WIKIVIEWER_MENU BUTTON_POWER
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD2 BUTTON_VOL_UP
+#define WIKIVIEWER_FWD2_REPEAT (BUTTON_VOL_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK2 BUTTON_VOL_DOWN
+#define WIKIVIEWER_BACK2_REPEAT (BUTTON_VOL_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_REC
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_REC|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
+#define WIKIVIEWER_MENU BUTTON_POWER
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+#define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD2 BUTTON_SCROLL_FWD
+#define WIKIVIEWER_FWD2_REPEAT (BUTTON_SCROLL_FWD|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK2 BUTTON_SCROLL_BACK
+#define WIKIVIEWER_BACK2_REPEAT (BUTTON_SCROLL_BACK|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+#define WIKIVIEWER_RECORD BUTTON_REC
+#define WIKIVIEWER_RECORD_REPEAT (BUTTON_REC|BUTTON_REPEAT)
+
+#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
+#define WIKIVIEWER_MENU BUTTON_HOME
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+/* #define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT) */
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD2 BUTTON_SCROLL_FWD
+#define WIKIVIEWER_FWD2_REPEAT (BUTTON_SCROLL_FWD|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK2 BUTTON_SCROLL_BACK
+#define WIKIVIEWER_BACK2_REPEAT (BUTTON_SCROLL_BACK|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+/* #define WIKIVIEWER_RECORD (BUTTON_SELECT|BUTTON_REPEAT) */
+/* #define WIKIVIEWER_RECORD_REPEAT (BUTTON_HOME|BUTTON_REPEAT) */
+
+#elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
+#define WIKIVIEWER_MENU BUTTON_POWER
+#define WIKIVIEWER_SELECT BUTTON_SELECT
+/* #define WIKIVIEWER_STOP_RECORD (BUTTON_SELECT|BUTTON_REPEAT) */
+#define WIKIVIEWER_FWD BUTTON_DOWN
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_DOWN|BUTTON_REPEAT)
+#define WIKIVIEWER_FWD2 BUTTON_PLAYPAUSE
+#define WIKIVIEWER_FWD2_REPEAT (BUTTON_PLAYPAUSE|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK BUTTON_UP
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_UP|BUTTON_REPEAT)
+#define WIKIVIEWER_BACK2 BUTTON_BACK
+#define WIKIVIEWER_BACK2_REPEAT (BUTTON_BACK|BUTTON_REPEAT)
+#define WIKIVIEWER_PREV BUTTON_LEFT
+#define WIKIVIEWER_NEXT BUTTON_RIGHT
+/* #define WIKIVIEWER_RECORD (BUTTON_SELECT|BUTTON_REPEAT) */
+/* #define WIKIVIEWER_RECORD_REPEAT (BUTTON_HOME|BUTTON_REPEAT) */
+
+#elif (CONFIG_KEYPAD == ONDAVX747_PAD)
+#define WIKIVIEWER_MENU BUTTON_POWER
+
+#endif
+
+#ifdef HAVE_TOUCHSCREEN
+#ifndef WIKIVIEWER_SELECT
+#define WIKIVIEWER_SELECT BUTTON_CENTER
+#endif
+#ifndef WIKIVIEWER_MENU
+#define WIKIVIEWER_MENU BUTTON_TOPLEFT
+#endif
+#ifndef WIKIVIEWER_FWD
+#define WIKIVIEWER_FWD BUTTON_BOTTOMMIDDLE
+#define WIKIVIEWER_FWD_REPEAT (BUTTON_BOTTOMMIDDLE|BUTTON_REPEAT)
+#endif
+#ifndef WIKIVIEWER_BACK
+#define WIKIVIEWER_BACK BUTTON_TOPMIDDLE
+#define WIKIVIEWER_BACK_REPEAT (BUTTON_TOPMIDDLE|BUTTON_REPEAT)
+#endif
+#ifndef WIKIVIEWER_PREV
+#define WIKIVIEWER_PREV BUTTON_MIDLEFT
+#endif
+#ifndef WIKIVIEWER_NEXT
+#define WIKIVIEWER_NEXT BUTTON_MIDRIGHT
+#endif
+#endif
+
+#define LINEBUFLEN 255
+#define MARKUP_STARTULINE 1
+#define MARKUP_ENDULINE 2
+#define MARKUP_STARTBOLD 3
+#define MARKUP_ENDBOLD 4
+#define MARKUP_STARTITALIC 5
+#define MARKUP_ENDITALIC 6
+#define MARKUP_STARTLINK 7
+#define MARKUP_ENDLINK 8
+#define MARKUP_LINEFEED 10
+#define MARKUP_MODE 15
+
+
+/* change on any change to file format, histstate */
+#define SAVE_FILE_VERSION_MAGIC 0x12340004
+
+static bool wrquit=false;
+static char filename[MAX_PATH+1]; /* name of database, without extension */
+
+#define MAINMEMBUF 0
+
+
+static void *inflatebuf; /* heap for gunzip */
+static char *articlebuf; /* destination for uncompressed article */
+static uint32_t articlebuflen=0; /* size of the article buffer */
+static uint32_t articlelen=0; /* used size of the article buffer */
+
+static int lastlinkcount=0; /* set by render() to the number of links
+ found */
+
+/* provides everything render() needs to know to render a screen */
+struct linestate {
+ int32_t renderoff;
+ int renderlen;
+ int32_t linknameoff;
+ int inlink,inlinkname;
+#ifdef COLORLINKS
+ uint8_t inem;
+#endif
+ int eof; /* should be interpreted as disallowing further scrolling down */
+};
+
+#define ARTICLENAMEBUF_LENGTH 255
+
+/* where we'll be scrolling next */
+static struct linestate curlinestate,nextlinestate;
+
+/* scrollback, to allow scrolling backwards fairly quickly (without some memory
+ entire article might need to be reparsed to find context of previous line)
+ stack, up to SCROLLBACK_LENGTH, past that the oldest entry is discarded */
+#define SCROLLBACK_LENGTH 50
+
+static int curlinehist,linehistdepth;
+static int curline;
+static struct linestate linehist[SCROLLBACK_LENGTH];
+
+/* bookmarks, for going back to a previous article */
+
+struct bookstate {
+ char name[ARTICLENAMEBUF_LENGTH];
+ int32_t curoff;
+ uint32_t res_lo; /* avoid searching again */
+ uint32_t res_hi;
+};
+
+
+/* history, for going back to a previous article */
+
+struct histstate {
+ char name[ARTICLENAMEBUF_LENGTH];
+ int32_t curoff;
+ uint32_t res_lo; /* avoid searching again */
+ uint32_t res_hi;
+};
+
+/*
+ It should be possible to reconstruct a linestate from a histstate as follows:
+ 1) load the article given by res_lo, res_hi 2) linestate=0 3) while
+ linestate.renderoff<histstate.curoff && !eof, advance
+ */
+
+/* stack, up to HISTORY_LENGTH deep, past that oldest entry is discarded */
+/* the current article occupies hist[curhist] */
+#define HISTORY_LENGTH 20
+
+static int curhist,histdepth;
+static int bookdepth;
+static bool record_stop_flag=true;
+static struct histstate hist[HISTORY_LENGTH];
+static struct bookstate book[200];
+static int record_flag=0; /*set to 1 when recording a line to an external
+ file*/
+static int last_line_i=0; /*set to linespace when recording a line to an
+ external file*/
+static char last_line[256]; /* the current line of text to be recorded */
+static int end_of_file=0; /*flag for end of file in recording*/
+static int record_linebreak=0; /*flag for line break in recorded line*/
+
+static void viewer_exit(void *parameter);
+static struct linestate render(struct linestate cur, int norender,
+ int linktonote);
+static void readlink(struct linestate cur, int link, char * namebuf,
+ int buflen);
+
+static void viewer_exit(void *parameter)
+{
+ (void)parameter;
+}
+
+static void set_article_offset(int32_t off);
+static void reset_scrollback(void);
+static void advance_scrollback(int a, int dorender);
+static void reset_history(void);
+static void advance_history(int d);
+
+static int load_bookmarks(const char * filename)
+{
+ char fnbuf[MAX_PATH+1];
+ int fd, i, booki, ret = 0;
+ uint32_t magic=0;
+
+ rb->snprintf(fnbuf,MAX_PATH,"%s.wwb",filename);
+ fd = rb->open(fnbuf, O_RDONLY);
+
+ if (fd<0)
+ goto err;
+
+ if (rb->read(fd,&magic,sizeof(magic))<=0)
+ goto err;
+
+ if (magic!=SAVE_FILE_VERSION_MAGIC)
+ goto err;
+
+ if (rb->read(fd,&booki,sizeof(booki))<=0)
+ goto err;
+
+ if (booki<=0)
+ goto err;
+
+ for (i=0; i<booki; i++)
+ {
+ if (rb->read(fd,book+i,sizeof(struct bookstate))<=0)
+ goto err;
+ }
+
+ bookdepth = booki;
+
+ ret = 1;
+
+err:
+ if(fd >= 0)
+ rb->close(fd);
+
+ return ret;
+}
+
+static void save_bookmark(const char * filename)
+{
+ char fnbuf[MAX_PATH+1];
+ int fd;
+ int i,booki;
+ uint32_t magic;
+
+ rb->snprintf(fnbuf,MAX_PATH,"%s.wwb",filename);
+
+ fd = rb->open(fnbuf, O_WRONLY|O_CREAT);
+ if (fd<0) return;
+
+ magic=SAVE_FILE_VERSION_MAGIC;
+ booki=0;
+
+ rb->write(fd, &magic, sizeof(magic));
+ rb->write(fd, &bookdepth, sizeof(bookdepth));
+
+ for (i=0; i<bookdepth; i++)
+ {
+ rb->write(fd, book+booki, sizeof(struct bookstate));
+ booki=(booki+1);
+ }
+
+ rb->close(fd);
+}
+
+static void add_bookmark(void)
+{
+ rb->strcpy(book[bookdepth].name,hist[curhist].name);
+ book[bookdepth].curoff=hist[curhist].curoff;
+ book[bookdepth].res_lo=hist[curhist].res_lo;
+ book[bookdepth].res_hi=hist[curhist].res_hi;
+ bookdepth++;
+}
+
+static void render_bookmarks(int off)
+{
+ int i=(bookdepth-1),j,d;
+ int x,y,fontheight,t;
+ char buf[10];
+
+ rb->lcd_clear_display();
+
+ y=1;
+ fontheight = rb->font_get(FONT_UI)->height;
+
+ for (j=1,d=(bookdepth); d>0 && y+fontheight<LCD_HEIGHT; d--,j++)
+ {
+ if (j>=(off+1))
+ {
+ rb->snprintf(buf,10,"%3d. ",j);
+ rb->lcd_putsxy(1,y,buf);
+ rb->lcd_getstringsize(buf,&x,&t);
+ rb->lcd_putsxy(1+x,y,book[i].name);
+ y+=fontheight;
+ }
+
+ i--;
+ }
+
+ rb->lcd_update();
+}
+
+static void remove_bookmark(int bookviewoff)
+{
+ int i,booki;
+
+ booki=bookdepth-bookviewoff-1;
+ for (i=booki; i<bookdepth; i++)
+ {
+ rb->strcpy(book[i].name,book[(i+1)].name);
+ book[i].curoff=book[(i+1)].curoff;
+ book[i].res_lo=book[(i+1)].res_lo;
+ book[i].res_hi=book[(i+1)].res_hi;
+ }
+ bookdepth--;
+}
+
+static void save_status(const char * filename)
+{
+ char fnbuf[MAX_PATH+1];
+ int fd;
+ int i,histi;
+ uint32_t magic;
+
+ rb->snprintf(fnbuf,MAX_PATH,"%s.wws",filename);
+
+ fd = rb->open(fnbuf, O_WRONLY|O_CREAT);
+ if (fd<0) return;
+
+ magic=SAVE_FILE_VERSION_MAGIC;
+
+ rb->write(fd, &magic, sizeof(magic));
+ rb->write(fd, &histdepth, sizeof(histdepth));
+
+ histi=curhist-(histdepth-1);
+ if (histi<0) histi+=HISTORY_LENGTH;
+
+ for (i=0; i<HISTORY_LENGTH; i++)
+ {
+ rb->write(fd, hist+histi, sizeof(struct histstate));
+ histi=(histi+1)%HISTORY_LENGTH;
+ }
+
+ rb->close(fd);
+}
+
+static int load_status(const char * filename)
+{
+ char fnbuf[MAX_PATH+1];
+ int fd, i, histi, ret = 0;
+ uint32_t magic=0;
+
+ rb->snprintf(fnbuf,MAX_PATH,"%s.wws",filename);
+ fd = rb->open(fnbuf, O_RDONLY);
+ if (fd<0)
+ goto err;
+
+ if (rb->read(fd,&magic,sizeof(magic))<=0)
+ goto err;
+
+ if (magic!=SAVE_FILE_VERSION_MAGIC)
+ goto err;
+
+ if (rb->read(fd,&histi,sizeof(histi))<=0)
+ goto err;
+
+ if (histi<=0)
+ goto err;
+
+ for (i=0; i<histi; i++)
+ {
+ if (rb->read(fd,hist+i,sizeof(struct histstate))<=0)
+ goto err;
+ }
+
+ histdepth=histi;
+ curhist=i-1;
+
+ ret = 1;
+
+err:
+ if(fd >= 0)
+ rb->close(fd);
+
+ return ret;
+}
+
+static void set_article_offset(int32_t off)
+{
+ /* go to offset off in current article */
+
+ reset_scrollback();
+ curline=0;
+ rb->memset(&curlinestate,0,sizeof(curlinestate));
+ rb->memset(&nextlinestate,0,sizeof(nextlinestate));
+
+ hist[curhist].curoff=curlinestate.renderoff=10+articlebuf[8];
+ curlinestate.renderlen=articlelen-10-articlebuf[8];
+
+ nextlinestate=render(curlinestate,1,0);
+
+ while (!nextlinestate.eof && hist[curhist].curoff<off)
+ {
+ advance_scrollback(1,0);
+ }
+}
+
+static void reset_scrollback(void)
+{
+ curlinehist=0;
+ linehistdepth=0;
+}
+
+/* all scrolling done through this */
+static void advance_scrollback(int a, int dorender)
+{
+ int i;
+ if (a<-1)
+ {
+ if ((a+curline)<0)
+ a=(-curline);
+
+ for (i=0; i<-a; i++)
+ {
+ advance_scrollback(-1,(dorender && i==-a-1));
+ }
+ }
+ else if (a>1)
+ for (i=0; i<a; i++)
+ {
+ advance_scrollback(1,(dorender && i==a-1));
+ }
+ else if (a==0)
+ return;
+
+ else if (a<0) /* a==-1 */
+ { /* scroll backwards one line */
+ if (curline==0) return;
+
+ curline--;
+
+ if (linehistdepth>0)
+ {
+ /* if we have records to fall back on */
+ linehistdepth--;
+ curlinehist--;
+
+ if (curlinehist<0)
+ curlinehist=SCROLLBACK_LENGTH-1;
+
+ curlinestate=linehist[curlinehist];
+ nextlinestate=render(curlinestate,!dorender,0);
+ hist[curhist].curoff=curlinestate.renderoff;
+ }
+ else
+ {
+ int amounttoscroll=curline;
+ /* redo from start */
+ reset_scrollback(); /* clear scrollback */
+ set_article_offset(0); /* go to top */
+ advance_scrollback(amounttoscroll,dorender); /* scoll down */
+ }
+ }
+ else /* a==1 */
+ /* scroll forwards one line */
+ if (!nextlinestate.eof || ((record_flag==1) && (end_of_file==0)))
+ {
+ linehist[curlinehist]=curlinestate;
+ curlinehist=(curlinehist+1)%SCROLLBACK_LENGTH;
+ linehistdepth++;
+
+ if (linehistdepth>SCROLLBACK_LENGTH)
+ linehistdepth=SCROLLBACK_LENGTH;
+
+ curlinestate=nextlinestate;
+ curline++;
+ nextlinestate=render(curlinestate,!dorender,0);
+ hist[curhist].curoff=curlinestate.renderoff;
+ }
+}
+
+static void reset_history(void)
+{
+ curhist=0;
+ histdepth=1;
+}
+
+static void advance_history(int d)
+{
+ if (d==1)
+ {
+ curhist=(curhist+1)%HISTORY_LENGTH;
+ histdepth++;
+ if (histdepth>HISTORY_LENGTH) histdepth=HISTORY_LENGTH;
+ }
+ else if (d==-1)
+ if (histdepth>1)
+ {
+ curhist--;
+ if (curhist<0) curhist=HISTORY_LENGTH-1;
+
+ histdepth--;
+ }
+}
+
+static int iswhitespace(uint16_t c)
+{
+ return (c==' ' || c=='\n'); /* what about tab character? */
+}
+
+static int islinebreak(uint16_t c)
+{
+ return (c=='\n');
+}
+
+/* return next place to scroll to */
+static struct linestate render(struct linestate cur, int norender,
+ int linktonote)
+{
+ int i; /* offset in buf */
+ int x,y; /* current render location */
+ const char * nextchar; /* character after the current UTF-8 character
+ */
+ int charsize; /* size of the current UTF-8 character */
+ uint16_t ucs; /* current UTF-8 character */
+ char buf[LINEBUFLEN+1]; /* the current line of text to be rendered */
+ uint8_t underline[LCD_WIDTH]; /* should there be an underline at this pixel?
+ */
+
+ int fontheight;
+ int first = 0; /*record only first line through*/
+ char fnbuf1[MAX_PATH+1]; /*for recording line of text*/
+ int fd1; /*for recording line of text*/
+
+ end_of_file=0;
+
+#ifdef COLORLINKS
+ uint8_t em[LCD_WIDTH]; /* type of emphasis for this pixel */
+ unsigned lastcol=0;
+ rb->memset(em,0,sizeof(em));
+#endif
+
+ struct linestate nextline; /* state to return */
+
+ /* state to back up to when doing line break */
+ int lastspace_i=0;
+ int lastspace_linkcount=0;
+ struct linestate lastspace;
+
+ int linkcount;
+ int linedone=0;
+ /* if the last character was whitespace we'll ignore subsequent whitespace
+ */
+ int lastwaswhitespace;
+
+ fontheight = rb->font_get(FONT_UI)->height;
+ y=1;
+
+ linkcount=1;
+ lastlinkcount=0;
+
+ nextline.renderoff=-1;
+ /* it's not going to be any less the end... */
+ nextline.eof=cur.eof;
+
+ if (!norender) rb->memset(underline,0,sizeof(underline));
+
+ while (((y+fontheight) < LCD_HEIGHT) && cur.renderlen>0)
+ {
+ lastspace.renderoff=-1;
+ ucs=0;
+
+ /* prevent whitespace from appearing at the beginning of a line */
+ lastwaswhitespace=1;
+
+ /* scroll halfway down the screen */
+ /* if (y==(LCD_HEIGHT/fontheight/2)*fontheight+1) halfway=article; */
+ /* see how much we can fit on a line */
+
+ for (x=1,i=0,linedone=0; !linedone && i<LINEBUFLEN && cur.renderlen>0; )
+ {
+ /*handle markup*/
+ switch (articlebuf[cur.renderoff])
+ {
+ case MARKUP_STARTULINE:
+ case MARKUP_STARTBOLD:
+ case MARKUP_STARTITALIC:
+
+#ifdef COLORLINKS
+ switch (articlebuf[cur.renderoff])
+ {
+ case MARKUP_STARTULINE:
+ cur.inem=1;
+ break;
+ case MARKUP_STARTBOLD:
+ cur.inem=2;
+ break;
+ case MARKUP_STARTITALIC:
+ cur.inem=3;
+ break;
+ }
+#endif
+ cur.renderlen--;
+ cur.renderoff++;
+ break;
+
+ case MARKUP_ENDULINE:
+ case MARKUP_ENDBOLD:
+ case MARKUP_ENDITALIC:
+
+#ifdef COLORLINKS
+ if (cur.inem)
+ cur.inem=0;
+
+#endif
+
+ cur.renderlen--;
+ cur.renderoff++;
+ break;
+
+ case MARKUP_STARTLINK:
+ cur.inlink=1;
+ cur.linknameoff=cur.renderoff;
+ cur.renderlen--;
+ cur.renderoff++;
+ break;
+
+ case MARKUP_ENDLINK:
+ if (cur.inlinkname)
+ {
+ cur.inlinkname=0;
+ linkcount++;
+ }
+
+ if (cur.inlink)
+ {
+ /* start outputting link text */
+ cur.renderlen+=cur.renderoff-cur.linknameoff;
+ cur.renderoff-=cur.renderoff-cur.linknameoff;
+ cur.linknameoff=0;
+ cur.inlink=0;
+ cur.inlinkname=1;
+ }
+
+ cur.renderlen--;
+ cur.renderoff++;
+ break;
+
+ case MARKUP_MODE:
+ if (cur.inlink) cur.linknameoff=cur.renderoff;
+
+ cur.renderlen--;
+ cur.renderoff++;
+ break;
+
+ default:
+ nextchar = rb->utf8decode(articlebuf+cur.renderoff,&ucs);
+ charsize = nextchar-cur.renderoff-articlebuf;
+ /* multiple newlines work */
+ if (islinebreak(ucs)) linedone=1; /* break;*/ /* but multiple
+ other whitespace will be
+ ignored */
+
+ if (!linedone && !cur.inlink && !(iswhitespace(ucs) &&
+ lastwaswhitespace))
+ {
+ /* display */
+ int charwidth=rb->font_get_width(rb->font_get(FONT_UI),ucs);
+
+ if (iswhitespace(ucs))
+ {
+ lastspace=cur;
+ lastspace_i=i;
+ lastspace_linkcount=linkcount;
+ lastwaswhitespace=1;
+ }
+ else lastwaswhitespace=0;
+
+ if ((x+=charwidth) > LCD_WIDTH)
+ {
+ linedone=1;
+ break;
+ }
+
+ if (!norender)
+ {
+ rb->memcpy(buf+i,articlebuf+cur.renderoff,charsize);
+ if (cur.inlinkname)
+ rb->memset(underline+(x-charwidth),linkcount,
+ charwidth);
+
+#ifdef COLORLINKS
+ if (cur.inem)
+ rb->memset(em+(x-charwidth),cur.inem,charwidth);
+
+#endif
+ }
+
+ i+=charsize;
+ }
+ else
+ {
+ /* hidden */
+ }
+
+ cur.renderlen-=charsize;
+ cur.renderoff+=charsize;
+ } /* end markup switch */
+ } /* end for characters in a line */
+
+ if (x>=LCD_WIDTH)
+ {
+ /* the next character would be offscreen, terminate at previous
+ space */
+ if (lastspace.renderoff==-1)
+ {
+ /* if we have a long word, break it here */
+ }
+ else
+ {
+ cur=lastspace;
+ linkcount=lastspace_linkcount;
+ i=lastspace_i;
+ }
+ }
+
+ if (!norender)
+ {
+ int uc,ucm,t;
+
+ if (first<1)
+ {
+ if (record_flag==1)
+ {
+ record_flag=0;
+
+ rb->snprintf(fnbuf1,MAX_PATH,"/wiki/%s.txt",hist[curhist].name);
+ fd1 = rb->open(fnbuf1, O_WRONLY|O_APPEND|O_CREAT);
+
+ if (fd1<0)
+ rb->splash(HZ*2, "Recording NOT done");
+ else
+ {
+ if (record_stop_flag)
+ {
+ rb->write(fd1,"\n\r",2);
+ record_stop_flag=false;
+ }
+
+ rb->write(fd1,(unsigned char *)last_line,last_line_i);
+ if (record_linebreak>0)
+ rb->write(fd1,"\n",2);
+ else
+ rb->write(fd1," ",1);
+
+ rb->close(fd1);
+ }
+ }
+
+ last_line_i=i;
+ rb->memcpy(last_line,buf,(last_line_i));
+ if (islinebreak(ucs))
+ record_linebreak=1;
+ else
+ record_linebreak=0;
+ }
+
+ first++;
+
+ buf[(i<=LINEBUFLEN) ? i : LINEBUFLEN]='\0';
+ rb->lcd_putsxy(1,y, (unsigned char *)buf);
+ rb->lcd_getstringsize(buf,&ucm,&t);
+
+ int oldmode=rb->lcd_get_drawmode();
+
+ /* mark links */
+ for (uc=0; uc<LCD_WIDTH; uc++)
+ {
+#ifdef COLORLINKS
+ /* mark emphasis */
+ if (em[uc])
+ {
+ lastcol=rb->lcd_get_foreground();
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+ switch (em[uc])
+ {
+ case 1:
+ rb->lcd_set_foreground(LCD_RGBPACK(255,0,0));
+ break;
+ case 2:
+ rb->lcd_set_foreground(LCD_RGBPACK(0,255,0));
+ break;
+ case 3:
+ rb->lcd_set_foreground(LCD_RGBPACK(0,0,255));
+ break;
+ }
+ rb->lcd_drawpixel(uc,y+fontheight-1);
+ rb->lcd_set_foreground(lastcol);
+ }
+
+ em[uc]=0;
+#endif
+
+ if (underline[uc] && uc > 1 && uc < (ucm+1))
+ {
+ if (underline[uc]==linktonote)
+ {
+ rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
+ rb->lcd_vline(uc,y,y+fontheight-1);
+ }
+ else
+ {
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+ rb->lcd_drawpixel(uc,y+fontheight-1);
+ }
+
+ lastlinkcount=underline[uc];
+ }
+
+ underline[uc]=0;
+ /* clear rest of line */
+ if (uc >= ucm+1)
+ {
+ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ rb->lcd_vline(uc,y,y+fontheight-1);
+ }
+ }
+
+ rb->lcd_set_drawmode(oldmode);
+ }
+
+ y+=fontheight;
+ /* scroll one line */
+ if (nextline.renderoff==-1)
+ {
+ nextline=cur;
+ if (norender) return nextline;
+ }
+ }
+ if (cur.renderlen<=0)
+ {
+ if (first<2)
+ end_of_file=1;
+
+ cur.eof=1;
+ nextline.eof=1;
+ while ((y+fontheight) < LCD_HEIGHT)
+ {
+ rb->lcd_putsxy(1,y," ");
+ y+=fontheight;
+ }
+ }
+
+ if (!norender)
+ rb->lcd_update();
+
+ return ((nextline.renderoff==-1) ? cur : nextline);
+}
+
+static void readlink(struct linestate cur, int link, char * namebuf, int buflen)
+{
+ int linkcount=0;
+
+ rb->memset(namebuf,0,buflen);
+
+ if (!link)
+ return;
+
+ /*
+ screen starts partway through a link
+ */
+ if (cur.inlink || cur.inlinkname)
+ /* seek back to the start of the link */
+ while (articlebuf[cur.renderoff] != MARKUP_STARTLINK)
+ {
+ cur.renderoff--;
+ cur.renderlen++;
+ }
+
+ while (cur.renderlen)
+ {
+ if (articlebuf[cur.renderoff] == MARKUP_STARTLINK)
+ {
+ linkcount++;
+ if (linkcount==link)
+ {
+ cur.renderoff++;
+ cur.renderlen--;
+ break;
+ }
+ }
+
+ cur.renderoff++;
+ cur.renderlen--;
+ }
+ if (linkcount==link)
+ {
+ int i;
+ for (i=0; i<buflen-1 && cur.renderlen>0; )
+ {
+ int charsize;
+ if (articlebuf[cur.renderoff]==MARKUP_ENDLINK || articlebuf[cur.renderoff]==MARKUP_MODE) break;
+
+ charsize = rb->utf8seek(articlebuf+cur.renderoff,1);
+ rb->memcpy(namebuf+i,articlebuf+cur.renderoff,charsize);
+ cur.renderlen-=charsize;
+ i+=charsize;
+ cur.renderoff+=charsize;
+ }
+ }
+}
+
+/* locate headings, indicated by a line beginning with = */
+/* this is unfortunately quite stupid about markup in headings */
+
+static int render_outline(int inoff, int norender)
+{
+ int renderoff;
+ int renderlen;
+ int nextlineoff;
+ int linecount;
+ int intdepth=0;
+ int extdepth=0;
+ int linestart;
+ int charsize;
+ int fontheight;
+ int y;
+
+ char preserv;
+ enum {
+ STATE_START, /* start of line */
+ STATE_DD, /* determining depth */
+ STATE_DD2, /* check closing =s */
+ STATE_HTEXT, /* heading text */
+ STATE_OTHERTEXT /* other text */
+ } state;
+
+ if (inoff<10) inoff=10;
+
+ renderoff=inoff;
+ renderlen=articlelen-renderoff;
+ linecount=0;
+
+ y=1;
+ fontheight=rb->font_get(FONT_UI)->height;
+
+ if (!norender)
+ rb->lcd_clear_display();
+
+ if (renderoff==10) /* we will include the article title */
+ {
+ if (!norender)
+ {
+ preserv=articlebuf[articlebuf[8]+10];
+ articlebuf[articlebuf[8]+10]='\0';
+ rb->lcd_putsxy(1,y,articlebuf+10);
+
+ articlebuf[articlebuf[8]+10]=preserv;
+ }
+
+ y+=fontheight;
+ linecount++;
+
+ renderlen-=articlebuf[8];
+ renderoff+=articlebuf[8];
+ }
+
+ nextlineoff=renderoff;
+
+ linestart=renderoff;
+ state=STATE_START;
+
+ while (renderlen>0 && y+fontheight < LCD_HEIGHT)
+ {
+ switch (state)
+ {
+ case STATE_START:
+ linestart=renderoff;
+ switch (articlebuf[renderoff])
+ {
+ case '=':
+ if (linecount==1)
+ {
+ nextlineoff=renderoff; /* beginning of second line
+ */
+ if (norender) return nextlineoff;
+ }
+
+ intdepth=1;
+ state=STATE_DD;
+ break;
+ case MARKUP_LINEFEED:
+ break;
+ default:
+ state=STATE_OTHERTEXT;
+ break;
+ }
+ break;
+
+ case STATE_DD:
+ switch (articlebuf[renderoff])
+ {
+ case '=':
+ intdepth++;
+ break;
+ case MARKUP_LINEFEED:
+ state=STATE_START; /* give up */
+ break;
+ default:
+ state=STATE_HTEXT;
+ break;
+ }
+ break;
+
+ case STATE_DD2:
+ switch (articlebuf[renderoff])
+ {
+ case '=':
+ extdepth++;
+ break;
+ case MARKUP_LINEFEED:
+ /* final acceptance here */
+ if (!norender)
+ {
+ if (linecount==0)
+ rb->lcd_clear_display();
+
+ int depth;
+ if(extdepth<intdepth)
+ depth=extdepth;
+ else
+ depth=intdepth;
+
+ int whitey=0;
+ preserv=articlebuf[renderoff-depth];
+ articlebuf[renderoff-depth]='\0';
+
+ /* trim leading whitespace */
+
+ while (iswhitespace(articlebuf[linestart+depth+
+ whitey]))
+ {
+ charsize = rb->utf8seek(articlebuf+linestart+
+ depth+whitey,1);
+
+ whitey+=charsize;
+ }
+
+ rb->lcd_putsxy(1+depth*rb->font_get_width(
+ rb->font_get(FONT_UI),'_'),y,
+ articlebuf+linestart+depth+whitey);
+
+ articlebuf[renderoff-depth]=preserv;
+ }
+
+ linecount++;
+ y+=fontheight;
+ state=STATE_START;
+ break;
+
+ default:
+ /* return to HTEXT, might be an = within a heading */
+ state=STATE_HTEXT;
+ break;
+ }
+ break;
+
+ case STATE_HTEXT:
+ switch (articlebuf[renderoff])
+ {
+ case '=':
+ extdepth=1;
+ state=STATE_DD2;
+ break;
+
+ case MARKUP_LINEFEED:
+ state=STATE_START; /* give up */
+ break;
+
+ default:
+ /* continue to accept text within the heading */
+ break;
+ }
+ break;
+
+ case STATE_OTHERTEXT:
+ switch (articlebuf[renderoff])
+ {
+ case MARKUP_LINEFEED:
+ state=STATE_START;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ } /* end switch (state) */
+
+ charsize = rb->utf8seek(articlebuf+renderoff,1);
+ renderlen-=charsize;
+ renderoff+=charsize;
+ }
+
+ if (!norender)
+ rb->lcd_update();
+
+ return nextlineoff;
+}
+
+static void render_history(int off)
+{
+ int i=curhist,j,d;
+ int x,y,fontheight,t;
+ char buf[10];
+
+ rb->lcd_clear_display();
+
+ y=1;
+ fontheight = rb->font_get(FONT_UI)->height;
+
+ for (j=1,d=histdepth; d>0 && y+fontheight<LCD_HEIGHT; d--,j++)
+ {
+ if (j>=(off+1))
+ {
+ rb->snprintf(buf,10,"%2d. ",j);
+ rb->lcd_putsxy(1,y,buf);
+ rb->lcd_getstringsize(buf,&x,&t);
+ rb->lcd_putsxy(1+x,y,hist[i].name);
+ y+=fontheight;
+ }
+
+ i--;
+ if (i<0)
+ i=HISTORY_LENGTH-1;
+ }
+
+ rb->lcd_update();
+}
+
+/* read 2 character ascii hex */
+static int readhex(unsigned char * buf)
+{
+ int t=0;
+ if (buf[0]>='0'&&buf[0]<='9')
+ t=(buf[0]-'0')*16;
+ else if (buf[0]>='A'&&buf[0]<='F')
+ t=(buf[0]-'A'+10)*16;
+ else return -1;
+
+ if (buf[1]>='0'&&buf[1]<='9')
+ t+=(buf[1]-'0');
+ else if (buf[1]>='A'&&buf[1]<='F')
+ t+=(buf[1]-'A'+10);
+ else return -1;
+
+ return t;
+}
+
+static bool viewer_init(void)
+{
+ enum {
+ MODE_LINK,
+ MODE_NAVVIEW,
+ MODE_HISTVIEW,
+ MODE_NORMAL,
+ MODE_BOOKVIEW
+ } mode;
+
+ int linkno; /* currently selected link */
+ int navcur; /* current offset for navigation */
+ int navnext; /* next offset for navigation mode */
+ int navline; /* current line for navigation (important for scrolling up)
+ */
+ int histviewoff; /* how far back */
+ int bookviewoff; /* how far back */
+ int fontheight;
+ fontheight = rb->font_get(FONT_UI)->height;
+
+ char * target; /* an anchor to seek out */
+
+ bool prompt=1; /* prompt for article name? */
+ bool dofind=1; /* run mwdb_findarticle? */
+ curline=0; /* current line */
+
+ reset_history();
+ hist[curhist].curoff=0; /* first article starts at beginning */
+
+ /* allocate memory */
+ wpw_init_mempool(MAINMEMBUF);
+ inflatebuf=wpw_malloc(MAINMEMBUF,0x13500);
+ articlebuflen=wpw_available(MAINMEMBUF)>0x32000 ? 0x32000 : wpw_available(MAINMEMBUF);
+ articlebuf=wpw_malloc(MAINMEMBUF,articlebuflen);
+
+ /* initially no name in prompt, subsequently default to previous name */
+ rb->memset(hist[curhist].name,0,ARTICLENAMEBUF_LENGTH);
+
+#ifdef HAVE_TOUCHSCREEN
+ rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
+#endif
+
+ if (load_status(filename))
+ {
+ prompt=0;
+ dofind=0;
+ }
+
+ if (load_bookmarks(filename)) /*UNSURE*/
+ {
+ }
+
+ target=0;
+
+loadnewarticle:
+ if (prompt || dofind)
+ if (!mwdb_findarticle(filename,hist[curhist].name,ARTICLENAMEBUF_LENGTH,
+ &hist[curhist].res_lo,&hist[curhist].res_hi,true,
+ prompt,1))
+ {
+ /* try case-insensitive match if case-sensitive match fails */
+ if (!rb->strlen(hist[curhist].name))
+ {
+ /* bailed out */
+
+ /* automatically go back */
+ if (histdepth>1)
+ {
+ prompt=0;
+ dofind=0;
+ advance_history(-1);
+
+ goto loadnewarticle; /* few lines up */
+ }
+
+ return true;
+ }
+
+ if (!mwdb_findarticle(filename,hist[curhist].name,
+ ARTICLENAMEBUF_LENGTH,&hist[curhist].res_lo,
+ &hist[curhist].res_hi,true,0,0))
+ {
+ rb->splashf(HZ*2, "didn't find \"%s\"",hist[curhist].name);
+
+ /* automatically go back */
+ if (histdepth>1)
+ {
+ if (prompt==0)
+ {
+ prompt=0;
+ dofind=0;
+ advance_history(-1);
+ }
+
+ goto loadnewarticle; /* few lines up */
+ }
+
+ return true; /* No reason to make the plugin fail */
+ }
+ }
+
+
+ prompt=0;
+
+ if (!mwdb_loadarticle(filename,inflatebuf,articlebuf,articlebuflen,
+ &articlelen,hist[curhist].res_lo,hist[curhist].res_hi))
+ return false; /* load error is a problem */
+
+ rb->lcd_clear_display();
+
+ /* if a target has been specified */
+ if (target)
+ {
+ int i,j;
+
+ /* find headings */
+ navcur=-1;
+ navnext=0;
+ while (navcur!=navnext)
+ {
+ navcur=navnext;
+ navnext=render_outline(navcur,1);
+ /* navnext will be pointing at the start of the line with the target
+ in it
+
+ * Attempt match, this is a bit tricky (and it is probably equally
+ *tricky to get an anchor pointing to another document in wikicode
+ *in the first place).
+ */
+ for (i=0,j=navnext; target[i] && articlebuf[j]; )
+ {
+ if (target[i]==articlebuf[j])
+ {
+ i++; j++;
+ }
+ else
+ {
+ if (i==0 && (articlebuf[j]=='=' || articlebuf[j]==' '))
+ j++; /* ignore starting =s, spaces */
+ else if (target[i]=='_' && articlebuf[j]==' ')
+ {
+ i++;
+ j++; /* spaces converted to underscores */
+ }
+ else if (target[i]=='.') /* check for escape sequences */
+ {
+ if (articlebuf[j]==readhex(target+i+1))
+ {
+ i+=3;
+ j++;
+ }
+ else break;
+ }
+ else break;
+ }
+ }
+ if (target[i])
+ continue;
+
+ while (articlebuf[j]==' ')
+ j++; /* sometimes we see spaces before the closing = */
+
+ if (articlebuf[j]!='=')
+ continue; /* we want the exact heading */
+
+ /* we have a winner! */
+ hist[curhist].curoff=navnext;
+ }
+ target=0;
+ }
+
+ set_article_offset(hist[curhist].curoff);
+ nextlinestate=render(curlinestate,0,0);
+
+ /* get authoritative title name */
+ rb->memcpy(hist[curhist].name,articlebuf+10,articlebuf[8]);
+ hist[curhist].name[(int)(articlebuf[8])]='\0';
+
+ mode=MODE_NORMAL;
+ linkno=0;
+ navcur=0;
+ navnext=0;
+ navline=0;
+ histviewoff=0;
+ bookviewoff=0;
+
+
+ int button;
+ while (!wrquit)
+ {
+ button = rb->button_get(true);
+
+ if (rb->default_event_handler_ex(button, viewer_exit, NULL)==
+ SYS_USB_CONNECTED)
+ return PLUGIN_USB_CONNECTED;
+
+#ifdef HAVE_TOUCHSCREEN
+ if (button & BUTTON_TOUCHSCREEN)
+ {
+ static int prevY = 0;
+ short x, y;
+ int fontheight = rb->font_get(FONT_UI)->height;
+ x = rb->button_get_data() >> 16;
+ y = rb->button_get_data() & 0xFFFF;
+
+ if(button & BUTTON_REL)
+ prevY = 0;
+ else
+ {
+ if (prevY != 0)
+ {
+ if (mode == MODE_NORMAL)
+ {
+ int pageDelta = (y - prevY) / fontheight;
+ advance_scrollback(-pageDelta, 1);
+ }
+ }
+ prevY = y;
+ }
+ }
+#endif
+
+ switch(button)
+ {
+ case WIKIVIEWER_MENU:
+ switch (mode)
+ {
+ case MODE_BOOKVIEW:
+ save_bookmark(filename);
+ case MODE_HISTVIEW:
+ case MODE_LINK:
+ case MODE_NAVVIEW:
+ rb->lcd_clear_display();
+ mode=MODE_NORMAL;
+ nextlinestate=render(curlinestate,0,0);
+ break;
+ case MODE_NORMAL:
+ {
+ /* wrquit=true; */
+ MENUITEM_STRINGLIST(main_menu, "Wikiviewer Menu", NULL,
+ "Save History",
+ "Navigate",
+ "Find Similar Article",
+ "Find Article",
+ "Clear History",
+ "View History",
+ "Show Playback Menu",
+ "Bookmarks",
+ "Add Bookmark",
+ "Exit");
+
+ int selection = 0;
+ int lasthist;
+ switch (rb->do_menu(&main_menu, &selection, NULL, false))
+ {
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+
+ case 0: /* save status */
+ save_status(filename);
+ break;
+
+ case 1: /* navigate */
+ mode=MODE_NAVVIEW;
+ navcur=0;
+ navnext=render_outline(navcur,0);
+ navline=0;
+ break;
+
+ case 2: /* find similar article */
+ lasthist=curhist;
+ prompt=1;
+ dofind=1;
+
+ advance_history(1);
+ rb->strcpy(hist[curhist].name,hist[lasthist].name);
+ hist[curhist].curoff=0;
+
+ goto loadnewarticle;
+ break;
+
+ case 3: /* find article (blank name) */
+ lasthist=curhist;
+ prompt=1;
+ dofind=1;
+
+ advance_history(1);
+ rb->memset(hist[curhist].name,0,ARTICLENAMEBUF_LENGTH);
+ hist[curhist].curoff=0;
+
+ goto loadnewarticle;
+ break;
+
+ case 4: /* clear hist */
+ reset_history();
+ rb->memset(hist[curhist].name,0,ARTICLENAMEBUF_LENGTH);
+ hist[curhist].curoff=0;
+ prompt=1;
+ dofind=1;
+ goto loadnewarticle;
+ break;
+
+ case 5: /* view hist */
+ mode=MODE_HISTVIEW;
+ histviewoff=0;
+ render_history(histviewoff);
+ break;
+
+ case 6: /* playback control */
+ playback_control(NULL);
+ break;
+
+ case 7: /* Bookmarks */
+ mode=MODE_BOOKVIEW;
+ render_bookmarks(0);
+ break;
+
+ case 8: /* Add Bookmark */
+ add_bookmark();
+ save_bookmark(filename);
+ break;
+
+ case 9: /* exit */
+ wrquit=true;
+ break;
+ } /* end menu switch */
+
+ if ((mode==MODE_NORMAL) && !wrquit)
+ {
+ rb->lcd_clear_display();
+ nextlinestate=render(curlinestate,0,0);
+ }
+ } /* end menu block */
+ } /* end mode switch */
+ break;
+
+ case WIKIVIEWER_SELECT:
+ switch (mode)
+ {
+ case MODE_NAVVIEW:
+ set_article_offset(navcur);
+ nextlinestate=render(curlinestate,0,0);
+ mode=MODE_NORMAL;
+ break;
+
+ case MODE_NORMAL:
+ /* enter link select mode */
+ mode=MODE_LINK;
+ linkno=1;
+ nextlinestate=render(curlinestate,0,linkno);
+ if (lastlinkcount==0) mode=MODE_NORMAL;
+
+ break;
+
+ case MODE_LINK:
+ /* load a new article */
+ advance_history(1);
+ readlink(curlinestate,linkno,hist[curhist].name,ARTICLENAMEBUF_LENGTH);
+ if ((target=rb->strrchr(hist[curhist].name,'#')))
+ {
+ /* cut the target name off the end of the string */
+ target[0]='\0';
+ target++; /* to point at the name of the target */
+ }
+
+ hist[curhist].curoff=0;
+ dofind=1;
+ goto loadnewarticle; /* ~line 1165 */
+
+ case MODE_HISTVIEW:
+ /* jump back */
+ if (histviewoff>0)
+ {
+ int i;
+ for (i=0; i<histviewoff; i++)
+ advance_history(-1);
+ dofind=0;
+ mode=MODE_NORMAL;
+ goto loadnewarticle; /* ~line 1165 */
+ }
+
+ nextlinestate=render(curlinestate,0,0);
+ mode=MODE_NORMAL;
+ break;
+
+ case MODE_BOOKVIEW:
+ if ((bookdepth-bookviewoff)<0)
+ rb->splash(HZ*2, "error");
+ else
+ {
+ save_bookmark(filename);
+ int booki;
+ booki=bookdepth-bookviewoff-1;
+ curhist++;
+ histdepth++;
+ rb->strcpy(hist[curhist].name,book[booki].name);
+ hist[curhist].curoff=book[booki].curoff;
+ hist[curhist].res_lo=book[booki].res_lo;
+ hist[curhist].res_hi=book[booki].res_hi;
+ dofind=0;
+ mode=MODE_NORMAL;
+ goto loadnewarticle; /* ~line 1165 */
+ }
+
+ nextlinestate=render(curlinestate,0,0);
+ mode=MODE_NORMAL;
+ break;
+
+ default:
+ break;
+ } /* end mode switch */
+ break;
+
+#ifdef WIKIVIEWER_STOP_RECORD
+ case WIKIVIEWER_STOP_RECORD:
+ switch (mode)
+ {
+ case MODE_NORMAL: /*reset recording flag*/
+ record_stop_flag=true;
+ break;
+ default:
+ break;
+ } /* end mode switch */
+ break;
+#endif
+
+ case WIKIVIEWER_FWD:
+ case WIKIVIEWER_FWD_REPEAT:
+#ifdef WIKIVIEWER_FWD2
+ case WIKIVIEWER_FWD2:
+ case WIKIVIEWER_FWD2_REPEAT:
+#endif
+ switch (mode)
+ {
+ case MODE_NAVVIEW:
+ if (navnext!=navcur)
+ navline++;
+
+ navcur=navnext;
+ navnext=render_outline(navcur,0);
+ break;
+
+ case MODE_LINK:
+ /* select next link */
+ if (linkno<lastlinkcount)
+ {
+ linkno++;
+ nextlinestate=render(curlinestate,0,linkno);
+ }
+
+ break;
+
+ case MODE_NORMAL:
+#ifdef WIKIVIEWER_FWD2
+ if (button==WIKIVIEWER_FWD2)
+ {
+ /* enter link select mode */
+ mode=MODE_LINK;
+ linkno=1;
+ nextlinestate=render(curlinestate,0,linkno);
+ if (lastlinkcount==0)
+ mode=MODE_NORMAL;
+
+ break;
+ }
+ else
+#endif
+ { /* scroll down */
+ advance_scrollback(1,1);
+ }
+
+ break;
+
+ case MODE_HISTVIEW:
+ if (histviewoff<histdepth-1)
+ {
+ histviewoff++;
+ render_history(histviewoff);
+ }
+
+ break;
+
+ case MODE_BOOKVIEW:
+ if (bookviewoff<bookdepth-1)
+ {
+ bookviewoff++;
+ render_bookmarks(bookviewoff);
+ }
+
+ break;
+ } /* end mode switch */
+ break;
+
+ case WIKIVIEWER_BACK:
+ case WIKIVIEWER_BACK_REPEAT:
+#ifdef WIKIVIEWER_BACK2
+ case WIKIVIEWER_BACK2:
+ case WIKIVIEWER_BACK2_REPEAT:
+#endif
+ switch (mode)
+ {
+ case MODE_NAVVIEW:
+ {
+ int i;
+ if (navline>0)
+ {
+ navline--;
+ navnext=0;
+ for (i=0; i<navline; i++)
+ {
+ navcur=navnext;
+ navnext=render_outline(navcur,1);
+ }
+ navcur=navnext;
+ navnext=render_outline(navcur,0);
+ }
+ }
+ break;
+
+ case MODE_LINK:
+ /* select previous link */
+ if (linkno>1)
+ {
+ linkno--;
+ nextlinestate=render(curlinestate,0,linkno);
+ }
+
+ break;
+
+ case MODE_NORMAL:
+#ifdef WIKIVIEWER_BACK2
+ if (button==WIKIVIEWER_BACK2) /*scroll up page*/
+ advance_scrollback((1-(LCD_HEIGHT/fontheight)),1);
+ else
+#endif
+ { /* scroll up */
+ advance_scrollback(-1,1);
+ }
+
+ break;
+
+ case MODE_HISTVIEW:
+ if (histviewoff>0)
+ {
+ histviewoff--;
+ render_history(histviewoff);
+ }
+
+ break;
+
+ case MODE_BOOKVIEW:
+ if (bookviewoff>0)
+ {
+ bookviewoff--;
+ render_bookmarks(bookviewoff);
+ }
+
+ break;
+ } /* end mode switch */
+ break;
+
+ case WIKIVIEWER_PREV: /* go back */
+ switch (mode)
+ {
+ case MODE_NORMAL:
+ if (histdepth>1)
+ {
+ advance_history(-1);
+ dofind=0;
+ goto loadnewarticle; /* ~line 1165 */
+ }
+
+ break;
+
+ case MODE_BOOKVIEW:
+ remove_bookmark(bookviewoff);
+ bookviewoff=0;
+ render_bookmarks(bookviewoff);
+ break;
+
+ default:
+ break;
+ } /* end mode switch */
+ break;
+
+ case WIKIVIEWER_NEXT: /* go forward */
+ switch (mode)
+ {
+ case MODE_NORMAL:
+ if (hist[(curhist+1)].curoff>0)
+ {
+ advance_history(1);
+ dofind=0;
+ bookviewoff=0;
+ goto loadnewarticle; /* ~line 1165 */
+ }
+
+ break;
+
+ case MODE_BOOKVIEW:
+ if (load_bookmarks(filename))
+ render_bookmarks(bookviewoff);
+
+ break;
+
+ default:
+ break;
+ } /* end mode switch */
+ break;
+
+#ifdef WIKIVIEWER_RECORD
+ case WIKIVIEWER_RECORD: /*record line*/
+ case WIKIVIEWER_RECORD_REPEAT:
+ switch (mode)
+ {
+ case MODE_NORMAL:
+ record_flag=1;
+ advance_scrollback(1,1);
+ break;
+ case MODE_BOOKVIEW:
+ save_bookmark(filename);
+ break;
+ default:
+ break;
+ } /* end mode switch */
+ break;
+#endif
+
+ default:
+ break;
+ } /* end button switch */
+ } /* end main loop */
+ return true;
+}
+
+static bool check_dir(char *folder)
+{
+ DIR *dir = rb->opendir(folder);
+ if (!dir && rb->strcmp(folder, "/"))
+ {
+ int rc = rb->mkdir(folder);
+ if(rc < 0)
+ return false;
+
+ return true;
+ }
+
+ rb->closedir(dir);
+ return true;
+}
+
+enum plugin_status plugin_start(const void* file)
+{
+ rb->backlight_set_timeout(0); /*Turn off backlight timeout*/
+ check_dir("/wiki");
+ if (!file)
+ return PLUGIN_ERROR;
+
+ rb->strlcpy(filename,file,sizeof(filename));
+ filename[rb->strlen(filename)-4]='\0';
+
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+
+ if (!viewer_init())
+ return PLUGIN_ERROR;
+
+ viewer_exit(NULL);
+ rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
+ /*Turn on backlight timeout*/
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/wikiviewer/wikiviewer.make b/apps/plugins/wikiviewer/wikiviewer.make
new file mode 100644
index 0000000..4953212
--- /dev/null
+++ b/apps/plugins/wikiviewer/wikiviewer.make
@@ -0,0 +1,21 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id: $
+#
+
+WIKIVIEWER_SRCDIR := $(APPSDIR)/plugins/wikiviewer
+WIKIVIEWER_BUILDDIR := $(BUILDDIR)/apps/plugins/wikiviewer
+
+ROCKS += $(WIKIVIEWER_BUILDDIR)/wikiviewer.rock
+
+WIKIVIEWER_SRC := $(call preprocess, $(WIKIVIEWER_SRCDIR)/SOURCES)
+WIKIVIEWER_OBJ := $(call c2obj, $(WIKIVIEWER_SRC))
+
+# add source files to OTHER_SRC to get automatic dependencies
+OTHER_SRC += $(WIKIVIEWER_SRC)
+
+$(WIKIVIEWER_BUILDDIR)/wikiviewer.rock: $(WIKIVIEWER_OBJ)