From d4b95f3ed4b8a0f47dab35a2560d76bb66117277 Mon Sep 17 00:00:00 2001
From: Ruben <ruben@rubenkerkhof.com>
Date: Sun, 17 Oct 2010 17:50:39 +0200
Subject: [PATCH 12/14] 30_utf8-support.diff from Debian
---
README.txt | 240 ++++++++++++++++++++++++++--------------------------
ajaxterm.1 | 2 +
ajaxterm.html | 1 +
ajaxterm.js | 13 +++-
ajaxterm.py | 81 ++++++++----------
configure.makefile | 2 +-
utf8-escape.js | 80 +++++++++++++++++
7 files changed, 251 insertions(+), 168 deletions(-)
create mode 100644 utf8-escape.js
diff --git a/README.txt b/README.txt
index 4b0ae99..05ae97f 100644
--- a/README.txt
+++ b/README.txt
@@ -1,120 +1,120 @@
-= [http://antony.lesuisse.org/qweb/trac/wiki/AjaxTerm Ajaxterm] =
-
-Ajaxterm is a web based terminal. It was totally inspired and works almost
-exactly like http://anyterm.org/ except it's much easier to install (see
-comparaison with anyterm below).
-
-Ajaxterm written in python (and some AJAX javascript for client side) and depends only on python2.3 or better.[[BR]]
-Ajaxterm is '''very simple to install''' on Linux, MacOS X, FreeBSD, Solaris, cygwin and any Unix that runs python2.3.[[BR]]
-Ajaxterm was written by Antony Lesuisse (email: al AT udev.org), License Public Domain.
-
-Use the [/qweb/forum/viewforum.php?id=2 Forum], if you have any question or remark.
-
-== News ==
-
- * 2006-10-29: v0.10 allow space in login, cgi launch fix, redhat init
- * 2006-07-12: v0.9 change uid, daemon fix (Daniel Fischer)
- * 2006-07-04: v0.8 add login support to ssh (Sven Geggus), change max width to 256
- * 2006-05-31: v0.7 minor fixes, daemon option
- * 2006-05-23: v0.6 Applied debian and gentoo patches, renamed to Ajaxterm, default port 8022
-
-== Download and Install ==
-
- * Release: [/qweb/files/Ajaxterm-0.10.tar.gz Ajaxterm-0.10.tar.gz]
- * Browse src: [/qweb/trac/browser/trunk/ajaxterm/ ajaxterm/]
-
-To install Ajaxterm issue the following commands:
-{{{
-wget http://antony.lesuisse.org/qweb/files/Ajaxterm-0.10.tar.gz
-tar zxvf Ajaxterm-0.10.tar.gz
-cd Ajaxterm-0.10
-./ajaxterm.py
-}}}
-Then point your browser to this URL : http://localhost:8022/
-
-== Screenshot ==
-
-{{{
-#!html
-<center><img src="/qweb/trac/attachment/wiki/AjaxTerm/scr.png?format=raw" alt="ajaxterm screenshot" style=""/></center>
-}}}
-
-== Documentation and Caveats ==
-
- * Ajaxterm only support latin1, if you use Ubuntu or any LANG==en_US.UTF-8 distribution don't forget to "unset LANG".
-
- * If run as root ajaxterm will run /bin/login, otherwise it will run ssh
- localhost. To use an other command use the -c option.
-
- * By default Ajaxterm only listen at 127.0.0.1:8022. For remote access, it is
- strongly recommended to use '''https SSL/TLS''', and that is simple to
- configure if you use the apache web server using mod_proxy.[[BR]][[BR]]
- Using ssl will also speed up ajaxterm (probably because of keepalive).[[BR]][[BR]]
- Here is an configuration example:
-
-{{{
- Listen 443
- NameVirtualHost *:443
-
- <VirtualHost *:443>
- ServerName localhost
- SSLEngine On
- SSLCertificateKeyFile ssl/apache.pem
- SSLCertificateFile ssl/apache.pem
-
- ProxyRequests Off
- <Proxy *>
- Order deny,allow
- Allow from all
- </Proxy>
- ProxyPass /ajaxterm/ http://localhost:8022/
- ProxyPassReverse /ajaxterm/ http://localhost:8022/
- </VirtualHost>
-}}}
-
- * Using GET HTTP request seems to speed up ajaxterm, just click on GET in the
- interface, but be warned that your keystrokes might be loggued (by apache or
- any proxy). I usually enable it after the login.
-
- * Ajaxterm commandline usage:
-
-{{{
-usage: ajaxterm.py [options]
-
-options:
- -h, --help show this help message and exit
- -pPORT, --port=PORT Set the TCP port (default: 8022)
- -cCMD, --command=CMD set the command (default: /bin/login or ssh localhost)
- -l, --log log requests to stderr (default: quiet mode)
- -d, --daemon run as daemon in the background
- -PPIDFILE, --pidfile=PIDFILE
- set the pidfile (default: /var/run/ajaxterm.pid)
- -iINDEX_FILE, --index=INDEX_FILE
- default index file (default: ajaxterm.html)
- -uUID, --uid=UID Set the daemon's user id
-}}}
-
- * Ajaxterm was first written as a demo for qweb (my web framework), but
- actually doesn't use many features of qweb.
-
- * Compared to anyterm:
- * There are no partial updates, ajaxterm updates either all the screen or
- nothing. That make the code simpler and I also think it's faster. HTTP
- replies are always gzencoded. When used in 80x25 mode, almost all of
- them are below the 1500 bytes (size of an ethernet frame) and we just
- replace the screen with the reply (no javascript string handling).
- * Ajaxterm polls the server for updates with an exponentially growing
- timeout when the screen hasn't changed. The timeout is also resetted as
- soon as a key is pressed. Anyterm blocks on a pending request and use a
- parallel connection for keypresses. The anyterm approch is better
- when there aren't any keypress.
-
- * Ajaxterm files are released in the Public Domain, (except [http://sarissa.sourceforge.net/doc/ sarissa*] which are LGPL).
-
-== TODO ==
-
- * insert mode ESC [ 4 h
- * change size x,y from gui (sending signal)
- * vt102 graphic codepage
- * use innerHTML or prototype instead of sarissa
-
+= [http://antony.lesuisse.org/qweb/trac/wiki/AjaxTerm Ajaxterm] =
+
+Ajaxterm is a web based terminal. It was totally inspired and works almost
+exactly like http://anyterm.org/ except it's much easier to install (see
+comparaison with anyterm below).
+
+Ajaxterm written in python (and some AJAX javascript for client side) and depends only on python2.3 or better.[[BR]]
+Ajaxterm is '''very simple to install''' on Linux, MacOS X, FreeBSD, Solaris, cygwin and any Unix that runs python2.3.[[BR]]
+Ajaxterm was written by Antony Lesuisse (email: al AT udev.org), License Public Domain.
+
+Use the [/qweb/forum/viewforum.php?id=2 Forum], if you have any question or remark.
+
+== News ==
+
+ * 2006-10-29: v0.10 allow space in login, cgi launch fix, redhat init
+ * 2006-07-12: v0.9 change uid, daemon fix (Daniel Fischer)
+ * 2006-07-04: v0.8 add login support to ssh (Sven Geggus), change max width to 256
+ * 2006-05-31: v0.7 minor fixes, daemon option
+ * 2006-05-23: v0.6 Applied debian and gentoo patches, renamed to Ajaxterm, default port 8022
+
+== Download and Install ==
+
+ * Release: [/qweb/files/Ajaxterm-0.10.tar.gz Ajaxterm-0.10.tar.gz]
+ * Browse src: [/qweb/trac/browser/trunk/ajaxterm/ ajaxterm/]
+
+To install Ajaxterm issue the following commands:
+{{{
+wget http://antony.lesuisse.org/qweb/files/Ajaxterm-0.10.tar.gz
+tar zxvf Ajaxterm-0.10.tar.gz
+cd Ajaxterm-0.10
+./ajaxterm.py
+}}}
+Then point your browser to this URL : http://localhost:8022/
+
+== Screenshot ==
+
+{{{
+#!html
+<center><img src="/qweb/trac/attachment/wiki/AjaxTerm/scr.png?format=raw" alt="ajaxterm screenshot" style=""/></center>
+}}}
+
+== Documentation and Caveats ==
+
+ * Ajaxterm only support utf8.
+
+ * If run as root ajaxterm will run /bin/login, otherwise it will run ssh
+ localhost. To use an other command use the -c option.
+
+ * By default Ajaxterm only listen at 127.0.0.1:8022. For remote access, it is
+ strongly recommended to use '''https SSL/TLS''', and that is simple to
+ configure if you use the apache web server using mod_proxy.[[BR]][[BR]]
+ Using ssl will also speed up ajaxterm (probably because of keepalive).[[BR]][[BR]]
+ Here is an configuration example:
+
+{{{
+ Listen 443
+ NameVirtualHost *:443
+
+ <VirtualHost *:443>
+ ServerName localhost
+ SSLEngine On
+ SSLCertificateKeyFile ssl/apache.pem
+ SSLCertificateFile ssl/apache.pem
+
+ ProxyRequests Off
+ <Proxy *>
+ Order deny,allow
+ Allow from all
+ </Proxy>
+ ProxyPass /ajaxterm/ http://localhost:8022/
+ ProxyPassReverse /ajaxterm/ http://localhost:8022/
+ </VirtualHost>
+}}}
+
+ * Using GET HTTP request seems to speed up ajaxterm, just click on GET in the
+ interface, but be warned that your keystrokes might be loggued (by apache or
+ any proxy). I usually enable it after the login.
+
+ * Ajaxterm commandline usage:
+
+{{{
+usage: ajaxterm.py [options]
+
+options:
+ -h, --help show this help message and exit
+ -pPORT, --port=PORT Set the TCP port (default: 8022)
+ -cCMD, --command=CMD set the command (default: /bin/login or ssh localhost)
+ -l, --log log requests to stderr (default: quiet mode)
+ -d, --daemon run as daemon in the background
+ -PPIDFILE, --pidfile=PIDFILE
+ set the pidfile (default: /var/run/ajaxterm.pid)
+ -iINDEX_FILE, --index=INDEX_FILE
+ default index file (default: ajaxterm.html)
+ -uUID, --uid=UID Set the daemon's user id
+}}}
+
+ * Ajaxterm was first written as a demo for qweb (my web framework), but
+ actually doesn't use many features of qweb.
+
+ * Compared to anyterm:
+ * There are no partial updates, ajaxterm updates either all the screen or
+ nothing. That make the code simpler and I also think it's faster. HTTP
+ replies are always gzencoded. When used in 80x25 mode, almost all of
+ them are below the 1500 bytes (size of an ethernet frame) and we just
+ replace the screen with the reply (no javascript string handling).
+ * Ajaxterm polls the server for updates with an exponentially growing
+ timeout when the screen hasn't changed. The timeout is also resetted as
+ soon as a key is pressed. Anyterm blocks on a pending request and use a
+ parallel connection for keypresses. The anyterm approch is better
+ when there aren't any keypress.
+
+ * Ajaxterm files are released in the Public Domain, (except [http://sarissa.sourceforge.net/doc/ sarissa*] which are LGPL).
+
+== TODO ==
+
+ * insert mode ESC [ 4 h
+ * change size x,y from gui (sending signal)
+ * vt102 graphic codepage
+ * use innerHTML or prototype instead of sarissa
+
diff --git a/ajaxterm.1 b/ajaxterm.1
index 8d0a942..29d8250 100644
--- a/ajaxterm.1
+++ b/ajaxterm.1
@@ -20,6 +20,8 @@ A summary of the options supported by \fBajaxterm\fR is included below.
.SH AUTHOR
Antony Lesuisse <al@udev.org>
+Adopted to UTF-8 by Sergej Pupykin <ps@lx-ltd.ru>
+
This manual page was written for the Debian system by
Julien Valroff <julien@kirya.net> (but may be used by others).
diff --git a/ajaxterm.html b/ajaxterm.html
index 095e7d6..2cc48ef 100644
--- a/ajaxterm.html
+++ b/ajaxterm.html
@@ -8,6 +8,7 @@
<script type="text/javascript" src="sarissa_dhtml.js"></script>
<script type="text/javascript" src="ajaxterm.js"></script>
<script type="text/javascript" src="ajaxterm_config.js"></script>
+ <script type="text/javascript" src="utf8-escape.js"></script>
<script type="text/javascript">
window.onload=function() {
t=ajaxterm.Terminal("term",width,height);
diff --git a/ajaxterm.js b/ajaxterm.js
index 2579a8f..b976a96 100644
--- a/ajaxterm.js
+++ b/ajaxterm.js
@@ -142,7 +142,16 @@ ajaxterm.Terminal_ctor=function(id,width,height) {
if (r.readyState==4) {
if(r.status==200) {
window.clearTimeout(error_timeout);
- de=r.responseXML.documentElement;
+ if(ie)
+ {
+ var responseXMLdoc = new ActiveXObject("Microsoft.XMLDOM");
+ responseXMLdoc.loadXML(r.responseText);
+ de = responseXMLdoc.documentElement;
+ }
+ else
+ {
+ de=r.responseXML.documentElement;
+ }
if(de.tagName=="pre") {
if(ie) {
Sarissa.updateContentFromNode(de, dterm);
@@ -250,7 +259,7 @@ ajaxterm.Terminal_ctor=function(id,width,height) {
if(k=="+") {
queue("%2B");
} else {
- queue(escape(k));
+ queue(utf8Escape(k));
}
}
ev.cancelBubble=true;
diff --git a/ajaxterm.py b/ajaxterm.py
index 8695590..92bf67e 100755
--- a/ajaxterm.py
+++ b/ajaxterm.py
@@ -21,7 +21,9 @@ os.chdir(os.path.normpath(os.path.dirname(__file__)))
# Optional: Add QWeb in sys path
sys.path[0:0]=glob.glob('../../python')
-import qweb
+import qweb, codecs
+utf8decoder = codecs.getincrementaldecoder('utf8')()
+
from socket import gethostname
@@ -92,31 +94,14 @@ class Terminal:
for i in [i[4] for i in dir(self) if i.startswith('csi_') and len(i)==5]:
if not self.csi_seq.has_key(i):
self.csi_seq[i]=(getattr(self,'csi_'+i),[1])
- # Init 0-256 to latin1 and html translation table
- self.trl1=""
- for i in range(256):
- if i<32:
- self.trl1+=" "
- elif i<127 or i>160:
- self.trl1+=chr(i)
- else:
- self.trl1+="?"
- self.trhtml=""
- for i in range(256):
- if i==0x0a or (i>32 and i<127) or i>160:
- self.trhtml+=chr(i)
- elif i<=32:
- self.trhtml+="\xa0"
- else:
- self.trhtml+="?"
- def reset(self,s=""):
- self.scr=array.array('i',[0x000700]*(self.width*self.height))
+ def reset(self,s=u""):
+ self.scr=array.array('l',[0x07000000]*(self.width*self.height))
self.st=0
self.sb=self.height-1
self.cx_bak=self.cx=0
self.cy_bak=self.cy=0
self.cl=0
- self.sgr=0x000700
+ self.sgr=0x07000000
self.buf=""
self.outbuf=""
self.last_html=""
@@ -127,7 +112,7 @@ class Terminal:
self.scr[pos:pos+len(s)]=s
def zero(self,y1,x1,y2,x2):
w=self.width*(y2-y1)+x2-x1+1
- z=array.array('i',[0x000700]*w)
+ z=array.array('l',[0x07000000]*w)
self.scr[self.width*y1+x1:self.width*y2+x2+1]=z
def scroll_up(self,y1,y2):
self.poke(y1,0,self.peek(y1+1,0,y2,self.width))
@@ -280,17 +265,17 @@ class Terminal:
def csi_m(self,l):
for i in l:
if i==0 or i==39 or i==49 or i==27:
- self.sgr=0x000700
+ self.sgr=0x07000000
elif i==1:
- self.sgr=(self.sgr|0x000800)
+ self.sgr=(self.sgr|0x08000000)
elif i==7:
- self.sgr=0x070000
+ self.sgr=0x70000000
elif i>=30 and i<=37:
c=i-30
- self.sgr=(self.sgr&0xff08ff)|(c<<8)
+ self.sgr=(self.sgr&0xf8ffffff)|(c<<24)
elif i>=40 and i<=47:
c=i-40
- self.sgr=(self.sgr&0x00ffff)|(c<<16)
+ self.sgr=(self.sgr&0x0fffffff)|(c<<28)
# else:
# print "CSI sgr ignore",l,i
# print 'sgr: %r %x'%(l,self.sgr)
@@ -320,12 +305,12 @@ class Terminal:
break
# if self.buf=='': print "ESC %r\n"%e
def write(self,s):
- for i in s:
+ for i in utf8decoder.decode(s):
if len(self.buf) or (i in self.esc_seq):
- self.buf+=i
+ self.buf+=chr(ord(i)&255)
self.escape()
elif i == '\x1b':
- self.buf+=i
+ self.buf+=chr(ord(i)&255)
else:
self.echo(i)
def read(self):
@@ -333,35 +318,41 @@ class Terminal:
self.outbuf=""
return b
def dump(self):
- r=''
+ r=u''
for i in self.scr:
- r+=chr(i&255)
+ r+=unichr(i&0xFFFFFF)
return r
- def dumplatin1(self):
- return self.dump().translate(self.trl1)
+ def dumputf8(self):
+ return self.dump().encode('utf8')
def dumphtml(self,color=1):
h=self.height
w=self.width
r=""
- span=""
+ span=u""
span_bg,span_fg=-1,-1
for i in range(h*w):
- q,c=divmod(self.scr[i],256)
+ q,c=divmod(self.scr[i],256*256*256)
if color:
- bg,fg=divmod(q,256)
+ bg,fg=divmod(q,16)
+ bg &= 0x7
else:
bg,fg=0,7
if i==self.cy*w+self.cx:
bg,fg=1,7
if (bg!=span_bg or fg!=span_fg or i==h*w-1):
if len(span):
- r+='<span class="f%d b%d">%s</span>'%(span_fg,span_bg,cgi.escape(span.translate(self.trhtml)))
- span=""
+ r+='<span class="f%d b%d">%s</span>'%(span_fg,span_bg,cgi.escape(span.replace(u' ',u'\xa0').encode('utf8')))
+ span=u""
span_bg,span_fg=bg,fg
- span+=chr(c)
+ if c == 0:
+ span+=u' '
+ elif c > 0x10000:
+ span+=u'?'
+ else:
+ span+=unichr(c&0xFFFF)
if i%w==w-1:
- span+='\n'
- r='<?xml version="1.0" encoding="ISO-8859-1"?><pre class="term">%s</pre>'%r
+ span+=u'\n'
+ r='<?xml version="1.0" encoding="utf8"?><pre class="term">%s</pre>'%r
if self.last_html==r:
return '<?xml version="1.0"?><idem></idem>'
else:
@@ -369,8 +360,8 @@ class Terminal:
# print self
return r
def __repr__(self):
- d=self.dumplatin1()
- r=""
+ d=self.dumputf8()
+ r=u""
for i in range(self.height):
r+="|%s|\n"%d[self.width*i:self.width*(i+1)]
return r
@@ -432,7 +423,7 @@ class Multiplex:
elif os.getuid()==0:
cmd=['/bin/login']
else:
- sys.stdout.write(gethostname() + " login: ")
+ sys.stdout.write(gethostname() + u" login: ")
login=sys.stdin.readline().strip()
if re.match('^[0-9A-Za-z-_. ]+$',login):
cmd=['ssh']
diff --git a/configure.makefile b/configure.makefile
index a30dc23..c866804 100644
--- a/configure.makefile
+++ b/configure.makefile
@@ -5,7 +5,7 @@ install:
install -d "%(bin)s"
install -d "%(lib)s"
install ajaxterm.bin "%(bin)s/ajaxterm"
- install -m 644 ajaxterm.css ajaxterm.html ajaxterm.js qweb.py sarissa.js sarissa_dhtml.js "%(lib)s"
+ install -m 644 ajaxterm.css ajaxterm.html ajaxterm.js qweb.py sarissa.js sarissa_dhtml.js utf8-escape.js "%(lib)s"
install -m 644 ajaxterm.conf "%(etc)s"
install -m 755 ajaxterm.py "%(lib)s"
ln -s /etc/ajaxterm.conf "%(lib)s"/ajaxterm_config.js
diff --git a/utf8-escape.js b/utf8-escape.js
new file mode 100644
index 0000000..dfb8cb3
--- /dev/null
+++ b/utf8-escape.js
@@ -0,0 +1,80 @@
+var char2hex = new Array(
+ "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
+ "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
+ "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
+ "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
+ "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
+ "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f",
+ "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
+ "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f",
+ "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
+ "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f",
+ "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
+ "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f",
+ "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
+ "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f",
+ "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
+ "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f",
+ "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
+ "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
+ "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
+ "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
+ "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
+ "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
+ "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
+ "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
+ "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+ "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
+ "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
+ "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
+ "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+ "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
+ );
+
+function utf8Escape(s)
+{
+ var sbuf = "";
+ var i;
+
+ var len = s.length;
+ for (i = 0; i < len; i++)
+ {
+ var ch = s.charCodeAt(i);
+ if( (65 <= ch && ch <= 90) ||
+ (97 <= ch && ch <= 122) ||
+ (48 <= ch && ch <= 57) )
+ {
+ sbuf += String.fromCharCode(ch);
+ }
+ else if (ch == 32)
+ {
+ sbuf += '+';
+ }
+ else if (ch == 45 || ch == 95
+ || ch == 46 || ch == 33
+ || ch == 126 || ch == 42
+ || ch == 39 || ch == 40
+ || ch == 41)
+ {
+ sbuf += char2hex[ch];
+ }
+ else if (ch <= 0x007F)
+ {
+ sbuf += char2hex[ch];
+ }
+ else if (ch <= 0x07FF)
+ {
+ sbuf += char2hex[0xc0 | (ch >> 6)];
+ sbuf += char2hex[0x80 | (ch & 0x3F)];
+ }
+ else
+ {
+ sbuf += char2hex[0xe0 | (ch >> 12)];
+ sbuf += char2hex[0x80 | ((ch >> 6) & 0x3F)];
+ sbuf += char2hex[0x80 | (ch & 0x3F)];
+ }
+ }
+ return sbuf;
+}
--
1.7.3.1